]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
280b1aaa75428a8fda2d8aa9d43b5ff11b84eb64
[rails.git] / vendor / assets / iD / iD.js
1 (function(exports) {
2
3   var bootstrap = (typeof exports.bootstrap === "object") ?
4     exports.bootstrap :
5     (exports.bootstrap = {});
6
7   bootstrap.tooltip = function() {
8
9     var tooltip = function(selection) {
10         selection.each(setup);
11       },
12       animation = d3.functor(false),
13       html = d3.functor(false),
14       title = function() {
15         var title = this.getAttribute("data-original-title");
16         if (title) {
17           return title;
18         } else {
19           title = this.getAttribute("title");
20           this.removeAttribute("title");
21           this.setAttribute("data-original-title", title);
22         }
23         return title;
24       },
25       over = "mouseenter.tooltip",
26       out = "mouseleave.tooltip",
27       placements = "top left bottom right".split(" "),
28       placement = d3.functor("top");
29
30     tooltip.title = function(_) {
31       if (arguments.length) {
32         title = d3.functor(_);
33         return tooltip;
34       } else {
35         return title;
36       }
37     };
38
39     tooltip.html = function(_) {
40       if (arguments.length) {
41         html = d3.functor(_);
42         return tooltip;
43       } else {
44         return html;
45       }
46     };
47
48     tooltip.placement = function(_) {
49       if (arguments.length) {
50         placement = d3.functor(_);
51         return tooltip;
52       } else {
53         return placement;
54       }
55     };
56
57     tooltip.show = function(selection) {
58       selection.each(show);
59     };
60
61     tooltip.hide = function(selection) {
62       selection.each(hide);
63     };
64
65     tooltip.toggle = function(selection) {
66       selection.each(toggle);
67     };
68
69     tooltip.destroy = function(selection) {
70       selection
71         .on(over, null)
72         .on(out, null)
73         .attr("title", function() {
74           return this.getAttribute("data-original-title") || this.getAttribute("title");
75         })
76         .attr("data-original-title", null)
77         .select(".tooltip")
78         .remove();
79     };
80
81     function setup() {
82       var root = d3.select(this),
83           animate = animation.apply(this, arguments),
84           tip = root.append("div")
85             .attr("class", "tooltip");
86
87       if (animate) {
88         tip.classed("fade", true);
89       }
90
91       // TODO "inside" checks?
92
93       tip.append("div")
94         .attr("class", "tooltip-arrow");
95       tip.append("div")
96         .attr("class", "tooltip-inner");
97
98       var place = placement.apply(this, arguments);
99       tip.classed(place, true);
100
101       root.on(over, show);
102       root.on(out, hide);
103     }
104
105     function show() {
106       var root = d3.select(this),
107           content = title.apply(this, arguments),
108           tip = root.select(".tooltip")
109             .classed("in", true),
110           markup = html.apply(this, arguments),
111           innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content),
112           place = placement.apply(this, arguments),
113           outer = getPosition(root.node()),
114           inner = getPosition(tip.node()),
115           pos;
116
117       switch (place) {
118         case "top":
119           pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
120           break;
121         case "right":
122           pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
123           break;
124         case "left":
125           pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
126           break;
127         case "bottom":
128           pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h};
129           break;
130       }
131
132       tip.style(pos ?
133         {left: ~~pos.x + "px", top: ~~pos.y + "px"} :
134         {left: null, top: null});
135
136       this.tooltipVisible = true;
137     }
138
139     function hide() {
140       d3.select(this).select(".tooltip")
141         .classed("in", false);
142
143       this.tooltipVisible = false;
144     }
145
146     function toggle() {
147       if (this.tooltipVisible) {
148         hide.apply(this, arguments);
149       } else {
150         show.apply(this, arguments);
151       }
152     }
153
154     return tooltip;
155   };
156
157   function getPosition(node) {
158     var mode = d3.select(node).style('position');
159     if (mode === 'absolute' || mode === 'static') {
160       return {
161         x: node.offsetLeft,
162         y: node.offsetTop,
163         w: node.offsetWidth,
164         h: node.offsetHeight
165       };
166     } else {
167       return {
168         x: 0,
169         y: 0,
170         w: node.offsetWidth,
171         h: node.offsetHeight
172       };
173     }
174   }
175
176 })(this);
177 !function(){
178   var d3 = {version: "3.5.5"}; // semver
179 d3.ascending = d3_ascending;
180
181 function d3_ascending(a, b) {
182   return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
183 }
184 d3.descending = function(a, b) {
185   return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
186 };
187 d3.min = function(array, f) {
188   var i = -1,
189       n = array.length,
190       a,
191       b;
192   if (arguments.length === 1) {
193     while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
194     while (++i < n) if ((b = array[i]) != null && a > b) a = b;
195   } else {
196     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
197     while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
198   }
199   return a;
200 };
201 d3.max = function(array, f) {
202   var i = -1,
203       n = array.length,
204       a,
205       b;
206   if (arguments.length === 1) {
207     while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
208     while (++i < n) if ((b = array[i]) != null && b > a) a = b;
209   } else {
210     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
211     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
212   }
213   return a;
214 };
215 d3.extent = function(array, f) {
216   var i = -1,
217       n = array.length,
218       a,
219       b,
220       c;
221   if (arguments.length === 1) {
222     while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; }
223     while (++i < n) if ((b = array[i]) != null) {
224       if (a > b) a = b;
225       if (c < b) c = b;
226     }
227   } else {
228     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = c = b; break; }
229     while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
230       if (a > b) a = b;
231       if (c < b) c = b;
232     }
233   }
234   return [a, c];
235 };
236 function d3_number(x) {
237   return x === null ? NaN : +x;
238 }
239
240 function d3_numeric(x) {
241   return !isNaN(x);
242 }
243
244 d3.sum = function(array, f) {
245   var s = 0,
246       n = array.length,
247       a,
248       i = -1;
249   if (arguments.length === 1) {
250     while (++i < n) if (d3_numeric(a = +array[i])) s += a; // zero and null are equivalent
251   } else {
252     while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
253   }
254   return s;
255 };
256
257 d3.mean = function(array, f) {
258   var s = 0,
259       n = array.length,
260       a,
261       i = -1,
262       j = n;
263   if (arguments.length === 1) {
264     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
265   } else {
266     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
267   }
268   if (j) return s / j;
269 };
270 // R-7 per <http://en.wikipedia.org/wiki/Quantile>
271 d3.quantile = function(values, p) {
272   var H = (values.length - 1) * p + 1,
273       h = Math.floor(H),
274       v = +values[h - 1],
275       e = H - h;
276   return e ? v + e * (values[h] - v) : v;
277 };
278
279 d3.median = function(array, f) {
280   var numbers = [],
281       n = array.length,
282       a,
283       i = -1;
284   if (arguments.length === 1) {
285     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
286   } else {
287     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
288   }
289   if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
290 };
291
292 d3.variance = function(array, f) {
293   var n = array.length,
294       m = 0,
295       a,
296       d,
297       s = 0,
298       i = -1,
299       j = 0;
300   if (arguments.length === 1) {
301     while (++i < n) {
302       if (d3_numeric(a = d3_number(array[i]))) {
303         d = a - m;
304         m += d / ++j;
305         s += d * (a - m);
306       }
307     }
308   } else {
309     while (++i < n) {
310       if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
311         d = a - m;
312         m += d / ++j;
313         s += d * (a - m);
314       }
315     }
316   }
317   if (j > 1) return s / (j - 1);
318 };
319
320 d3.deviation = function() {
321   var v = d3.variance.apply(this, arguments);
322   return v ? Math.sqrt(v) : v;
323 };
324
325 function d3_bisector(compare) {
326   return {
327     left: function(a, x, lo, hi) {
328       if (arguments.length < 3) lo = 0;
329       if (arguments.length < 4) hi = a.length;
330       while (lo < hi) {
331         var mid = lo + hi >>> 1;
332         if (compare(a[mid], x) < 0) lo = mid + 1;
333         else hi = mid;
334       }
335       return lo;
336     },
337     right: function(a, x, lo, hi) {
338       if (arguments.length < 3) lo = 0;
339       if (arguments.length < 4) hi = a.length;
340       while (lo < hi) {
341         var mid = lo + hi >>> 1;
342         if (compare(a[mid], x) > 0) hi = mid;
343         else lo = mid + 1;
344       }
345       return lo;
346     }
347   };
348 }
349
350 var d3_bisect = d3_bisector(d3_ascending);
351 d3.bisectLeft = d3_bisect.left;
352 d3.bisect = d3.bisectRight = d3_bisect.right;
353
354 d3.bisector = function(f) {
355   return d3_bisector(f.length === 1
356       ? function(d, x) { return d3_ascending(f(d), x); }
357       : f);
358 };
359 d3.shuffle = function(array, i0, i1) {
360   if ((m = arguments.length) < 3) { i1 = array.length; if (m < 2) i0 = 0; }
361   var m = i1 - i0, t, i;
362   while (m) {
363     i = Math.random() * m-- | 0;
364     t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
365   }
366   return array;
367 };
368 d3.permute = function(array, indexes) {
369   var i = indexes.length, permutes = new Array(i);
370   while (i--) permutes[i] = array[indexes[i]];
371   return permutes;
372 };
373 d3.pairs = function(array) {
374   var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
375   while (i < n) pairs[i] = [p0 = p1, p1 = array[++i]];
376   return pairs;
377 };
378
379 d3.zip = function() {
380   if (!(n = arguments.length)) return [];
381   for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m;) {
382     for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n;) {
383       zip[j] = arguments[j][i];
384     }
385   }
386   return zips;
387 };
388
389 function d3_zipLength(d) {
390   return d.length;
391 }
392
393 d3.transpose = function(matrix) {
394   return d3.zip.apply(d3, matrix);
395 };
396 d3.keys = function(map) {
397   var keys = [];
398   for (var key in map) keys.push(key);
399   return keys;
400 };
401 d3.values = function(map) {
402   var values = [];
403   for (var key in map) values.push(map[key]);
404   return values;
405 };
406 d3.entries = function(map) {
407   var entries = [];
408   for (var key in map) entries.push({key: key, value: map[key]});
409   return entries;
410 };
411 d3.merge = function(arrays) {
412   var n = arrays.length,
413       m,
414       i = -1,
415       j = 0,
416       merged,
417       array;
418
419   while (++i < n) j += arrays[i].length;
420   merged = new Array(j);
421
422   while (--n >= 0) {
423     array = arrays[n];
424     m = array.length;
425     while (--m >= 0) {
426       merged[--j] = array[m];
427     }
428   }
429
430   return merged;
431 };
432 var abs = Math.abs;
433
434 d3.range = function(start, stop, step) {
435   if (arguments.length < 3) {
436     step = 1;
437     if (arguments.length < 2) {
438       stop = start;
439       start = 0;
440     }
441   }
442   if ((stop - start) / step === Infinity) throw new Error("infinite range");
443   var range = [],
444        k = d3_range_integerScale(abs(step)),
445        i = -1,
446        j;
447   start *= k, stop *= k, step *= k;
448   if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k);
449   else while ((j = start + step * ++i) < stop) range.push(j / k);
450   return range;
451 };
452
453 function d3_range_integerScale(x) {
454   var k = 1;
455   while (x * k % 1) k *= 10;
456   return k;
457 }
458 function d3_class(ctor, properties) {
459   for (var key in properties) {
460     Object.defineProperty(ctor.prototype, key, {
461       value: properties[key],
462       enumerable: false
463     });
464   }
465 }
466
467 d3.map = function(object, f) {
468   var map = new d3_Map;
469   if (object instanceof d3_Map) {
470     object.forEach(function(key, value) { map.set(key, value); });
471   } else if (Array.isArray(object)) {
472     var i = -1,
473         n = object.length,
474         o;
475     if (arguments.length === 1) while (++i < n) map.set(i, object[i]);
476     else while (++i < n) map.set(f.call(object, o = object[i], i), o);
477   } else {
478     for (var key in object) map.set(key, object[key]);
479   }
480   return map;
481 };
482
483 function d3_Map() {
484   this._ = Object.create(null);
485 }
486
487 var d3_map_proto = "__proto__",
488     d3_map_zero = "\0";
489
490 d3_class(d3_Map, {
491   has: d3_map_has,
492   get: function(key) {
493     return this._[d3_map_escape(key)];
494   },
495   set: function(key, value) {
496     return this._[d3_map_escape(key)] = value;
497   },
498   remove: d3_map_remove,
499   keys: d3_map_keys,
500   values: function() {
501     var values = [];
502     for (var key in this._) values.push(this._[key]);
503     return values;
504   },
505   entries: function() {
506     var entries = [];
507     for (var key in this._) entries.push({key: d3_map_unescape(key), value: this._[key]});
508     return entries;
509   },
510   size: d3_map_size,
511   empty: d3_map_empty,
512   forEach: function(f) {
513     for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
514   }
515 });
516
517 function d3_map_escape(key) {
518   return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
519 }
520
521 function d3_map_unescape(key) {
522   return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
523 }
524
525 function d3_map_has(key) {
526   return d3_map_escape(key) in this._;
527 }
528
529 function d3_map_remove(key) {
530   return (key = d3_map_escape(key)) in this._ && delete this._[key];
531 }
532
533 function d3_map_keys() {
534   var keys = [];
535   for (var key in this._) keys.push(d3_map_unescape(key));
536   return keys;
537 }
538
539 function d3_map_size() {
540   var size = 0;
541   for (var key in this._) ++size;
542   return size;
543 }
544
545 function d3_map_empty() {
546   for (var key in this._) return false;
547   return true;
548 }
549
550 d3.nest = function() {
551   var nest = {},
552       keys = [],
553       sortKeys = [],
554       sortValues,
555       rollup;
556
557   function map(mapType, array, depth) {
558     if (depth >= keys.length) return rollup
559         ? rollup.call(nest, array) : (sortValues
560         ? array.sort(sortValues)
561         : array);
562
563     var i = -1,
564         n = array.length,
565         key = keys[depth++],
566         keyValue,
567         object,
568         setter,
569         valuesByKey = new d3_Map,
570         values;
571
572     while (++i < n) {
573       if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
574         values.push(object);
575       } else {
576         valuesByKey.set(keyValue, [object]);
577       }
578     }
579
580     if (mapType) {
581       object = mapType();
582       setter = function(keyValue, values) {
583         object.set(keyValue, map(mapType, values, depth));
584       };
585     } else {
586       object = {};
587       setter = function(keyValue, values) {
588         object[keyValue] = map(mapType, values, depth);
589       };
590     }
591
592     valuesByKey.forEach(setter);
593     return object;
594   }
595
596   function entries(map, depth) {
597     if (depth >= keys.length) return map;
598
599     var array = [],
600         sortKey = sortKeys[depth++];
601
602     map.forEach(function(key, keyMap) {
603       array.push({key: key, values: entries(keyMap, depth)});
604     });
605
606     return sortKey
607         ? array.sort(function(a, b) { return sortKey(a.key, b.key); })
608         : array;
609   }
610
611   nest.map = function(array, mapType) {
612     return map(mapType, array, 0);
613   };
614
615   nest.entries = function(array) {
616     return entries(map(d3.map, array, 0), 0);
617   };
618
619   nest.key = function(d) {
620     keys.push(d);
621     return nest;
622   };
623
624   // Specifies the order for the most-recently specified key.
625   // Note: only applies to entries. Map keys are unordered!
626   nest.sortKeys = function(order) {
627     sortKeys[keys.length - 1] = order;
628     return nest;
629   };
630
631   // Specifies the order for leaf values.
632   // Applies to both maps and entries array.
633   nest.sortValues = function(order) {
634     sortValues = order;
635     return nest;
636   };
637
638   nest.rollup = function(f) {
639     rollup = f;
640     return nest;
641   };
642
643   return nest;
644 };
645
646 d3.set = function(array) {
647   var set = new d3_Set;
648   if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
649   return set;
650 };
651
652 function d3_Set() {
653   this._ = Object.create(null);
654 }
655
656 d3_class(d3_Set, {
657   has: d3_map_has,
658   add: function(key) {
659     this._[d3_map_escape(key += "")] = true;
660     return key;
661   },
662   remove: d3_map_remove,
663   values: d3_map_keys,
664   size: d3_map_size,
665   empty: d3_map_empty,
666   forEach: function(f) {
667     for (var key in this._) f.call(this, d3_map_unescape(key));
668   }
669 });
670 d3.behavior = {};
671 var d3_document = this.document;
672
673 function d3_documentElement(node) {
674   return node
675       && (node.ownerDocument // node is a Node
676       || node.document // node is a Window
677       || node).documentElement; // node is a Document
678 }
679
680 function d3_window(node) {
681   return node
682       && ((node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
683         || (node.document && node) // node is a Window
684         || node.defaultView); // node is a Document
685 }
686 // Copies a variable number of methods from source to target.
687 d3.rebind = function(target, source) {
688   var i = 1, n = arguments.length, method;
689   while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
690   return target;
691 };
692
693 // Method is assumed to be a standard D3 getter-setter:
694 // If passed with no arguments, gets the value.
695 // If passed with arguments, sets the value and returns the target.
696 function d3_rebind(target, source, method) {
697   return function() {
698     var value = method.apply(source, arguments);
699     return value === source ? target : value;
700   };
701 }
702 function d3_vendorSymbol(object, name) {
703   if (name in object) return name;
704   name = name.charAt(0).toUpperCase() + name.slice(1);
705   for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
706     var prefixName = d3_vendorPrefixes[i] + name;
707     if (prefixName in object) return prefixName;
708   }
709 }
710
711 var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"];
712 var d3_arraySlice = [].slice,
713     d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists
714 function d3_noop() {}
715
716 d3.dispatch = function() {
717   var dispatch = new d3_dispatch,
718       i = -1,
719       n = arguments.length;
720   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
721   return dispatch;
722 };
723
724 function d3_dispatch() {}
725
726 d3_dispatch.prototype.on = function(type, listener) {
727   var i = type.indexOf("."),
728       name = "";
729
730   // Extract optional namespace, e.g., "click.foo"
731   if (i >= 0) {
732     name = type.slice(i + 1);
733     type = type.slice(0, i);
734   }
735
736   if (type) return arguments.length < 2
737       ? this[type].on(name)
738       : this[type].on(name, listener);
739
740   if (arguments.length === 2) {
741     if (listener == null) for (type in this) {
742       if (this.hasOwnProperty(type)) this[type].on(name, null);
743     }
744     return this;
745   }
746 };
747
748 function d3_dispatch_event(dispatch) {
749   var listeners = [],
750       listenerByName = new d3_Map;
751
752   function event() {
753     var z = listeners, // defensive reference
754         i = -1,
755         n = z.length,
756         l;
757     while (++i < n) if (l = z[i].on) l.apply(this, arguments);
758     return dispatch;
759   }
760
761   event.on = function(name, listener) {
762     var l = listenerByName.get(name),
763         i;
764
765     // return the current listener, if any
766     if (arguments.length < 2) return l && l.on;
767
768     // remove the old listener, if any (with copy-on-write)
769     if (l) {
770       l.on = null;
771       listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
772       listenerByName.remove(name);
773     }
774
775     // add the new listener, if any
776     if (listener) listeners.push(listenerByName.set(name, {on: listener}));
777
778     return dispatch;
779   };
780
781   return event;
782 }
783
784 d3.event = null;
785
786 function d3_eventPreventDefault() {
787   d3.event.preventDefault();
788 }
789
790 function d3_eventCancel() {
791   d3.event.preventDefault();
792   d3.event.stopPropagation();
793 }
794
795 function d3_eventSource() {
796   var e = d3.event, s;
797   while (s = e.sourceEvent) e = s;
798   return e;
799 }
800
801 // Like d3.dispatch, but for custom events abstracting native UI events. These
802 // events have a target component (such as a brush), a target element (such as
803 // the svg:g element containing the brush) and the standard arguments `d` (the
804 // target element's data) and `i` (the selection index of the target element).
805 function d3_eventDispatch(target) {
806   var dispatch = new d3_dispatch,
807       i = 0,
808       n = arguments.length;
809
810   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
811
812   // Creates a dispatch context for the specified `thiz` (typically, the target
813   // DOM element that received the source event) and `argumentz` (typically, the
814   // data `d` and index `i` of the target element). The returned function can be
815   // used to dispatch an event to any registered listeners; the function takes a
816   // single argument as input, being the event to dispatch. The event must have
817   // a "type" attribute which corresponds to a type registered in the
818   // constructor. This context will automatically populate the "sourceEvent" and
819   // "target" attributes of the event, as well as setting the `d3.event` global
820   // for the duration of the notification.
821   dispatch.of = function(thiz, argumentz) {
822     return function(e1) {
823       try {
824         var e0 =
825         e1.sourceEvent = d3.event;
826         e1.target = target;
827         d3.event = e1;
828         dispatch[e1.type].apply(thiz, argumentz);
829       } finally {
830         d3.event = e0;
831       }
832     };
833   };
834
835   return dispatch;
836 }
837 d3.requote = function(s) {
838   return s.replace(d3_requote_re, "\\$&");
839 };
840
841 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
842 var d3_subclass = {}.__proto__?
843
844 // Until ECMAScript supports array subclassing, prototype injection works well.
845 function(object, prototype) {
846   object.__proto__ = prototype;
847 }:
848
849 // And if your browser doesn't support __proto__, we'll use direct extension.
850 function(object, prototype) {
851   for (var property in prototype) object[property] = prototype[property];
852 };
853
854 function d3_selection(groups) {
855   d3_subclass(groups, d3_selectionPrototype);
856   return groups;
857 }
858
859 var d3_select = function(s, n) { return n.querySelector(s); },
860     d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
861     d3_selectMatches = function(n, s) {
862       var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
863       d3_selectMatches = function(n, s) {
864         return d3_selectMatcher.call(n, s);
865       };
866       return d3_selectMatches(n, s);
867     };
868
869 // Prefer Sizzle, if available.
870 if (typeof Sizzle === "function") {
871   d3_select = function(s, n) { return Sizzle(s, n)[0] || null; };
872   d3_selectAll = Sizzle;
873   d3_selectMatches = Sizzle.matchesSelector;
874 }
875
876 d3.selection = function() {
877   return d3.select(d3_document.documentElement);
878 };
879
880 var d3_selectionPrototype = d3.selection.prototype = [];
881
882
883 d3_selectionPrototype.select = function(selector) {
884   var subgroups = [],
885       subgroup,
886       subnode,
887       group,
888       node;
889
890   selector = d3_selection_selector(selector);
891
892   for (var j = -1, m = this.length; ++j < m;) {
893     subgroups.push(subgroup = []);
894     subgroup.parentNode = (group = this[j]).parentNode;
895     for (var i = -1, n = group.length; ++i < n;) {
896       if (node = group[i]) {
897         subgroup.push(subnode = selector.call(node, node.__data__, i, j));
898         if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
899       } else {
900         subgroup.push(null);
901       }
902     }
903   }
904
905   return d3_selection(subgroups);
906 };
907
908 function d3_selection_selector(selector) {
909   return typeof selector === "function" ? selector : function() {
910     return d3_select(selector, this);
911   };
912 }
913
914 d3_selectionPrototype.selectAll = function(selector) {
915   var subgroups = [],
916       subgroup,
917       node;
918
919   selector = d3_selection_selectorAll(selector);
920
921   for (var j = -1, m = this.length; ++j < m;) {
922     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
923       if (node = group[i]) {
924         subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
925         subgroup.parentNode = node;
926       }
927     }
928   }
929
930   return d3_selection(subgroups);
931 };
932
933 function d3_selection_selectorAll(selector) {
934   return typeof selector === "function" ? selector : function() {
935     return d3_selectAll(selector, this);
936   };
937 }
938 var d3_nsPrefix = {
939   svg: "http://www.w3.org/2000/svg",
940   xhtml: "http://www.w3.org/1999/xhtml",
941   xlink: "http://www.w3.org/1999/xlink",
942   xml: "http://www.w3.org/XML/1998/namespace",
943   xmlns: "http://www.w3.org/2000/xmlns/"
944 };
945
946 d3.ns = {
947   prefix: d3_nsPrefix,
948   qualify: function(name) {
949     var i = name.indexOf(":"),
950         prefix = name;
951     if (i >= 0) {
952       prefix = name.slice(0, i);
953       name = name.slice(i + 1);
954     }
955     return d3_nsPrefix.hasOwnProperty(prefix)
956         ? {space: d3_nsPrefix[prefix], local: name}
957         : name;
958   }
959 };
960
961 d3_selectionPrototype.attr = function(name, value) {
962   if (arguments.length < 2) {
963
964     // For attr(string), return the attribute value for the first node.
965     if (typeof name === "string") {
966       var node = this.node();
967       name = d3.ns.qualify(name);
968       return name.local
969           ? node.getAttributeNS(name.space, name.local)
970           : node.getAttribute(name);
971     }
972
973     // For attr(object), the object specifies the names and values of the
974     // attributes to set or remove. The values may be functions that are
975     // evaluated for each element.
976     for (value in name) this.each(d3_selection_attr(value, name[value]));
977     return this;
978   }
979
980   return this.each(d3_selection_attr(name, value));
981 };
982
983 function d3_selection_attr(name, value) {
984   name = d3.ns.qualify(name);
985
986   // For attr(string, null), remove the attribute with the specified name.
987   function attrNull() {
988     this.removeAttribute(name);
989   }
990   function attrNullNS() {
991     this.removeAttributeNS(name.space, name.local);
992   }
993
994   // For attr(string, string), set the attribute with the specified name.
995   function attrConstant() {
996     this.setAttribute(name, value);
997   }
998   function attrConstantNS() {
999     this.setAttributeNS(name.space, name.local, value);
1000   }
1001
1002   // For attr(string, function), evaluate the function for each element, and set
1003   // or remove the attribute as appropriate.
1004   function attrFunction() {
1005     var x = value.apply(this, arguments);
1006     if (x == null) this.removeAttribute(name);
1007     else this.setAttribute(name, x);
1008   }
1009   function attrFunctionNS() {
1010     var x = value.apply(this, arguments);
1011     if (x == null) this.removeAttributeNS(name.space, name.local);
1012     else this.setAttributeNS(name.space, name.local, x);
1013   }
1014
1015   return value == null
1016       ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
1017       ? (name.local ? attrFunctionNS : attrFunction)
1018       : (name.local ? attrConstantNS : attrConstant));
1019 }
1020 function d3_collapse(s) {
1021   return s.trim().replace(/\s+/g, " ");
1022 }
1023
1024 d3_selectionPrototype.classed = function(name, value) {
1025   if (arguments.length < 2) {
1026
1027     // For classed(string), return true only if the first node has the specified
1028     // class or classes. Note that even if the browser supports DOMTokenList, it
1029     // probably doesn't support it on SVG elements (which can be animated).
1030     if (typeof name === "string") {
1031       var node = this.node(),
1032           n = (name = d3_selection_classes(name)).length,
1033           i = -1;
1034       if (value = node.classList) {
1035         while (++i < n) if (!value.contains(name[i])) return false;
1036       } else {
1037         value = node.getAttribute("class");
1038         while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
1039       }
1040       return true;
1041     }
1042
1043     // For classed(object), the object specifies the names of classes to add or
1044     // remove. The values may be functions that are evaluated for each element.
1045     for (value in name) this.each(d3_selection_classed(value, name[value]));
1046     return this;
1047   }
1048
1049   // Otherwise, both a name and a value are specified, and are handled as below.
1050   return this.each(d3_selection_classed(name, value));
1051 };
1052
1053 function d3_selection_classedRe(name) {
1054   return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
1055 }
1056
1057 function d3_selection_classes(name) {
1058   return (name + "").trim().split(/^|\s+/);
1059 }
1060
1061 // Multiple class names are allowed (e.g., "foo bar").
1062 function d3_selection_classed(name, value) {
1063   name = d3_selection_classes(name).map(d3_selection_classedName);
1064   var n = name.length;
1065
1066   function classedConstant() {
1067     var i = -1;
1068     while (++i < n) name[i](this, value);
1069   }
1070
1071   // When the value is a function, the function is still evaluated only once per
1072   // element even if there are multiple class names.
1073   function classedFunction() {
1074     var i = -1, x = value.apply(this, arguments);
1075     while (++i < n) name[i](this, x);
1076   }
1077
1078   return typeof value === "function"
1079       ? classedFunction
1080       : classedConstant;
1081 }
1082
1083 function d3_selection_classedName(name) {
1084   var re = d3_selection_classedRe(name);
1085   return function(node, value) {
1086     if (c = node.classList) return value ? c.add(name) : c.remove(name);
1087     var c = node.getAttribute("class") || "";
1088     if (value) {
1089       re.lastIndex = 0;
1090       if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
1091     } else {
1092       node.setAttribute("class", d3_collapse(c.replace(re, " ")));
1093     }
1094   };
1095 }
1096
1097 d3_selectionPrototype.style = function(name, value, priority) {
1098   var n = arguments.length;
1099   if (n < 3) {
1100
1101     // For style(object) or style(object, string), the object specifies the
1102     // names and values of the attributes to set or remove. The values may be
1103     // functions that are evaluated for each element. The optional string
1104     // specifies the priority.
1105     if (typeof name !== "string") {
1106       if (n < 2) value = "";
1107       for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
1108       return this;
1109     }
1110
1111     // For style(string), return the computed style value for the first node.
1112     if (n < 2) {
1113       var node = this.node();
1114       return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
1115     }
1116
1117     // For style(string, string) or style(string, function), use the default
1118     // priority. The priority is ignored for style(string, null).
1119     priority = "";
1120   }
1121
1122   // Otherwise, a name, value and priority are specified, and handled as below.
1123   return this.each(d3_selection_style(name, value, priority));
1124 };
1125
1126 function d3_selection_style(name, value, priority) {
1127
1128   // For style(name, null) or style(name, null, priority), remove the style
1129   // property with the specified name. The priority is ignored.
1130   function styleNull() {
1131     this.style.removeProperty(name);
1132   }
1133
1134   // For style(name, string) or style(name, string, priority), set the style
1135   // property with the specified name, using the specified priority.
1136   function styleConstant() {
1137     this.style.setProperty(name, value, priority);
1138   }
1139
1140   // For style(name, function) or style(name, function, priority), evaluate the
1141   // function for each element, and set or remove the style property as
1142   // appropriate. When setting, use the specified priority.
1143   function styleFunction() {
1144     var x = value.apply(this, arguments);
1145     if (x == null) this.style.removeProperty(name);
1146     else this.style.setProperty(name, x, priority);
1147   }
1148
1149   return value == null
1150       ? styleNull : (typeof value === "function"
1151       ? styleFunction : styleConstant);
1152 }
1153
1154 d3_selectionPrototype.property = function(name, value) {
1155   if (arguments.length < 2) {
1156
1157     // For property(string), return the property value for the first node.
1158     if (typeof name === "string") return this.node()[name];
1159
1160     // For property(object), the object specifies the names and values of the
1161     // properties to set or remove. The values may be functions that are
1162     // evaluated for each element.
1163     for (value in name) this.each(d3_selection_property(value, name[value]));
1164     return this;
1165   }
1166
1167   // Otherwise, both a name and a value are specified, and are handled as below.
1168   return this.each(d3_selection_property(name, value));
1169 };
1170
1171 function d3_selection_property(name, value) {
1172
1173   // For property(name, null), remove the property with the specified name.
1174   function propertyNull() {
1175     delete this[name];
1176   }
1177
1178   // For property(name, string), set the property with the specified name.
1179   function propertyConstant() {
1180     this[name] = value;
1181   }
1182
1183   // For property(name, function), evaluate the function for each element, and
1184   // set or remove the property as appropriate.
1185   function propertyFunction() {
1186     var x = value.apply(this, arguments);
1187     if (x == null) delete this[name];
1188     else this[name] = x;
1189   }
1190
1191   return value == null
1192       ? propertyNull : (typeof value === "function"
1193       ? propertyFunction : propertyConstant);
1194 }
1195
1196 d3_selectionPrototype.text = function(value) {
1197   return arguments.length
1198       ? this.each(typeof value === "function"
1199       ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null
1200       ? function() { if (this.textContent !== "") this.textContent = ""; }
1201       : function() { if (this.textContent !== value) this.textContent = value; })
1202       : this.node().textContent;
1203 };
1204
1205 d3_selectionPrototype.html = function(value) {
1206   return arguments.length
1207       ? this.each(typeof value === "function"
1208       ? function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; } : value == null
1209       ? function() { this.innerHTML = ""; }
1210       : function() { this.innerHTML = value; })
1211       : this.node().innerHTML;
1212 };
1213
1214 d3_selectionPrototype.append = function(name) {
1215   name = d3_selection_creator(name);
1216   return this.select(function() {
1217     return this.appendChild(name.apply(this, arguments));
1218   });
1219 };
1220
1221 function d3_selection_creator(name) {
1222
1223   function create() {
1224     var document = this.ownerDocument,
1225         namespace = this.namespaceURI;
1226     return namespace
1227         ? document.createElementNS(namespace, name)
1228         : document.createElement(name);
1229   }
1230
1231   function createNS() {
1232     return this.ownerDocument.createElementNS(name.space, name.local);
1233   }
1234
1235   return typeof name === "function" ? name
1236       : (name = d3.ns.qualify(name)).local ? createNS
1237       : create;
1238 }
1239
1240 d3_selectionPrototype.insert = function(name, before) {
1241   name = d3_selection_creator(name);
1242   before = d3_selection_selector(before);
1243   return this.select(function() {
1244     return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
1245   });
1246 };
1247
1248 // TODO remove(selector)?
1249 // TODO remove(node)?
1250 // TODO remove(function)?
1251 d3_selectionPrototype.remove = function() {
1252   return this.each(d3_selectionRemove);
1253 };
1254
1255 function d3_selectionRemove() {
1256   var parent = this.parentNode;
1257   if (parent) parent.removeChild(this);
1258 }
1259
1260 d3_selectionPrototype.data = function(value, key) {
1261   var i = -1,
1262       n = this.length,
1263       group,
1264       node;
1265
1266   // If no value is specified, return the first value.
1267   if (!arguments.length) {
1268     value = new Array(n = (group = this[0]).length);
1269     while (++i < n) {
1270       if (node = group[i]) {
1271         value[i] = node.__data__;
1272       }
1273     }
1274     return value;
1275   }
1276
1277   function bind(group, groupData) {
1278     var i,
1279         n = group.length,
1280         m = groupData.length,
1281         n0 = Math.min(n, m),
1282         updateNodes = new Array(m),
1283         enterNodes = new Array(m),
1284         exitNodes = new Array(n),
1285         node,
1286         nodeData;
1287
1288     if (key) {
1289       var nodeByKeyValue = new d3_Map,
1290           keyValues = new Array(n),
1291           keyValue;
1292
1293       for (i = -1; ++i < n;) {
1294         if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) {
1295           exitNodes[i] = node; // duplicate selection key
1296         } else {
1297           nodeByKeyValue.set(keyValue, node);
1298         }
1299         keyValues[i] = keyValue;
1300       }
1301
1302       for (i = -1; ++i < m;) {
1303         if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
1304           enterNodes[i] = d3_selection_dataNode(nodeData);
1305         } else if (node !== true) { // no duplicate data key
1306           updateNodes[i] = node;
1307           node.__data__ = nodeData;
1308         }
1309         nodeByKeyValue.set(keyValue, true);
1310       }
1311
1312       for (i = -1; ++i < n;) {
1313         if (nodeByKeyValue.get(keyValues[i]) !== true) {
1314           exitNodes[i] = group[i];
1315         }
1316       }
1317     } else {
1318       for (i = -1; ++i < n0;) {
1319         node = group[i];
1320         nodeData = groupData[i];
1321         if (node) {
1322           node.__data__ = nodeData;
1323           updateNodes[i] = node;
1324         } else {
1325           enterNodes[i] = d3_selection_dataNode(nodeData);
1326         }
1327       }
1328       for (; i < m; ++i) {
1329         enterNodes[i] = d3_selection_dataNode(groupData[i]);
1330       }
1331       for (; i < n; ++i) {
1332         exitNodes[i] = group[i];
1333       }
1334     }
1335
1336     enterNodes.update
1337         = updateNodes;
1338
1339     enterNodes.parentNode
1340         = updateNodes.parentNode
1341         = exitNodes.parentNode
1342         = group.parentNode;
1343
1344     enter.push(enterNodes);
1345     update.push(updateNodes);
1346     exit.push(exitNodes);
1347   }
1348
1349   var enter = d3_selection_enter([]),
1350       update = d3_selection([]),
1351       exit = d3_selection([]);
1352
1353   if (typeof value === "function") {
1354     while (++i < n) {
1355       bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1356     }
1357   } else {
1358     while (++i < n) {
1359       bind(group = this[i], value);
1360     }
1361   }
1362
1363   update.enter = function() { return enter; };
1364   update.exit = function() { return exit; };
1365   return update;
1366 };
1367
1368 function d3_selection_dataNode(data) {
1369   return {__data__: data};
1370 }
1371
1372 d3_selectionPrototype.datum = function(value) {
1373   return arguments.length
1374       ? this.property("__data__", value)
1375       : this.property("__data__");
1376 };
1377
1378 d3_selectionPrototype.filter = function(filter) {
1379   var subgroups = [],
1380       subgroup,
1381       group,
1382       node;
1383
1384   if (typeof filter !== "function") filter = d3_selection_filter(filter);
1385
1386   for (var j = 0, m = this.length; j < m; j++) {
1387     subgroups.push(subgroup = []);
1388     subgroup.parentNode = (group = this[j]).parentNode;
1389     for (var i = 0, n = group.length; i < n; i++) {
1390       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
1391         subgroup.push(node);
1392       }
1393     }
1394   }
1395
1396   return d3_selection(subgroups);
1397 };
1398
1399 function d3_selection_filter(selector) {
1400   return function() {
1401     return d3_selectMatches(this, selector);
1402   };
1403 }
1404
1405 d3_selectionPrototype.order = function() {
1406   for (var j = -1, m = this.length; ++j < m;) {
1407     for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
1408       if (node = group[i]) {
1409         if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
1410         next = node;
1411       }
1412     }
1413   }
1414   return this;
1415 };
1416
1417 d3_selectionPrototype.sort = function(comparator) {
1418   comparator = d3_selection_sortComparator.apply(this, arguments);
1419   for (var j = -1, m = this.length; ++j < m;) this[j].sort(comparator);
1420   return this.order();
1421 };
1422
1423 function d3_selection_sortComparator(comparator) {
1424   if (!arguments.length) comparator = d3_ascending;
1425   return function(a, b) {
1426     return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
1427   };
1428 }
1429
1430 d3_selectionPrototype.each = function(callback) {
1431   return d3_selection_each(this, function(node, i, j) {
1432     callback.call(node, node.__data__, i, j);
1433   });
1434 };
1435
1436 function d3_selection_each(groups, callback) {
1437   for (var j = 0, m = groups.length; j < m; j++) {
1438     for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
1439       if (node = group[i]) callback(node, i, j);
1440     }
1441   }
1442   return groups;
1443 }
1444
1445 d3_selectionPrototype.call = function(callback) {
1446   var args = d3_array(arguments);
1447   callback.apply(args[0] = this, args);
1448   return this;
1449 };
1450
1451 d3_selectionPrototype.empty = function() {
1452   return !this.node();
1453 };
1454
1455 d3_selectionPrototype.node = function() {
1456   for (var j = 0, m = this.length; j < m; j++) {
1457     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
1458       var node = group[i];
1459       if (node) return node;
1460     }
1461   }
1462   return null;
1463 };
1464
1465 d3_selectionPrototype.size = function() {
1466   var n = 0;
1467   d3_selection_each(this, function() { ++n; });
1468   return n;
1469 };
1470
1471 function d3_selection_enter(selection) {
1472   d3_subclass(selection, d3_selection_enterPrototype);
1473   return selection;
1474 }
1475
1476 var d3_selection_enterPrototype = [];
1477
1478 d3.selection.enter = d3_selection_enter;
1479 d3.selection.enter.prototype = d3_selection_enterPrototype;
1480
1481 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
1482 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
1483 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
1484 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
1485 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
1486
1487
1488 d3_selection_enterPrototype.select = function(selector) {
1489   var subgroups = [],
1490       subgroup,
1491       subnode,
1492       upgroup,
1493       group,
1494       node;
1495
1496   for (var j = -1, m = this.length; ++j < m;) {
1497     upgroup = (group = this[j]).update;
1498     subgroups.push(subgroup = []);
1499     subgroup.parentNode = group.parentNode;
1500     for (var i = -1, n = group.length; ++i < n;) {
1501       if (node = group[i]) {
1502         subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
1503         subnode.__data__ = node.__data__;
1504       } else {
1505         subgroup.push(null);
1506       }
1507     }
1508   }
1509
1510   return d3_selection(subgroups);
1511 };
1512
1513 d3_selection_enterPrototype.insert = function(name, before) {
1514   if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
1515   return d3_selectionPrototype.insert.call(this, name, before);
1516 };
1517
1518 function d3_selection_enterInsertBefore(enter) {
1519   var i0, j0;
1520   return function(d, i, j) {
1521     var group = enter[j].update,
1522         n = group.length,
1523         node;
1524     if (j != j0) j0 = j, i0 = 0;
1525     if (i >= i0) i0 = i + 1;
1526     while (!(node = group[i0]) && ++i0 < n);
1527     return node;
1528   };
1529 }
1530
1531 // TODO fast singleton implementation?
1532 d3.select = function(node) {
1533   var group;
1534   if (typeof node === "string") {
1535     group = [d3_select(node, d3_document)];
1536     group.parentNode = d3_document.documentElement;
1537   } else {
1538     group = [node];
1539     group.parentNode = d3_documentElement(node);
1540   }
1541   return d3_selection([group]);
1542 };
1543
1544 d3.selectAll = function(nodes) {
1545   var group;
1546   if (typeof nodes === "string") {
1547     group = d3_array(d3_selectAll(nodes, d3_document));
1548     group.parentNode = d3_document.documentElement;
1549   } else {
1550     group = nodes;
1551     group.parentNode = null;
1552   }
1553   return d3_selection([group]);
1554 };
1555
1556 d3_selectionPrototype.on = function(type, listener, capture) {
1557   var n = arguments.length;
1558   if (n < 3) {
1559
1560     // For on(object) or on(object, boolean), the object specifies the event
1561     // types and listeners to add or remove. The optional boolean specifies
1562     // whether the listener captures events.
1563     if (typeof type !== "string") {
1564       if (n < 2) listener = false;
1565       for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
1566       return this;
1567     }
1568
1569     // For on(string), return the listener for the first node.
1570     if (n < 2) return (n = this.node()["__on" + type]) && n._;
1571
1572     // For on(string, function), use the default capture.
1573     capture = false;
1574   }
1575
1576   // Otherwise, a type, listener and capture are specified, and handled as below.
1577   return this.each(d3_selection_on(type, listener, capture));
1578 };
1579
1580 function d3_selection_on(type, listener, capture) {
1581   var name = "__on" + type,
1582       i = type.indexOf("."),
1583       wrap = d3_selection_onListener;
1584
1585   if (i > 0) type = type.slice(0, i);
1586   var filter = d3_selection_onFilters.get(type);
1587   if (filter) type = filter, wrap = d3_selection_onFilter;
1588
1589   function onRemove() {
1590     var l = this[name];
1591     if (l) {
1592       this.removeEventListener(type, l, l.$);
1593       delete this[name];
1594     }
1595   }
1596
1597   function onAdd() {
1598     var l = wrap(listener, d3_array(arguments));
1599     if (typeof Raven !== 'undefined') l = Raven.wrap(l);
1600     onRemove.call(this);
1601     this.addEventListener(type, this[name] = l, l.$ = capture);
1602     l._ = listener;
1603   }
1604
1605   function removeAll() {
1606     var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"),
1607         match;
1608     for (var name in this) {
1609       if (match = name.match(re)) {
1610         var l = this[name];
1611         this.removeEventListener(match[1], l, l.$);
1612         delete this[name];
1613       }
1614     }
1615   }
1616
1617   return i
1618       ? listener ? onAdd : onRemove
1619       : listener ? d3_noop : removeAll;
1620 }
1621
1622 var d3_selection_onFilters = d3.map({
1623   mouseenter: "mouseover",
1624   mouseleave: "mouseout"
1625 });
1626
1627 if (d3_document) {
1628   d3_selection_onFilters.forEach(function(k) {
1629     if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1630   });
1631 }
1632
1633 function d3_selection_onListener(listener, argumentz) {
1634   return function(e) {
1635     var o = d3.event; // Events can be reentrant (e.g., focus).
1636     d3.event = e;
1637     argumentz[0] = this.__data__;
1638     try {
1639       listener.apply(this, argumentz);
1640     } finally {
1641       d3.event = o;
1642     }
1643   };
1644 }
1645
1646 function d3_selection_onFilter(listener, argumentz) {
1647   var l = d3_selection_onListener(listener, argumentz);
1648   return function(e) {
1649     var target = this, related = e.relatedTarget;
1650     if (!related || (related !== target && !(related.compareDocumentPosition(target) & 8))) {
1651       l.call(target, e);
1652     }
1653   };
1654 }
1655
1656 var d3_event_dragSelect,
1657     d3_event_dragId = 0;
1658
1659 function d3_event_dragSuppress(node) {
1660   var name = ".dragsuppress-" + ++d3_event_dragId,
1661       click = "click" + name,
1662       w = d3.select(d3_window(node))
1663           .on("touchmove" + name, d3_eventPreventDefault)
1664           .on("dragstart" + name, d3_eventPreventDefault)
1665           .on("selectstart" + name, d3_eventPreventDefault);
1666
1667   if (d3_event_dragSelect == null) {
1668     d3_event_dragSelect = "onselectstart" in node ? false
1669         : d3_vendorSymbol(node.style, "userSelect");
1670   }
1671
1672   if (d3_event_dragSelect) {
1673     var style = d3_documentElement(node).style,
1674         select = style[d3_event_dragSelect];
1675     style[d3_event_dragSelect] = "none";
1676   }
1677
1678   return function(suppressClick) {
1679     w.on(name, null);
1680     if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
1681     if (suppressClick) { // suppress the next click, but only if it’s immediate
1682       var off = function() { w.on(click, null); };
1683       w.on(click, function() { d3_eventCancel(); off(); }, true);
1684       setTimeout(off, 0);
1685     }
1686   };
1687 }
1688
1689 d3.mouse = function(container) {
1690   return d3_mousePoint(container, d3_eventSource());
1691 };
1692
1693 // https://bugs.webkit.org/show_bug.cgi?id=44083
1694 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1695
1696 function d3_mousePoint(container, e) {
1697   if (e.changedTouches) e = e.changedTouches[0];
1698   var svg = container.ownerSVGElement || container;
1699   if (svg.createSVGPoint) {
1700     var point = svg.createSVGPoint();
1701     if (d3_mouse_bug44083 < 0) {
1702       var window = d3_window(container);
1703       if (window.scrollX || window.scrollY) {
1704         svg = d3.select("body").append("svg").style({
1705           position: "absolute",
1706           top: 0,
1707           left: 0,
1708           margin: 0,
1709           padding: 0,
1710           border: "none"
1711         }, "important");
1712         var ctm = svg[0][0].getScreenCTM();
1713         d3_mouse_bug44083 = !(ctm.f || ctm.e);
1714         svg.remove();
1715       }
1716     }
1717     if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY;
1718     else point.x = e.clientX, point.y = e.clientY;
1719     point = point.matrixTransform(container.getScreenCTM().inverse());
1720     return [point.x, point.y];
1721   }
1722   var rect = container.getBoundingClientRect();
1723   return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop];
1724 };
1725
1726 d3.touches = function(container, touches) {
1727   if (arguments.length < 2) touches = d3_eventSource().touches;
1728   return touches ? d3_array(touches).map(function(touch) {
1729     var point = d3_mousePoint(container, touch);
1730     point.identifier = touch.identifier;
1731     return point;
1732   }) : [];
1733 };
1734 var ε = 1e-6,
1735     ε2 = ε * ε,
1736     π = Math.PI,
1737     τ = 2 * π,
1738     τε = τ - ε,
1739     halfπ = π / 2,
1740     d3_radians = π / 180,
1741     d3_degrees = 180 / π;
1742
1743 function d3_sgn(x) {
1744   return x > 0 ? 1 : x < 0 ? -1 : 0;
1745 }
1746
1747 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
1748 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
1749 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
1750 // negative if clockwise, and zero if the points are collinear.
1751 function d3_cross2d(a, b, c) {
1752   return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1753 }
1754
1755 function d3_acos(x) {
1756   return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
1757 }
1758
1759 function d3_asin(x) {
1760   return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
1761 }
1762
1763 function d3_sinh(x) {
1764   return ((x = Math.exp(x)) - 1 / x) / 2;
1765 }
1766
1767 function d3_cosh(x) {
1768   return ((x = Math.exp(x)) + 1 / x) / 2;
1769 }
1770
1771 function d3_tanh(x) {
1772   return ((x = Math.exp(2 * x)) - 1) / (x + 1);
1773 }
1774
1775 function d3_haversin(x) {
1776   return (x = Math.sin(x / 2)) * x;
1777 }
1778
1779 var ρ = Math.SQRT2,
1780     ρ2 = 2,
1781     ρ4 = 4;
1782
1783 // p0 = [ux0, uy0, w0]
1784 // p1 = [ux1, uy1, w1]
1785 d3.interpolateZoom = function(p0, p1) {
1786   var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
1787       ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
1788
1789   var dx = ux1 - ux0,
1790       dy = uy1 - uy0,
1791       d2 = dx * dx + dy * dy,
1792       d1 = Math.sqrt(d2),
1793       b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1),
1794       b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1),
1795       r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
1796       r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1),
1797       dr = r1 - r0,
1798       S = (dr || Math.log(w1 / w0)) / ρ;
1799
1800   function interpolate(t) {
1801     var s = t * S;
1802     if (dr) {
1803       // General case.
1804       var coshr0 = d3_cosh(r0),
1805           u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
1806       return [
1807         ux0 + u * dx,
1808         uy0 + u * dy,
1809         w0 * coshr0 / d3_cosh(ρ * s + r0)
1810       ];
1811     }
1812     // Special case for u0 ~= u1.
1813     return [
1814       ux0 + t * dx,
1815       uy0 + t * dy,
1816       w0 * Math.exp(ρ * s)
1817     ];
1818   }
1819
1820   interpolate.duration = S * 1000;
1821
1822   return interpolate;
1823 };
1824
1825 d3.behavior.zoom = function() {
1826   var view = {x: 0, y: 0, k: 1},
1827       translate0, // translate when we started zooming (to avoid drift)
1828       center0, // implicit desired position of translate0 after zooming
1829       center, // explicit desired position of translate0 after zooming
1830       size = [960, 500], // viewport size; required for zoom interpolation
1831       scaleExtent = d3_behavior_zoomInfinity,
1832       duration = 250,
1833       zooming = 0,
1834       mousedown = "mousedown.zoom",
1835       mousemove = "mousemove.zoom",
1836       mouseup = "mouseup.zoom",
1837       mousewheelTimer,
1838       touchstart = "touchstart.zoom",
1839       touchtime, // time of last touchstart (to detect double-tap)
1840       event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
1841       x0,
1842       x1,
1843       y0,
1844       y1;
1845
1846   // Lazily determine the DOM’s support for Wheel events.
1847   // https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
1848   if (!d3_behavior_zoomWheel) {
1849     d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
1850         : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
1851         : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
1852   }
1853
1854   function zoom(g) {
1855     g   .on(mousedown, mousedowned)
1856         .on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
1857         .on("dblclick.zoom", dblclicked)
1858         .on(touchstart, touchstarted);
1859   }
1860
1861   zoom.event = function(g) {
1862     g.each(function() {
1863       var dispatch = event.of(this, arguments),
1864           view1 = view;
1865       if (d3_transitionInheritId) {
1866         d3.select(this).transition()
1867             .each("start.zoom", function() {
1868               view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state
1869               zoomstarted(dispatch);
1870             })
1871             .tween("zoom:zoom", function() {
1872               var dx = size[0],
1873                   dy = size[1],
1874                   cx = center0 ? center0[0] : dx / 2,
1875                   cy = center0 ? center0[1] : dy / 2,
1876                   i = d3.interpolateZoom(
1877                     [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k],
1878                     [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k]
1879                   );
1880               return function(t) {
1881                 var l = i(t), k = dx / l[2];
1882                 this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k};
1883                 zoomed(dispatch);
1884               };
1885             })
1886             .each("interrupt.zoom", function() {
1887               zoomended(dispatch);
1888             })
1889             .each("end.zoom", function() {
1890               zoomended(dispatch);
1891             });
1892       } else {
1893         this.__chart__ = view;
1894         zoomstarted(dispatch);
1895         zoomed(dispatch);
1896         zoomended(dispatch);
1897       }
1898     });
1899   }
1900
1901   zoom.translate = function(_) {
1902     if (!arguments.length) return [view.x, view.y];
1903     view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write
1904     rescale();
1905     return zoom;
1906   };
1907
1908   zoom.scale = function(_) {
1909     if (!arguments.length) return view.k;
1910     view = {x: view.x, y: view.y, k: +_}; // copy-on-write
1911     rescale();
1912     return zoom;
1913   };
1914
1915   zoom.scaleExtent = function(_) {
1916     if (!arguments.length) return scaleExtent;
1917     scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
1918     return zoom;
1919   };
1920
1921   zoom.center = function(_) {
1922     if (!arguments.length) return center;
1923     center = _ && [+_[0], +_[1]];
1924     return zoom;
1925   };
1926
1927   zoom.size = function(_) {
1928     if (!arguments.length) return size;
1929     size = _ && [+_[0], +_[1]];
1930     return zoom;
1931   };
1932
1933   zoom.duration = function(_) {
1934     if (!arguments.length) return duration;
1935     duration = +_; // TODO function based on interpolateZoom distance?
1936     return zoom;
1937   };
1938
1939   zoom.x = function(z) {
1940     if (!arguments.length) return x1;
1941     x1 = z;
1942     x0 = z.copy();
1943     view = {x: 0, y: 0, k: 1}; // copy-on-write
1944     return zoom;
1945   };
1946
1947   zoom.y = function(z) {
1948     if (!arguments.length) return y1;
1949     y1 = z;
1950     y0 = z.copy();
1951     view = {x: 0, y: 0, k: 1}; // copy-on-write
1952     return zoom;
1953   };
1954
1955   function location(p) {
1956     return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
1957   }
1958
1959   function point(l) {
1960     return [l[0] * view.k + view.x, l[1] * view.k + view.y];
1961   }
1962
1963   function scaleTo(s) {
1964     view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
1965   }
1966
1967   function translateTo(p, l) {
1968     l = point(l);
1969     view.x += p[0] - l[0];
1970     view.y += p[1] - l[1];
1971   }
1972
1973   function zoomTo(that, p, l, k) {
1974     that.__chart__ = {x: view.x, y: view.y, k: view.k};
1975
1976     scaleTo(Math.pow(2, k));
1977     translateTo(center0 = p, l);
1978
1979     that = d3.select(that);
1980     if (duration > 0) that = that.transition().duration(duration);
1981     that.call(zoom.event);
1982   }
1983
1984   function rescale() {
1985     if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert));
1986     if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert));
1987   }
1988
1989   function zoomstarted(dispatch) {
1990     if (!zooming++) dispatch({type: "zoomstart"});
1991   }
1992
1993   function zoomed(dispatch) {
1994     rescale();
1995     dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]});
1996   }
1997
1998   function zoomended(dispatch) {
1999     if (!--zooming) dispatch({type: "zoomend"});
2000     center0 = null;
2001   }
2002
2003   function mousedowned() {
2004     var that = this,
2005         target = d3.event.target,
2006         dispatch = event.of(that, arguments),
2007         dragged = 0,
2008         subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
2009         location0 = location(d3.mouse(that)),
2010         dragRestore = d3_event_dragSuppress(that);
2011
2012     d3_selection_interrupt.call(that);
2013     zoomstarted(dispatch);
2014
2015     function moved() {
2016       dragged = 1;
2017       translateTo(d3.mouse(that), location0);
2018       zoomed(dispatch);
2019     }
2020
2021     function ended() {
2022       subject.on(mousemove, null).on(mouseup, null);
2023       dragRestore(dragged && d3.event.target === target);
2024       zoomended(dispatch);
2025     }
2026   }
2027
2028   // These closures persist for as long as at least one touch is active.
2029   function touchstarted() {
2030     var that = this,
2031         dispatch = event.of(that, arguments),
2032         locations0 = {}, // touchstart locations
2033         distance0 = 0, // distance² between initial touches
2034         scale0, // scale when we started touching
2035         zoomName = ".zoom-" + d3.event.changedTouches[0].identifier,
2036         touchmove = "touchmove" + zoomName,
2037         touchend = "touchend" + zoomName,
2038         targets = [],
2039         subject = d3.select(that),
2040         dragRestore = d3_event_dragSuppress(that);
2041
2042     started();
2043     zoomstarted(dispatch);
2044
2045     // Workaround for Chrome issue 412723: the touchstart listener must be set
2046     // after the touchmove listener.
2047     subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events
2048
2049     // Updates locations of any touches in locations0.
2050     function relocate() {
2051       var touches = d3.touches(that);
2052       scale0 = view.k;
2053       touches.forEach(function(t) {
2054         if (t.identifier in locations0) locations0[t.identifier] = location(t);
2055       });
2056       return touches;
2057     }
2058
2059     // Temporarily override touchstart while gesture is active.
2060     function started() {
2061
2062       // Listen for touchmove and touchend on the target of touchstart.
2063       var target = d3.event.target;
2064       d3.select(target).on(touchmove, moved).on(touchend, ended);
2065       targets.push(target);
2066
2067       // Only track touches started on the same subject element.
2068       var changed = d3.event.changedTouches;
2069       for (var i = 0, n = changed.length; i < n; ++i) {
2070         locations0[changed[i].identifier] = null;
2071       }
2072
2073       var touches = relocate(),
2074           now = Date.now();
2075
2076       if (touches.length === 1) {
2077         if (now - touchtime < 500) { // dbltap
2078           var p = touches[0];
2079           zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2080           d3_eventPreventDefault();
2081         }
2082         touchtime = now;
2083       } else if (touches.length > 1) {
2084         var p = touches[0], q = touches[1],
2085             dx = p[0] - q[0], dy = p[1] - q[1];
2086         distance0 = dx * dx + dy * dy;
2087       }
2088     }
2089
2090     function moved() {
2091       var touches = d3.touches(that),
2092           p0, l0,
2093           p1, l1;
2094
2095       d3_selection_interrupt.call(that);
2096
2097       for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2098         p1 = touches[i];
2099         if (l1 = locations0[p1.identifier]) {
2100           if (l0) break;
2101           p0 = p1, l0 = l1;
2102         }
2103       }
2104
2105       if (l1) {
2106         var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
2107             scale1 = distance0 && Math.sqrt(distance1 / distance0);
2108         p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
2109         l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
2110         scaleTo(scale1 * scale0);
2111       }
2112
2113       touchtime = null;
2114       translateTo(p0, l0);
2115       zoomed(dispatch);
2116     }
2117
2118     function ended() {
2119       // If there are any globally-active touches remaining, remove the ended
2120       // touches from locations0.
2121       if (d3.event.touches.length) {
2122         var changed = d3.event.changedTouches;
2123         for (var i = 0, n = changed.length; i < n; ++i) {
2124           delete locations0[changed[i].identifier];
2125         }
2126         // If locations0 is not empty, then relocate and continue listening for
2127         // touchmove and touchend.
2128         for (var identifier in locations0) {
2129           return void relocate(); // locations may have detached due to rotation
2130         }
2131       }
2132       // Otherwise, remove touchmove and touchend listeners.
2133       d3.selectAll(targets).on(zoomName, null);
2134       subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2135       dragRestore();
2136       zoomended(dispatch);
2137     }
2138   }
2139
2140   function mousewheeled() {
2141     var dispatch = event.of(this, arguments);
2142     if (mousewheelTimer) clearTimeout(mousewheelTimer);
2143     else translate0 = location(center0 = center || d3.mouse(this)), d3_selection_interrupt.call(this), zoomstarted(dispatch);
2144     mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50);
2145     d3_eventPreventDefault();
2146     scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
2147     translateTo(center0, translate0);
2148     zoomed(dispatch);
2149   }
2150
2151   function dblclicked() {
2152     var p = d3.mouse(this),
2153         k = Math.log(view.k) / Math.LN2;
2154
2155     zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2156   }
2157
2158   return d3.rebind(zoom, event, "on");
2159 };
2160
2161 var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
2162     d3_behavior_zoomDelta, // initialized lazily
2163     d3_behavior_zoomWheel;
2164 function d3_functor(v) {
2165   return typeof v === "function" ? v : function() { return v; };
2166 }
2167
2168 d3.functor = d3_functor;
2169
2170 d3.touch = function(container, touches, identifier) {
2171   if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
2172   if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
2173     if ((touch = touches[i]).identifier === identifier) {
2174       return d3_mousePoint(container, touch);
2175     }
2176   }
2177 };
2178
2179 var d3_timer_queueHead,
2180     d3_timer_queueTail,
2181     d3_timer_interval, // is an interval (or frame) active?
2182     d3_timer_timeout, // is a timeout active?
2183     d3_timer_active, // active timer object
2184     d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };
2185
2186 // The timer will continue to fire until callback returns true.
2187 d3.timer = function(callback, delay, then) {
2188   var n = arguments.length;
2189   if (n < 2) delay = 0;
2190   if (n < 3) then = Date.now();
2191
2192   // Add the callback to the tail of the queue.
2193   var time = then + delay, timer = {c: callback, t: time, f: false, n: null};
2194   if (d3_timer_queueTail) d3_timer_queueTail.n = timer;
2195   else d3_timer_queueHead = timer;
2196   d3_timer_queueTail = timer;
2197
2198   // Start animatin'!
2199   if (!d3_timer_interval) {
2200     d3_timer_timeout = clearTimeout(d3_timer_timeout);
2201     d3_timer_interval = 1;
2202     d3_timer_frame(d3_timer_step);
2203   }
2204 };
2205
2206 function d3_timer_step() {
2207   var now = d3_timer_mark(),
2208       delay = d3_timer_sweep() - now;
2209   if (delay > 24) {
2210     if (isFinite(delay)) {
2211       clearTimeout(d3_timer_timeout);
2212       d3_timer_timeout = setTimeout(d3_timer_step, delay);
2213     }
2214     d3_timer_interval = 0;
2215   } else {
2216     d3_timer_interval = 1;
2217     d3_timer_frame(d3_timer_step);
2218   }
2219 }
2220
2221 d3.timer.flush = function() {
2222   d3_timer_mark();
2223   d3_timer_sweep();
2224 };
2225
2226 function d3_timer_mark() {
2227   var now = Date.now();
2228   d3_timer_active = d3_timer_queueHead;
2229   while (d3_timer_active) {
2230     if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
2231     d3_timer_active = d3_timer_active.n;
2232   }
2233   return now;
2234 }
2235
2236 // Flush after callbacks to avoid concurrent queue modification.
2237 // Returns the time of the earliest active timer, post-sweep.
2238 function d3_timer_sweep() {
2239   var t0,
2240       t1 = d3_timer_queueHead,
2241       time = Infinity;
2242   while (t1) {
2243     if (t1.f) {
2244       t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2245     } else {
2246       if (t1.t < time) time = t1.t;
2247       t1 = (t0 = t1).n;
2248     }
2249   }
2250   d3_timer_queueTail = t0;
2251   return time;
2252 }
2253 d3.geo = {};
2254
2255 d3.geo.stream = function(object, listener) {
2256   if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
2257     d3_geo_streamObjectType[object.type](object, listener);
2258   } else {
2259     d3_geo_streamGeometry(object, listener);
2260   }
2261 };
2262
2263 function d3_geo_streamGeometry(geometry, listener) {
2264   if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
2265     d3_geo_streamGeometryType[geometry.type](geometry, listener);
2266   }
2267 }
2268
2269 var d3_geo_streamObjectType = {
2270   Feature: function(feature, listener) {
2271     d3_geo_streamGeometry(feature.geometry, listener);
2272   },
2273   FeatureCollection: function(object, listener) {
2274     var features = object.features, i = -1, n = features.length;
2275     while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
2276   }
2277 };
2278
2279 var d3_geo_streamGeometryType = {
2280   Sphere: function(object, listener) {
2281     listener.sphere();
2282   },
2283   Point: function(object, listener) {
2284     object = object.coordinates;
2285     listener.point(object[0], object[1], object[2]);
2286   },
2287   MultiPoint: function(object, listener) {
2288     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2289     while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
2290   },
2291   LineString: function(object, listener) {
2292     d3_geo_streamLine(object.coordinates, listener, 0);
2293   },
2294   MultiLineString: function(object, listener) {
2295     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2296     while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
2297   },
2298   Polygon: function(object, listener) {
2299     d3_geo_streamPolygon(object.coordinates, listener);
2300   },
2301   MultiPolygon: function(object, listener) {
2302     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2303     while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
2304   },
2305   GeometryCollection: function(object, listener) {
2306     var geometries = object.geometries, i = -1, n = geometries.length;
2307     while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
2308   }
2309 };
2310
2311 function d3_geo_streamLine(coordinates, listener, closed) {
2312   var i = -1, n = coordinates.length - closed, coordinate;
2313   listener.lineStart();
2314   while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
2315   listener.lineEnd();
2316 }
2317
2318 function d3_geo_streamPolygon(coordinates, listener) {
2319   var i = -1, n = coordinates.length;
2320   listener.polygonStart();
2321   while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
2322   listener.polygonEnd();
2323 }
2324
2325 d3.geo.length = function(object) {
2326   d3_geo_lengthSum = 0;
2327   d3.geo.stream(object, d3_geo_length);
2328   return d3_geo_lengthSum;
2329 };
2330
2331 var d3_geo_lengthSum;
2332
2333 var d3_geo_length = {
2334   sphere: d3_noop,
2335   point: d3_noop,
2336   lineStart: d3_geo_lengthLineStart,
2337   lineEnd: d3_noop,
2338   polygonStart: d3_noop,
2339   polygonEnd: d3_noop
2340 };
2341
2342 function d3_geo_lengthLineStart() {
2343   var λ0, sinφ0, cosφ0;
2344
2345   d3_geo_length.point = function(λ, φ) {
2346     λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
2347     d3_geo_length.point = nextPoint;
2348   };
2349
2350   d3_geo_length.lineEnd = function() {
2351     d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
2352   };
2353
2354   function nextPoint(λ, φ) {
2355     var sinφ = Math.sin(φ *= d3_radians),
2356         cosφ = Math.cos(φ),
2357         t = abs((λ *= d3_radians) - λ0),
2358         cosΔλ = Math.cos(t);
2359     d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
2360     λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
2361   }
2362 }
2363 function d3_identity(d) {
2364   return d;
2365 }
2366 function d3_true() {
2367   return true;
2368 }
2369
2370 function d3_geo_spherical(cartesian) {
2371   return [
2372     Math.atan2(cartesian[1], cartesian[0]),
2373     d3_asin(cartesian[2])
2374   ];
2375 }
2376
2377 function d3_geo_sphericalEqual(a, b) {
2378   return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
2379 }
2380
2381 // General spherical polygon clipping algorithm: takes a polygon, cuts it into
2382 // visible line segments and rejoins the segments by interpolating along the
2383 // clip edge.
2384 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
2385   var subject = [],
2386       clip = [];
2387
2388   segments.forEach(function(segment) {
2389     if ((n = segment.length - 1) <= 0) return;
2390     var n, p0 = segment[0], p1 = segment[n];
2391
2392     // If the first and last points of a segment are coincident, then treat as
2393     // a closed ring.
2394     // TODO if all rings are closed, then the winding order of the exterior
2395     // ring should be checked.
2396     if (d3_geo_sphericalEqual(p0, p1)) {
2397       listener.lineStart();
2398       for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
2399       listener.lineEnd();
2400       return;
2401     }
2402
2403     var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
2404         b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
2405     a.o = b;
2406     subject.push(a);
2407     clip.push(b);
2408     a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
2409     b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
2410     a.o = b;
2411     subject.push(a);
2412     clip.push(b);
2413   });
2414   clip.sort(compare);
2415   d3_geo_clipPolygonLinkCircular(subject);
2416   d3_geo_clipPolygonLinkCircular(clip);
2417   if (!subject.length) return;
2418
2419   for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
2420     clip[i].e = entry = !entry;
2421   }
2422
2423   var start = subject[0],
2424       points,
2425       point;
2426   while (1) {
2427     // Find first unvisited intersection.
2428     var current = start,
2429         isSubject = true;
2430     while (current.v) if ((current = current.n) === start) return;
2431     points = current.z;
2432     listener.lineStart();
2433     do {
2434       current.v = current.o.v = true;
2435       if (current.e) {
2436         if (isSubject) {
2437           for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
2438         } else {
2439           interpolate(current.x, current.n.x, 1, listener);
2440         }
2441         current = current.n;
2442       } else {
2443         if (isSubject) {
2444           points = current.p.z;
2445           for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
2446         } else {
2447           interpolate(current.x, current.p.x, -1, listener);
2448         }
2449         current = current.p;
2450       }
2451       current = current.o;
2452       points = current.z;
2453       isSubject = !isSubject;
2454     } while (!current.v);
2455     listener.lineEnd();
2456   }
2457 }
2458
2459 function d3_geo_clipPolygonLinkCircular(array) {
2460   if (!(n = array.length)) return;
2461   var n,
2462       i = 0,
2463       a = array[0],
2464       b;
2465   while (++i < n) {
2466     a.n = b = array[i];
2467     b.p = a;
2468     a = b;
2469   }
2470   a.n = b = array[0];
2471   b.p = a;
2472 }
2473
2474 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
2475   this.x = point;
2476   this.z = points;
2477   this.o = other; // another intersection
2478   this.e = entry; // is an entry?
2479   this.v = false; // visited
2480   this.n = this.p = null; // next & previous
2481 }
2482
2483 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
2484   return function(rotate, listener) {
2485     var line = clipLine(listener),
2486         rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
2487
2488     var clip = {
2489       point: point,
2490       lineStart: lineStart,
2491       lineEnd: lineEnd,
2492       polygonStart: function() {
2493         clip.point = pointRing;
2494         clip.lineStart = ringStart;
2495         clip.lineEnd = ringEnd;
2496         segments = [];
2497         polygon = [];
2498       },
2499       polygonEnd: function() {
2500         clip.point = point;
2501         clip.lineStart = lineStart;
2502         clip.lineEnd = lineEnd;
2503
2504         segments = d3.merge(segments);
2505         var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
2506         if (segments.length) {
2507           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2508           d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
2509         } else if (clipStartInside) {
2510           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2511           listener.lineStart();
2512           interpolate(null, null, 1, listener);
2513           listener.lineEnd();
2514         }
2515         if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
2516         segments = polygon = null;
2517       },
2518       sphere: function() {
2519         listener.polygonStart();
2520         listener.lineStart();
2521         interpolate(null, null, 1, listener);
2522         listener.lineEnd();
2523         listener.polygonEnd();
2524       }
2525     };
2526
2527     function point(λ, φ) {
2528       var point = rotate(λ, φ);
2529       if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
2530     }
2531     function pointLine(λ, φ) {
2532       var point = rotate(λ, φ);
2533       line.point(point[0], point[1]);
2534     }
2535     function lineStart() { clip.point = pointLine; line.lineStart(); }
2536     function lineEnd() { clip.point = point; line.lineEnd(); }
2537
2538     var segments;
2539
2540     var buffer = d3_geo_clipBufferListener(),
2541         ringListener = clipLine(buffer),
2542         polygonStarted = false,
2543         polygon,
2544         ring;
2545
2546     function pointRing(λ, φ) {
2547       ring.push([λ, φ]);
2548       var point = rotate(λ, φ);
2549       ringListener.point(point[0], point[1]);
2550     }
2551
2552     function ringStart() {
2553       ringListener.lineStart();
2554       ring = [];
2555     }
2556
2557     function ringEnd() {
2558       pointRing(ring[0][0], ring[0][1]);
2559       ringListener.lineEnd();
2560
2561       var clean = ringListener.clean(),
2562           ringSegments = buffer.buffer(),
2563           segment,
2564           n = ringSegments.length;
2565
2566       ring.pop();
2567       polygon.push(ring);
2568       ring = null;
2569
2570       if (!n) return;
2571
2572       // No intersections.
2573       if (clean & 1) {
2574         segment = ringSegments[0];
2575         var n = segment.length - 1,
2576             i = -1,
2577             point;
2578         if (n > 0) {
2579           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2580           listener.lineStart();
2581           while (++i < n) listener.point((point = segment[i])[0], point[1]);
2582           listener.lineEnd();
2583         }
2584         return;
2585       }
2586
2587       // Rejoin connected segments.
2588       // TODO reuse bufferListener.rejoin()?
2589       if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
2590
2591       segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
2592     }
2593
2594     return clip;
2595   };
2596 }
2597
2598 function d3_geo_clipSegmentLength1(segment) {
2599   return segment.length > 1;
2600 }
2601
2602 function d3_geo_clipBufferListener() {
2603   var lines = [],
2604       line;
2605   return {
2606     lineStart: function() { lines.push(line = []); },
2607     point: function(λ, φ) { line.push([λ, φ]); },
2608     lineEnd: d3_noop,
2609     buffer: function() {
2610       var buffer = lines;
2611       lines = [];
2612       line = null;
2613       return buffer;
2614     },
2615     rejoin: function() {
2616       if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
2617     }
2618   };
2619 }
2620
2621 // Intersection points are sorted along the clip edge. For both antimeridian
2622 // cutting and circle clipping, the same comparison is used.
2623 function d3_geo_clipSort(a, b) {
2624   return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1])
2625        - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
2626 }
2627
2628 var d3_geo_clipAntimeridian = d3_geo_clip(
2629     d3_true,
2630     d3_geo_clipAntimeridianLine,
2631     d3_geo_clipAntimeridianInterpolate,
2632     [-π, -π / 2]);
2633
2634 // Takes a line and cuts into visible segments. Return values:
2635 //   0: there were intersections or the line was empty.
2636 //   1: no intersections.
2637 //   2: there were intersections, and the first and last segments should be
2638 //      rejoined.
2639 function d3_geo_clipAntimeridianLine(listener) {
2640   var λ0 = NaN,
2641       φ0 = NaN,
2642       sλ0 = NaN,
2643       clean; // no intersections
2644
2645   return {
2646     lineStart: function() {
2647       listener.lineStart();
2648       clean = 1;
2649     },
2650     point: function(λ1, φ1) {
2651       var sλ1 = λ1 > 0 ? π : -π,
2652           dλ = abs(λ1 - λ0);
2653       if (abs(dλ - π) < ε) { // line crosses a pole
2654         listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
2655         listener.point(sλ0, φ0);
2656         listener.lineEnd();
2657         listener.lineStart();
2658         listener.point(sλ1, φ0);
2659         listener.point(λ1, φ0);
2660         clean = 0;
2661       } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian
2662         // handle degeneracies
2663         if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
2664         if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
2665         φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
2666         listener.point(sλ0, φ0);
2667         listener.lineEnd();
2668         listener.lineStart();
2669         listener.point(sλ1, φ0);
2670         clean = 0;
2671       }
2672       listener.point(λ0 = λ1, φ0 = φ1);
2673       sλ0 = sλ1;
2674     },
2675     lineEnd: function() {
2676       listener.lineEnd();
2677       λ0 = φ0 = NaN;
2678     },
2679     // if there are intersections, we always rejoin the first and last segments.
2680     clean: function() { return 2 - clean; }
2681   };
2682 }
2683
2684 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
2685   var cosφ0,
2686       cosφ1,
2687       sinλ0_λ1 = Math.sin(λ0 - λ1);
2688   return abs(sinλ0_λ1) > ε
2689       ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1)
2690                  - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0))
2691                  / (cosφ0 * cosφ1 * sinλ0_λ1))
2692       : (φ0 + φ1) / 2;
2693 }
2694
2695 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
2696   var φ;
2697   if (from == null) {
2698     φ = direction * halfπ;
2699     listener.point(-π,  φ);
2700     listener.point( 0,  φ);
2701     listener.point( π,  φ);
2702     listener.point( π,  0);
2703     listener.point( π, -φ);
2704     listener.point( 0, -φ);
2705     listener.point(-π, -φ);
2706     listener.point(-π,  0);
2707     listener.point(-π,  φ);
2708   } else if (abs(from[0] - to[0]) > ε) {
2709     var s = from[0] < to[0] ? π : -π;
2710     φ = direction * s / 2;
2711     listener.point(-s, φ);
2712     listener.point( 0, φ);
2713     listener.point( s, φ);
2714   } else {
2715     listener.point(to[0], to[1]);
2716   }
2717 }
2718 // TODO
2719 // cross and scale return new vectors,
2720 // whereas add and normalize operate in-place
2721
2722 function d3_geo_cartesian(spherical) {
2723   var λ = spherical[0],
2724       φ = spherical[1],
2725       cosφ = Math.cos(φ);
2726   return [
2727     cosφ * Math.cos(λ),
2728     cosφ * Math.sin(λ),
2729     Math.sin(φ)
2730   ];
2731 }
2732
2733 function d3_geo_cartesianDot(a, b) {
2734   return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2735 }
2736
2737 function d3_geo_cartesianCross(a, b) {
2738   return [
2739     a[1] * b[2] - a[2] * b[1],
2740     a[2] * b[0] - a[0] * b[2],
2741     a[0] * b[1] - a[1] * b[0]
2742   ];
2743 }
2744
2745 function d3_geo_cartesianAdd(a, b) {
2746   a[0] += b[0];
2747   a[1] += b[1];
2748   a[2] += b[2];
2749 }
2750
2751 function d3_geo_cartesianScale(vector, k) {
2752   return [
2753     vector[0] * k,
2754     vector[1] * k,
2755     vector[2] * k
2756   ];
2757 }
2758
2759 function d3_geo_cartesianNormalize(d) {
2760   var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
2761   d[0] /= l;
2762   d[1] /= l;
2763   d[2] /= l;
2764 }
2765 function d3_geo_compose(a, b) {
2766
2767   function compose(x, y) {
2768     return x = a(x, y), b(x[0], x[1]);
2769   }
2770
2771   if (a.invert && b.invert) compose.invert = function(x, y) {
2772     return x = b.invert(x, y), x && a.invert(x[0], x[1]);
2773   };
2774
2775   return compose;
2776 }
2777
2778 function d3_geo_equirectangular(λ, φ) {
2779   return [λ, φ];
2780 }
2781
2782 (d3.geo.equirectangular = function() {
2783   return d3_geo_projection(d3_geo_equirectangular);
2784 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
2785
2786 d3.geo.rotation = function(rotate) {
2787   rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
2788
2789   function forward(coordinates) {
2790     coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2791     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2792   }
2793
2794   forward.invert = function(coordinates) {
2795     coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2796     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2797   };
2798
2799   return forward;
2800 };
2801
2802 function d3_geo_identityRotation(λ, φ) {
2803   return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2804 }
2805
2806 d3_geo_identityRotation.invert = d3_geo_equirectangular;
2807
2808 // Note: |δλ| must be < 2π
2809 function d3_geo_rotation(δλ, δφ, δγ) {
2810   return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ))
2811     : d3_geo_rotationλ(δλ))
2812     : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ)
2813     : d3_geo_identityRotation);
2814 }
2815
2816 function d3_geo_forwardRotationλ(δλ) {
2817   return function(λ, φ) {
2818     return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2819   };
2820 }
2821
2822 function d3_geo_rotationλ(δλ) {
2823   var rotation = d3_geo_forwardRotationλ(δλ);
2824   rotation.invert = d3_geo_forwardRotationλ(-δλ);
2825   return rotation;
2826 }
2827
2828 function d3_geo_rotationφγ(δφ, δγ) {
2829   var cosδφ = Math.cos(δφ),
2830       sinδφ = Math.sin(δφ),
2831       cosδγ = Math.cos(δγ),
2832       sinδγ = Math.sin(δγ);
2833
2834   function rotation(λ, φ) {
2835     var cosφ = Math.cos(φ),
2836         x = Math.cos(λ) * cosφ,
2837         y = Math.sin(λ) * cosφ,
2838         z = Math.sin(φ),
2839         k = z * cosδφ + x * sinδφ;
2840     return [
2841       Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
2842       d3_asin(k * cosδγ + y * sinδγ)
2843     ];
2844   }
2845
2846   rotation.invert = function(λ, φ) {
2847     var cosφ = Math.cos(φ),
2848         x = Math.cos(λ) * cosφ,
2849         y = Math.sin(λ) * cosφ,
2850         z = Math.sin(φ),
2851         k = z * cosδγ - y * sinδγ;
2852     return [
2853       Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
2854       d3_asin(k * cosδφ - x * sinδφ)
2855     ];
2856   };
2857
2858   return rotation;
2859 }
2860
2861 d3.geo.circle = function() {
2862   var origin = [0, 0],
2863       angle,
2864       precision = 6,
2865       interpolate;
2866
2867   function circle() {
2868     var center = typeof origin === "function" ? origin.apply(this, arguments) : origin,
2869         rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert,
2870         ring = [];
2871
2872     interpolate(null, null, 1, {
2873       point: function(x, y) {
2874         ring.push(x = rotate(x, y));
2875         x[0] *= d3_degrees, x[1] *= d3_degrees;
2876       }
2877     });
2878
2879     return {type: "Polygon", coordinates: [ring]};
2880   }
2881
2882   circle.origin = function(x) {
2883     if (!arguments.length) return origin;
2884     origin = x;
2885     return circle;
2886   };
2887
2888   circle.angle = function(x) {
2889     if (!arguments.length) return angle;
2890     interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
2891     return circle;
2892   };
2893
2894   circle.precision = function(_) {
2895     if (!arguments.length) return precision;
2896     interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
2897     return circle;
2898   };
2899
2900   return circle.angle(90);
2901 };
2902
2903 // Interpolates along a circle centered at [0°, 0°], with a given radius and
2904 // precision.
2905 function d3_geo_circleInterpolate(radius, precision) {
2906   var cr = Math.cos(radius),
2907       sr = Math.sin(radius);
2908   return function(from, to, direction, listener) {
2909     var step = direction * precision;
2910     if (from != null) {
2911       from = d3_geo_circleAngle(cr, from);
2912       to = d3_geo_circleAngle(cr, to);
2913       if (direction > 0 ? from < to: from > to) from += direction * τ;
2914     } else {
2915       from = radius + direction * τ;
2916       to = radius - .5 * step;
2917     }
2918     for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
2919       listener.point((point = d3_geo_spherical([
2920         cr,
2921         -sr * Math.cos(t),
2922         -sr * Math.sin(t)
2923       ]))[0], point[1]);
2924     }
2925   };
2926 }
2927
2928 // Signed angle of a cartesian point relative to [cr, 0, 0].
2929 function d3_geo_circleAngle(cr, point) {
2930   var a = d3_geo_cartesian(point);
2931   a[0] -= cr;
2932   d3_geo_cartesianNormalize(a);
2933   var angle = d3_acos(-a[1]);
2934   return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
2935 }
2936 // Adds floating point numbers with twice the normal precision.
2937 // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
2938 // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
2939 // 305–363 (1997).
2940 // Code adapted from GeographicLib by Charles F. F. Karney,
2941 // http://geographiclib.sourceforge.net/
2942 // See lib/geographiclib/LICENSE for details.
2943
2944 function d3_adder() {}
2945
2946 d3_adder.prototype = {
2947   s: 0, // rounded value
2948   t: 0, // exact error
2949   add: function(y) {
2950     d3_adderSum(y, this.t, d3_adderTemp);
2951     d3_adderSum(d3_adderTemp.s, this.s, this);
2952     if (this.s) this.t += d3_adderTemp.t;
2953     else this.s = d3_adderTemp.t;
2954   },
2955   reset: function() {
2956     this.s = this.t = 0;
2957   },
2958   valueOf: function() {
2959     return this.s;
2960   }
2961 };
2962
2963 var d3_adderTemp = new d3_adder;
2964
2965 function d3_adderSum(a, b, o) {
2966   var x = o.s = a + b, // a + b
2967       bv = x - a, av = x - bv; // b_virtual & a_virtual
2968   o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff
2969 }
2970
2971 d3.geo.area = function(object) {
2972   d3_geo_areaSum = 0;
2973   d3.geo.stream(object, d3_geo_area);
2974   return d3_geo_areaSum;
2975 };
2976
2977 var d3_geo_areaSum,
2978     d3_geo_areaRingSum = new d3_adder;
2979
2980 var d3_geo_area = {
2981   sphere: function() { d3_geo_areaSum += 4 * π; },
2982   point: d3_noop,
2983   lineStart: d3_noop,
2984   lineEnd: d3_noop,
2985
2986   // Only count area for polygon rings.
2987   polygonStart: function() {
2988     d3_geo_areaRingSum.reset();
2989     d3_geo_area.lineStart = d3_geo_areaRingStart;
2990   },
2991   polygonEnd: function() {
2992     var area = 2 * d3_geo_areaRingSum;
2993     d3_geo_areaSum += area < 0 ? 4 * π + area : area;
2994     d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
2995   }
2996 };
2997
2998 function d3_geo_areaRingStart() {
2999   var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
3000
3001   // For the first point, …
3002   d3_geo_area.point = function(λ, φ) {
3003     d3_geo_area.point = nextPoint;
3004     λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ);
3005   };
3006
3007   // For subsequent points, …
3008   function nextPoint(λ, φ) {
3009     λ *= d3_radians;
3010     φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole
3011
3012     // Spherical excess E for a spherical triangle with vertices: south pole,
3013     // previous point, current point.  Uses a formula derived from Cagnoli’s
3014     // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
3015     var dλ = λ - λ0,
3016         sdλ = dλ >= 0 ? 1 : -1,
3017         adλ = sdλ * dλ,
3018         cosφ = Math.cos(φ),
3019         sinφ = Math.sin(φ),
3020         k = sinφ0 * sinφ,
3021         u = cosφ0 * cosφ + k * Math.cos(adλ),
3022         v = k * sdλ * Math.sin(adλ);
3023     d3_geo_areaRingSum.add(Math.atan2(v, u));
3024
3025     // Advance the previous points.
3026     λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3027   }
3028
3029   // For the last point, return to the start.
3030   d3_geo_area.lineEnd = function() {
3031     nextPoint(λ00, φ00);
3032   };
3033 }
3034
3035 function d3_geo_pointInPolygon(point, polygon) {
3036   var meridian = point[0],
3037       parallel = point[1],
3038       meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
3039       polarAngle = 0,
3040       winding = 0;
3041   d3_geo_areaRingSum.reset();
3042
3043   for (var i = 0, n = polygon.length; i < n; ++i) {
3044     var ring = polygon[i],
3045         m = ring.length;
3046     if (!m) continue;
3047     var point0 = ring[0],
3048         λ0 = point0[0],
3049         φ0 = point0[1] / 2 + π / 4,
3050         sinφ0 = Math.sin(φ0),
3051         cosφ0 = Math.cos(φ0),
3052         j = 1;
3053
3054     while (true) {
3055       if (j === m) j = 0;
3056       point = ring[j];
3057       var λ = point[0],
3058           φ = point[1] / 2 + π / 4,
3059           sinφ = Math.sin(φ),
3060           cosφ = Math.cos(φ),
3061           dλ = λ - λ0,
3062           sdλ = dλ >= 0 ? 1 : -1,
3063           adλ = sdλ * dλ,
3064           antimeridian = adλ > π,
3065           k = sinφ0 * sinφ;
3066       d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
3067
3068       polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
3069
3070       // Are the longitudes either side of the point's meridian, and are the
3071       // latitudes smaller than the parallel?
3072       if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
3073         var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
3074         d3_geo_cartesianNormalize(arc);
3075         var intersection = d3_geo_cartesianCross(meridianNormal, arc);
3076         d3_geo_cartesianNormalize(intersection);
3077         var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
3078         if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
3079           winding += antimeridian ^ dλ >= 0 ? 1 : -1;
3080         }
3081       }
3082       if (!j++) break;
3083       λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
3084     }
3085   }
3086
3087   // First, determine whether the South pole is inside or outside:
3088   //
3089   // It is inside if:
3090   // * the polygon winds around it in a clockwise direction.
3091   // * the polygon does not (cumulatively) wind around it, but has a negative
3092   //   (counter-clockwise) area.
3093   //
3094   // Second, count the (signed) number of times a segment crosses a meridian
3095   // from the point to the South pole.  If it is zero, then the point is the
3096   // same side as the South pole.
3097
3098   return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
3099 }
3100
3101 // Clip features against a small circle centered at [0°, 0°].
3102 function d3_geo_clipCircle(radius) {
3103   var cr = Math.cos(radius),
3104       smallRadius = cr > 0,
3105       notHemisphere = abs(cr) > ε, // TODO optimise for this common case
3106       interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
3107
3108   return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]);
3109
3110   function visible(λ, φ) {
3111     return Math.cos(λ) * Math.cos(φ) > cr;
3112   }
3113
3114   // Takes a line and cuts into visible segments. Return values used for
3115   // polygon clipping:
3116   //   0: there were intersections or the line was empty.
3117   //   1: no intersections.
3118   //   2: there were intersections, and the first and last segments should be
3119   //      rejoined.
3120   function clipLine(listener) {
3121     var point0, // previous point
3122         c0, // code for previous point
3123         v0, // visibility of previous point
3124         v00, // visibility of first point
3125         clean; // no intersections
3126     return {
3127       lineStart: function() {
3128         v00 = v0 = false;
3129         clean = 1;
3130       },
3131       point: function(λ, φ) {
3132         var point1 = [λ, φ],
3133             point2,
3134             v = visible(λ, φ),
3135             c = smallRadius
3136               ? v ? 0 : code(λ, φ)
3137               : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
3138         if (!point0 && (v00 = v0 = v)) listener.lineStart();
3139         // Handle degeneracies.
3140         // TODO ignore if not clipping polygons.
3141         if (v !== v0) {
3142           point2 = intersect(point0, point1);
3143           if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3144             point1[0] += ε;
3145             point1[1] += ε;
3146             v = visible(point1[0], point1[1]);
3147           }
3148         }
3149         if (v !== v0) {
3150           clean = 0;
3151           if (v) {
3152             // outside going in
3153             listener.lineStart();
3154             point2 = intersect(point1, point0);
3155             listener.point(point2[0], point2[1]);
3156           } else {
3157             // inside going out
3158             point2 = intersect(point0, point1);
3159             listener.point(point2[0], point2[1]);
3160             listener.lineEnd();
3161           }
3162           point0 = point2;
3163         } else if (notHemisphere && point0 && smallRadius ^ v) {
3164           var t;
3165           // If the codes for two points are different, or are both zero,
3166           // and there this segment intersects with the small circle.
3167           if (!(c & c0) && (t = intersect(point1, point0, true))) {
3168             clean = 0;
3169             if (smallRadius) {
3170               listener.lineStart();
3171               listener.point(t[0][0], t[0][1]);
3172               listener.point(t[1][0], t[1][1]);
3173               listener.lineEnd();
3174             } else {
3175               listener.point(t[1][0], t[1][1]);
3176               listener.lineEnd();
3177               listener.lineStart();
3178               listener.point(t[0][0], t[0][1]);
3179             }
3180           }
3181         }
3182         if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3183           listener.point(point1[0], point1[1]);
3184         }
3185         point0 = point1, v0 = v, c0 = c;
3186       },
3187       lineEnd: function() {
3188         if (v0) listener.lineEnd();
3189         point0 = null;
3190       },
3191       // Rejoin first and last segments if there were intersections and the first
3192       // and last points were visible.
3193       clean: function() { return clean | ((v00 && v0) << 1); }
3194     };
3195   }
3196
3197   // Intersects the great circle between a and b with the clip circle.
3198   function intersect(a, b, two) {
3199     var pa = d3_geo_cartesian(a),
3200         pb = d3_geo_cartesian(b);
3201
3202     // We have two planes, n1.p = d1 and n2.p = d2.
3203     // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
3204     var n1 = [1, 0, 0], // normal
3205         n2 = d3_geo_cartesianCross(pa, pb),
3206         n2n2 = d3_geo_cartesianDot(n2, n2),
3207         n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2),
3208         determinant = n2n2 - n1n2 * n1n2;
3209
3210     // Two polar points.
3211     if (!determinant) return !two && a;
3212
3213     var c1 =  cr * n2n2 / determinant,
3214         c2 = -cr * n1n2 / determinant,
3215         n1xn2 = d3_geo_cartesianCross(n1, n2),
3216         A = d3_geo_cartesianScale(n1, c1),
3217         B = d3_geo_cartesianScale(n2, c2);
3218     d3_geo_cartesianAdd(A, B);
3219
3220     // Solve |p(t)|^2 = 1.
3221     var u = n1xn2,
3222         w = d3_geo_cartesianDot(A, u),
3223         uu = d3_geo_cartesianDot(u, u),
3224         t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
3225
3226     if (t2 < 0) return;
3227
3228     var t = Math.sqrt(t2),
3229         q = d3_geo_cartesianScale(u, (-w - t) / uu);
3230     d3_geo_cartesianAdd(q, A);
3231     q = d3_geo_spherical(q);
3232     if (!two) return q;
3233
3234     // Two intersection points.
3235     var λ0 = a[0],
3236         λ1 = b[0],
3237         φ0 = a[1],
3238         φ1 = b[1],
3239         z;
3240     if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3241     var δλ = λ1 - λ0,
3242         polar = abs(δλ - π) < ε,
3243         meridian = polar || δλ < ε;
3244
3245     if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3246
3247     // Check that the first point is between a and b.
3248     if (meridian
3249         ? polar
3250           ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1)
3251           : φ0 <= q[1] && q[1] <= φ1
3252         : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
3253       var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
3254       d3_geo_cartesianAdd(q1, A);
3255       return [q, d3_geo_spherical(q1)];
3256     }
3257   }
3258
3259   // Generates a 4-bit vector representing the location of a point relative to
3260   // the small circle's bounding box.
3261   function code(λ, φ) {
3262     var r = smallRadius ? radius : π - radius,
3263         code = 0;
3264     if (λ < -r) code |= 1; // left
3265     else if (λ > r) code |= 2; // right
3266     if (φ < -r) code |= 4; // below
3267     else if (φ > r) code |= 8; // above
3268     return code;
3269   }
3270 }
3271
3272 // Liang–Barsky line clipping.
3273 function d3_geom_clipLine(x0, y0, x1, y1) {
3274   return function(line) {
3275     var a = line.a,
3276         b = line.b,
3277         ax = a.x,
3278         ay = a.y,
3279         bx = b.x,
3280         by = b.y,
3281         t0 = 0,
3282         t1 = 1,
3283         dx = bx - ax,
3284         dy = by - ay,
3285         r;
3286
3287     r = x0 - ax;
3288     if (!dx && r > 0) return;
3289     r /= dx;
3290     if (dx < 0) {
3291       if (r < t0) return;
3292       if (r < t1) t1 = r;
3293     } else if (dx > 0) {
3294       if (r > t1) return;
3295       if (r > t0) t0 = r;
3296     }
3297
3298     r = x1 - ax;
3299     if (!dx && r < 0) return;
3300     r /= dx;
3301     if (dx < 0) {
3302       if (r > t1) return;
3303       if (r > t0) t0 = r;
3304     } else if (dx > 0) {
3305       if (r < t0) return;
3306       if (r < t1) t1 = r;
3307     }
3308
3309     r = y0 - ay;
3310     if (!dy && r > 0) return;
3311     r /= dy;
3312     if (dy < 0) {
3313       if (r < t0) return;
3314       if (r < t1) t1 = r;
3315     } else if (dy > 0) {
3316       if (r > t1) return;
3317       if (r > t0) t0 = r;
3318     }
3319
3320     r = y1 - ay;
3321     if (!dy && r < 0) return;
3322     r /= dy;
3323     if (dy < 0) {
3324       if (r > t1) return;
3325       if (r > t0) t0 = r;
3326     } else if (dy > 0) {
3327       if (r < t0) return;
3328       if (r < t1) t1 = r;
3329     }
3330
3331     if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy};
3332     if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy};
3333     return line;
3334   };
3335 }
3336
3337 var d3_geo_clipExtentMAX = 1e9;
3338
3339 d3.geo.clipExtent = function() {
3340   var x0, y0, x1, y1,
3341       stream,
3342       clip,
3343       clipExtent = {
3344         stream: function(output) {
3345           if (stream) stream.valid = false;
3346           stream = clip(output);
3347           stream.valid = true; // allow caching by d3.geo.path
3348           return stream;
3349         },
3350         extent: function(_) {
3351           if (!arguments.length) return [[x0, y0], [x1, y1]];
3352           clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
3353           if (stream) stream.valid = false, stream = null;
3354           return clipExtent;
3355         }
3356       };
3357   return clipExtent.extent([[0, 0], [960, 500]]);
3358 };
3359
3360 function d3_geo_clipExtent(x0, y0, x1, y1) {
3361   return function(listener) {
3362     var listener_ = listener,
3363         bufferListener = d3_geo_clipBufferListener(),
3364         clipLine = d3_geom_clipLine(x0, y0, x1, y1),
3365         segments,
3366         polygon,
3367         ring;
3368
3369     var clip = {
3370       point: point,
3371       lineStart: lineStart,
3372       lineEnd: lineEnd,
3373       polygonStart: function() {
3374         listener = bufferListener;
3375         segments = [];
3376         polygon = [];
3377         clean = true;
3378       },
3379       polygonEnd: function() {
3380         listener = listener_;
3381         segments = d3.merge(segments);
3382         var clipStartInside = insidePolygon([x0, y1]),
3383             inside = clean && clipStartInside,
3384             visible = segments.length;
3385         if (inside || visible) {
3386           listener.polygonStart();
3387           if (inside) {
3388             listener.lineStart();
3389             interpolate(null, null, 1, listener);
3390             listener.lineEnd();
3391           }
3392           if (visible) {
3393             d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
3394           }
3395           listener.polygonEnd();
3396         }
3397         segments = polygon = ring = null;
3398       }
3399     };
3400
3401     function insidePolygon(p) {
3402       var wn = 0, // the winding number counter
3403           n = polygon.length,
3404           y = p[1];
3405
3406       for (var i = 0; i < n; ++i) {
3407         for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
3408           b = v[j];
3409           if (a[1] <= y) {
3410             if (b[1] >  y && d3_cross2d(a, b, p) > 0) ++wn;
3411           } else {
3412             if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
3413           }
3414           a = b;
3415         }
3416       }
3417       return wn !== 0;
3418     }
3419
3420     function interpolate(from, to, direction, listener) {
3421       var a = 0, a1 = 0;
3422       if (from == null ||
3423           (a = corner(from, direction)) !== (a1 = corner(to, direction)) ||
3424           comparePoints(from, to) < 0 ^ direction > 0) {
3425         do {
3426           listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
3427         } while ((a = (a + direction + 4) % 4) !== a1);
3428       } else {
3429         listener.point(to[0], to[1]);
3430       }
3431     }
3432
3433     function pointVisible(x, y) {
3434       return x0 <= x && x <= x1 && y0 <= y && y <= y1;
3435     }
3436
3437     function point(x, y) {
3438       if (pointVisible(x, y)) listener.point(x, y);
3439     }
3440
3441     var x__, y__, v__, // first point
3442         x_, y_, v_, // previous point
3443         first,
3444         clean;
3445
3446     function lineStart() {
3447       clip.point = linePoint;
3448       if (polygon) polygon.push(ring = []);
3449       first = true;
3450       v_ = false;
3451       x_ = y_ = NaN;
3452     }
3453
3454     function lineEnd() {
3455       // TODO rather than special-case polygons, simply handle them separately.
3456       // Ideally, coincident intersection points should be jittered to avoid
3457       // clipping issues.
3458       if (segments) {
3459         linePoint(x__, y__);
3460         if (v__ && v_) bufferListener.rejoin();
3461         segments.push(bufferListener.buffer());
3462       }
3463       clip.point = point;
3464       if (v_) listener.lineEnd();
3465     }
3466
3467     function linePoint(x, y) {
3468       x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
3469       y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
3470       var v = pointVisible(x, y);
3471       if (polygon) ring.push([x, y]);
3472       if (first) {
3473         x__ = x, y__ = y, v__ = v;
3474         first = false;
3475         if (v) {
3476           listener.lineStart();
3477           listener.point(x, y);
3478         }
3479       } else {
3480         if (v && v_) listener.point(x, y);
3481         else {
3482           var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
3483           if (clipLine(l)) {
3484             if (!v_) {
3485               listener.lineStart();
3486               listener.point(l.a.x, l.a.y);
3487             }
3488             listener.point(l.b.x, l.b.y);
3489             if (!v) listener.lineEnd();
3490             clean = false;
3491           } else if (v) {
3492             listener.lineStart();
3493             listener.point(x, y);
3494             clean = false;
3495           }
3496         }
3497       }
3498       x_ = x, y_ = y, v_ = v;
3499     }
3500
3501     return clip;
3502   };
3503
3504   function corner(p, direction) {
3505     return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
3506         : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
3507         : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
3508         : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε
3509   }
3510
3511   function compare(a, b) {
3512     return comparePoints(a.x, b.x);
3513   }
3514
3515   function comparePoints(a, b) {
3516     var ca = corner(a, 1),
3517         cb = corner(b, 1);
3518     return ca !== cb ? ca - cb
3519         : ca === 0 ? b[1] - a[1]
3520         : ca === 1 ? a[0] - b[0]
3521         : ca === 2 ? a[1] - b[1]
3522         : b[0] - a[0];
3523   }
3524 }
3525
3526 function d3_geo_conic(projectAt) {
3527   var φ0 = 0,
3528       φ1 = π / 3,
3529       m = d3_geo_projectionMutator(projectAt),
3530       p = m(φ0, φ1);
3531
3532   p.parallels = function(_) {
3533     if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180];
3534     return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
3535   };
3536
3537   return p;
3538 }
3539
3540 function d3_geo_conicEqualArea(φ0, φ1) {
3541   var sinφ0 = Math.sin(φ0),
3542       n = (sinφ0 + Math.sin(φ1)) / 2,
3543       C = 1 + sinφ0 * (2 * n - sinφ0),
3544       ρ0 = Math.sqrt(C) / n;
3545
3546   function forward(λ, φ) {
3547     var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
3548     return [
3549       ρ * Math.sin(λ *= n),
3550       ρ0 - ρ * Math.cos(λ)
3551     ];
3552   }
3553
3554   forward.invert = function(x, y) {
3555     var ρ0_y = ρ0 - y;
3556     return [
3557       Math.atan2(x, ρ0_y) / n,
3558       d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n))
3559     ];
3560   };
3561
3562   return forward;
3563 }
3564
3565 (d3.geo.conicEqualArea = function() {
3566   return d3_geo_conic(d3_geo_conicEqualArea);
3567 }).raw = d3_geo_conicEqualArea;
3568
3569 // ESRI:102003
3570 d3.geo.albers = function() {
3571   return d3.geo.conicEqualArea()
3572       .rotate([96, 0])
3573       .center([-.6, 38.7])
3574       .parallels([29.5, 45.5])
3575       .scale(1070);
3576 };
3577
3578 // A composite projection for the United States, configured by default for
3579 // 960×500. Also works quite well at 960×600 with scale 1285. The set of
3580 // standard parallels for each region comes from USGS, which is published here:
3581 // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
3582 d3.geo.albersUsa = function() {
3583   var lower48 = d3.geo.albers();
3584
3585   // EPSG:3338
3586   var alaska = d3.geo.conicEqualArea()
3587       .rotate([154, 0])
3588       .center([-2, 58.5])
3589       .parallels([55, 65]);
3590
3591   // ESRI:102007
3592   var hawaii = d3.geo.conicEqualArea()
3593       .rotate([157, 0])
3594       .center([-3, 19.9])
3595       .parallels([8, 18]);
3596
3597   var point,
3598       pointStream = {point: function(x, y) { point = [x, y]; }},
3599       lower48Point,
3600       alaskaPoint,
3601       hawaiiPoint;
3602
3603   function albersUsa(coordinates) {
3604     var x = coordinates[0], y = coordinates[1];
3605     point = null;
3606     (lower48Point(x, y), point)
3607         || (alaskaPoint(x, y), point)
3608         || hawaiiPoint(x, y);
3609     return point;
3610   }
3611
3612   albersUsa.invert = function(coordinates) {
3613     var k = lower48.scale(),
3614         t = lower48.translate(),
3615         x = (coordinates[0] - t[0]) / k,
3616         y = (coordinates[1] - t[1]) / k;
3617     return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska
3618         : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii
3619         : lower48).invert(coordinates);
3620   };
3621
3622   // A naïve multi-projection stream.
3623   // The projections must have mutually exclusive clip regions on the sphere,
3624   // as this will avoid emitting interleaving lines and polygons.
3625   albersUsa.stream = function(stream) {
3626     var lower48Stream = lower48.stream(stream),
3627         alaskaStream = alaska.stream(stream),
3628         hawaiiStream = hawaii.stream(stream);
3629     return {
3630       point: function(x, y) {
3631         lower48Stream.point(x, y);
3632         alaskaStream.point(x, y);
3633         hawaiiStream.point(x, y);
3634       },
3635       sphere: function() {
3636         lower48Stream.sphere();
3637         alaskaStream.sphere();
3638         hawaiiStream.sphere();
3639       },
3640       lineStart: function() {
3641         lower48Stream.lineStart();
3642         alaskaStream.lineStart();
3643         hawaiiStream.lineStart();
3644       },
3645       lineEnd: function() {
3646         lower48Stream.lineEnd();
3647         alaskaStream.lineEnd();
3648         hawaiiStream.lineEnd();
3649       },
3650       polygonStart: function() {
3651         lower48Stream.polygonStart();
3652         alaskaStream.polygonStart();
3653         hawaiiStream.polygonStart();
3654       },
3655       polygonEnd: function() {
3656         lower48Stream.polygonEnd();
3657         alaskaStream.polygonEnd();
3658         hawaiiStream.polygonEnd();
3659       }
3660     };
3661   };
3662
3663   albersUsa.precision = function(_) {
3664     if (!arguments.length) return lower48.precision();
3665     lower48.precision(_);
3666     alaska.precision(_);
3667     hawaii.precision(_);
3668     return albersUsa;
3669   };
3670
3671   albersUsa.scale = function(_) {
3672     if (!arguments.length) return lower48.scale();
3673     lower48.scale(_);
3674     alaska.scale(_ * .35);
3675     hawaii.scale(_);
3676     return albersUsa.translate(lower48.translate());
3677   };
3678
3679   albersUsa.translate = function(_) {
3680     if (!arguments.length) return lower48.translate();
3681     var k = lower48.scale(), x = +_[0], y = +_[1];
3682
3683     lower48Point = lower48
3684         .translate(_)
3685         .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
3686         .stream(pointStream).point;
3687
3688     alaskaPoint = alaska
3689         .translate([x - .307 * k, y + .201 * k])
3690         .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]])
3691         .stream(pointStream).point;
3692
3693     hawaiiPoint = hawaii
3694         .translate([x - .205 * k, y + .212 * k])
3695         .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]])
3696         .stream(pointStream).point;
3697
3698     return albersUsa;
3699   };
3700
3701   return albersUsa.scale(1070);
3702 };
3703
3704 d3.geo.bounds = (function() {
3705   var λ0, φ0, λ1, φ1, // bounds
3706       λ_, // previous λ-coordinate
3707       λ__, φ__, // first point
3708       p0, // previous 3D point
3709       dλSum,
3710       ranges,
3711       range;
3712
3713   var bound = {
3714     point: point,
3715     lineStart: lineStart,
3716     lineEnd: lineEnd,
3717
3718     polygonStart: function() {
3719       bound.point = ringPoint;
3720       bound.lineStart = ringStart;
3721       bound.lineEnd = ringEnd;
3722       dλSum = 0;
3723       d3_geo_area.polygonStart();
3724     },
3725     polygonEnd: function() {
3726       d3_geo_area.polygonEnd();
3727       bound.point = point;
3728       bound.lineStart = lineStart;
3729       bound.lineEnd = lineEnd;
3730       if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90);
3731       else if (dλSum > ε) φ1 = 90;
3732       else if (dλSum < -ε) φ0 = -90;
3733       range[0] = λ0, range[1] = λ1;
3734     }
3735   };
3736
3737   function point(λ, φ) {
3738     ranges.push(range = [λ0 = λ, λ1 = λ]);
3739     if (φ < φ0) φ0 = φ;
3740     if (φ > φ1) φ1 = φ;
3741   }
3742
3743   function linePoint(λ, φ) {
3744     var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]);
3745     if (p0) {
3746       var normal = d3_geo_cartesianCross(p0, p),
3747           equatorial = [normal[1], -normal[0], 0],
3748           inflection = d3_geo_cartesianCross(equatorial, normal);
3749       d3_geo_cartesianNormalize(inflection);
3750       inflection = d3_geo_spherical(inflection);
3751       var dλ = λ - λ_,
3752           s = dλ > 0 ? 1 : -1,
3753           λi = inflection[0] * d3_degrees * s,
3754           antimeridian = abs(dλ) > 180;
3755       if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3756         var φi = inflection[1] * d3_degrees;
3757         if (φi > φ1) φ1 = φi;
3758       } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3759         var φi = -inflection[1] * d3_degrees;
3760         if (φi < φ0) φ0 = φi;
3761       } else {
3762         if (φ < φ0) φ0 = φ;
3763         if (φ > φ1) φ1 = φ;
3764       }
3765       if (antimeridian) {
3766         if (λ < λ_) {
3767           if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3768         } else {
3769           if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3770         }
3771       } else {
3772         if (λ1 >= λ0) {
3773           if (λ < λ0) λ0 = λ;
3774           if (λ > λ1) λ1 = λ;
3775         } else {
3776           if (λ > λ_) {
3777             if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3778           } else {
3779             if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3780           }
3781         }
3782       }
3783     } else {
3784       point(λ, φ);
3785     }
3786     p0 = p, λ_ = λ;
3787   }
3788
3789   function lineStart() { bound.point = linePoint; }
3790   function lineEnd() {
3791     range[0] = λ0, range[1] = λ1;
3792     bound.point = point;
3793     p0 = null;
3794   }
3795
3796   function ringPoint(λ, φ) {
3797     if (p0) {
3798       var dλ = λ - λ_;
3799       dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3800     } else λ__ = λ, φ__ = φ;
3801     d3_geo_area.point(λ, φ);
3802     linePoint(λ, φ);
3803   }
3804
3805   function ringStart() {
3806     d3_geo_area.lineStart();
3807   }
3808
3809   function ringEnd() {
3810     ringPoint(λ__, φ__);
3811     d3_geo_area.lineEnd();
3812     if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
3813     range[0] = λ0, range[1] = λ1;
3814     p0 = null;
3815   }
3816
3817   // Finds the left-right distance between two longitudes.
3818   // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want
3819   // the distance between ±180° to be 360°.
3820   function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; }
3821
3822   function compareRanges(a, b) { return a[0] - b[0]; }
3823
3824   function withinRange(x, range) {
3825     return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3826   }
3827
3828   return function(feature) {
3829     φ1 = λ1 = -(λ0 = φ0 = Infinity);
3830     ranges = [];
3831
3832     d3.geo.stream(feature, bound);
3833
3834     var n = ranges.length;
3835     if (n) {
3836       // First, sort ranges by their minimum longitudes.
3837       ranges.sort(compareRanges);
3838
3839       // Then, merge any ranges that overlap.
3840       for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
3841         b = ranges[i];
3842         if (withinRange(b[0], a) || withinRange(b[1], a)) {
3843           if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
3844           if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
3845         } else {
3846           merged.push(a = b);
3847         }
3848       }
3849
3850       // Finally, find the largest gap between the merged ranges.
3851       // The final bounding box will be the inverse of this gap.
3852       var best = -Infinity, dλ;
3853       for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
3854         b = merged[i];
3855         if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3856       }
3857     }
3858     ranges = range = null;
3859
3860     return λ0 === Infinity || φ0 === Infinity
3861         ? [[NaN, NaN], [NaN, NaN]]
3862         : [[λ0, φ0], [λ1, φ1]];
3863   };
3864 })();
3865
3866 d3.geo.centroid = function(object) {
3867   d3_geo_centroidW0 = d3_geo_centroidW1 =
3868   d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
3869   d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
3870   d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
3871   d3.geo.stream(object, d3_geo_centroid);
3872
3873   var x = d3_geo_centroidX2,
3874       y = d3_geo_centroidY2,
3875       z = d3_geo_centroidZ2,
3876       m = x * x + y * y + z * z;
3877
3878   // If the area-weighted centroid is undefined, fall back to length-weighted centroid.
3879   if (m < ε2) {
3880     x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
3881     // If the feature has zero length, fall back to arithmetic mean of point vectors.
3882     if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
3883     m = x * x + y * y + z * z;
3884     // If the feature still has an undefined centroid, then return.
3885     if (m < ε2) return [NaN, NaN];
3886   }
3887
3888   return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
3889 };
3890
3891 var d3_geo_centroidW0,
3892     d3_geo_centroidW1,
3893     d3_geo_centroidX0,
3894     d3_geo_centroidY0,
3895     d3_geo_centroidZ0,
3896     d3_geo_centroidX1,
3897     d3_geo_centroidY1,
3898     d3_geo_centroidZ1,
3899     d3_geo_centroidX2,
3900     d3_geo_centroidY2,
3901     d3_geo_centroidZ2;
3902
3903 var d3_geo_centroid = {
3904   sphere: d3_noop,
3905   point: d3_geo_centroidPoint,
3906   lineStart: d3_geo_centroidLineStart,
3907   lineEnd: d3_geo_centroidLineEnd,
3908   polygonStart: function() {
3909     d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
3910   },
3911   polygonEnd: function() {
3912     d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3913   }
3914 };
3915
3916 // Arithmetic mean of Cartesian vectors.
3917 function d3_geo_centroidPoint(λ, φ) {
3918   λ *= d3_radians;
3919   var cosφ = Math.cos(φ *= d3_radians);
3920   d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3921 }
3922
3923 function d3_geo_centroidPointXYZ(x, y, z) {
3924   ++d3_geo_centroidW0;
3925   d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
3926   d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
3927   d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
3928 }
3929
3930 function d3_geo_centroidLineStart() {
3931   var x0, y0, z0; // previous point
3932
3933   d3_geo_centroid.point = function(λ, φ) {
3934     λ *= d3_radians;
3935     var cosφ = Math.cos(φ *= d3_radians);
3936     x0 = cosφ * Math.cos(λ);
3937     y0 = cosφ * Math.sin(λ);
3938     z0 = Math.sin(φ);
3939     d3_geo_centroid.point = nextPoint;
3940     d3_geo_centroidPointXYZ(x0, y0, z0);
3941   };
3942
3943   function nextPoint(λ, φ) {
3944     λ *= d3_radians;
3945     var cosφ = Math.cos(φ *= d3_radians),
3946         x = cosφ * Math.cos(λ),
3947         y = cosφ * Math.sin(λ),
3948         z = Math.sin(φ),
3949         w = Math.atan2(
3950           Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
3951           x0 * x + y0 * y + z0 * z);
3952     d3_geo_centroidW1 += w;
3953     d3_geo_centroidX1 += w * (x0 + (x0 = x));
3954     d3_geo_centroidY1 += w * (y0 + (y0 = y));
3955     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
3956     d3_geo_centroidPointXYZ(x0, y0, z0);
3957   }
3958 }
3959
3960 function d3_geo_centroidLineEnd() {
3961   d3_geo_centroid.point = d3_geo_centroidPoint;
3962 }
3963
3964 // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
3965 // J. Applied Mechanics 42, 239 (1975).
3966 function d3_geo_centroidRingStart() {
3967   var λ00, φ00, // first point
3968       x0, y0, z0; // previous point
3969
3970   d3_geo_centroid.point = function(λ, φ) {
3971     λ00 = λ, φ00 = φ;
3972     d3_geo_centroid.point = nextPoint;
3973     λ *= d3_radians;
3974     var cosφ = Math.cos(φ *= d3_radians);
3975     x0 = cosφ * Math.cos(λ);
3976     y0 = cosφ * Math.sin(λ);
3977     z0 = Math.sin(φ);
3978     d3_geo_centroidPointXYZ(x0, y0, z0);
3979   };
3980
3981   d3_geo_centroid.lineEnd = function() {
3982     nextPoint(λ00, φ00);
3983     d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
3984     d3_geo_centroid.point = d3_geo_centroidPoint;
3985   };
3986
3987   function nextPoint(λ, φ) {
3988     λ *= d3_radians;
3989     var cosφ = Math.cos(φ *= d3_radians),
3990         x = cosφ * Math.cos(λ),
3991         y = cosφ * Math.sin(λ),
3992         z = Math.sin(φ),
3993         cx = y0 * z - z0 * y,
3994         cy = z0 * x - x0 * z,
3995         cz = x0 * y - y0 * x,
3996         m = Math.sqrt(cx * cx + cy * cy + cz * cz),
3997         u = x0 * x + y0 * y + z0 * z,
3998         v = m && -d3_acos(u) / m, // area weight
3999         w = Math.atan2(m, u); // line weight
4000     d3_geo_centroidX2 += v * cx;
4001     d3_geo_centroidY2 += v * cy;
4002     d3_geo_centroidZ2 += v * cz;
4003     d3_geo_centroidW1 += w;
4004     d3_geo_centroidX1 += w * (x0 + (x0 = x));
4005     d3_geo_centroidY1 += w * (y0 + (y0 = y));
4006     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
4007     d3_geo_centroidPointXYZ(x0, y0, z0);
4008   }
4009 }
4010
4011 // TODO Unify this code with d3.geom.polygon area?
4012
4013 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4014   point: d3_noop,
4015   lineStart: d3_noop,
4016   lineEnd: d3_noop,
4017
4018   // Only count area for polygon rings.
4019   polygonStart: function() {
4020     d3_geo_pathAreaPolygon = 0;
4021     d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4022   },
4023   polygonEnd: function() {
4024     d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
4025     d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
4026   }
4027 };
4028
4029 function d3_geo_pathAreaRingStart() {
4030   var x00, y00, x0, y0;
4031
4032   // For the first point, …
4033   d3_geo_pathArea.point = function(x, y) {
4034     d3_geo_pathArea.point = nextPoint;
4035     x00 = x0 = x, y00 = y0 = y;
4036   };
4037
4038   // For subsequent points, …
4039   function nextPoint(x, y) {
4040     d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4041     x0 = x, y0 = y;
4042   }
4043
4044   // For the last point, return to the start.
4045   d3_geo_pathArea.lineEnd = function() {
4046     nextPoint(x00, y00);
4047   };
4048 }
4049
4050 var d3_geo_pathBoundsX0,
4051     d3_geo_pathBoundsY0,
4052     d3_geo_pathBoundsX1,
4053     d3_geo_pathBoundsY1;
4054
4055 var d3_geo_pathBounds = {
4056   point: d3_geo_pathBoundsPoint,
4057   lineStart: d3_noop,
4058   lineEnd: d3_noop,
4059   polygonStart: d3_noop,
4060   polygonEnd: d3_noop
4061 };
4062
4063 function d3_geo_pathBoundsPoint(x, y) {
4064   if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
4065   if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
4066   if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
4067   if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
4068 }
4069 function d3_geo_pathBuffer() {
4070   var pointCircle = d3_geo_pathBufferCircle(4.5),
4071       buffer = [];
4072
4073   var stream = {
4074     point: point,
4075
4076     // While inside a line, override point to moveTo then lineTo.
4077     lineStart: function() { stream.point = pointLineStart; },
4078     lineEnd: lineEnd,
4079
4080     // While inside a polygon, override lineEnd to closePath.
4081     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4082     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4083
4084     pointRadius: function(_) {
4085       pointCircle = d3_geo_pathBufferCircle(_);
4086       return stream;
4087     },
4088
4089     result: function() {
4090       if (buffer.length) {
4091         var result = buffer.join("");
4092         buffer = [];
4093         return result;
4094       }
4095     }
4096   };
4097
4098   function point(x, y) {
4099     buffer.push("M", x, ",", y, pointCircle);
4100   }
4101
4102   function pointLineStart(x, y) {
4103     buffer.push("M", x, ",", y);
4104     stream.point = pointLine;
4105   }
4106
4107   function pointLine(x, y) {
4108     buffer.push("L", x, ",", y);
4109   }
4110
4111   function lineEnd() {
4112     stream.point = point;
4113   }
4114
4115   function lineEndPolygon() {
4116     buffer.push("Z");
4117   }
4118
4119   return stream;
4120 }
4121
4122 function d3_geo_pathBufferCircle(radius) {
4123   return "m0," + radius
4124       + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
4125       + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
4126       + "z";
4127 }
4128
4129 // TODO Unify this code with d3.geom.polygon centroid?
4130 // TODO Enforce positive area for exterior, negative area for interior?
4131
4132 var d3_geo_pathCentroid = {
4133   point: d3_geo_pathCentroidPoint,
4134
4135   // For lines, weight by length.
4136   lineStart: d3_geo_pathCentroidLineStart,
4137   lineEnd: d3_geo_pathCentroidLineEnd,
4138
4139   // For polygons, weight by area.
4140   polygonStart: function() {
4141     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4142   },
4143   polygonEnd: function() {
4144     d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4145     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
4146     d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
4147   }
4148 };
4149
4150 function d3_geo_pathCentroidPoint(x, y) {
4151   d3_geo_centroidX0 += x;
4152   d3_geo_centroidY0 += y;
4153   ++d3_geo_centroidZ0;
4154 }
4155
4156 function d3_geo_pathCentroidLineStart() {
4157   var x0, y0;
4158
4159   d3_geo_pathCentroid.point = function(x, y) {
4160     d3_geo_pathCentroid.point = nextPoint;
4161     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4162   };
4163
4164   function nextPoint(x, y) {
4165     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4166     d3_geo_centroidX1 += z * (x0 + x) / 2;
4167     d3_geo_centroidY1 += z * (y0 + y) / 2;
4168     d3_geo_centroidZ1 += z;
4169     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4170   }
4171 }
4172
4173 function d3_geo_pathCentroidLineEnd() {
4174   d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4175 }
4176
4177 function d3_geo_pathCentroidRingStart() {
4178   var x00, y00, x0, y0;
4179
4180   // For the first point, …
4181   d3_geo_pathCentroid.point = function(x, y) {
4182     d3_geo_pathCentroid.point = nextPoint;
4183     d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
4184   };
4185
4186   // For subsequent points, …
4187   function nextPoint(x, y) {
4188     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4189     d3_geo_centroidX1 += z * (x0 + x) / 2;
4190     d3_geo_centroidY1 += z * (y0 + y) / 2;
4191     d3_geo_centroidZ1 += z;
4192
4193     z = y0 * x - x0 * y;
4194     d3_geo_centroidX2 += z * (x0 + x);
4195     d3_geo_centroidY2 += z * (y0 + y);
4196     d3_geo_centroidZ2 += z * 3;
4197     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4198   }
4199
4200   // For the last point, return to the start.
4201   d3_geo_pathCentroid.lineEnd = function() {
4202     nextPoint(x00, y00);
4203   };
4204 }
4205
4206 function d3_geo_pathContext(context) {
4207   var pointRadius = 4.5;
4208
4209   var stream = {
4210     point: point,
4211
4212     // While inside a line, override point to moveTo then lineTo.
4213     lineStart: function() { stream.point = pointLineStart; },
4214     lineEnd: lineEnd,
4215
4216     // While inside a polygon, override lineEnd to closePath.
4217     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4218     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4219
4220     pointRadius: function(_) {
4221       pointRadius = _;
4222       return stream;
4223     },
4224
4225     result: d3_noop
4226   };
4227
4228   function point(x, y) {
4229     context.moveTo(x + pointRadius, y);
4230     context.arc(x, y, pointRadius, 0, τ);
4231   }
4232
4233   function pointLineStart(x, y) {
4234     context.moveTo(x, y);
4235     stream.point = pointLine;
4236   }
4237
4238   function pointLine(x, y) {
4239     context.lineTo(x, y);
4240   }
4241
4242   function lineEnd() {
4243     stream.point = point;
4244   }
4245
4246   function lineEndPolygon() {
4247     context.closePath();
4248   }
4249
4250   return stream;
4251 }
4252
4253 function d3_geo_resample(project) {
4254   var δ2 = .5, // precision, px²
4255       cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance)
4256       maxDepth = 16;
4257
4258   function resample(stream) {
4259     return (maxDepth ? resampleRecursive : resampleNone)(stream);
4260   }
4261
4262   function resampleNone(stream) {
4263     return d3_geo_transformPoint(stream, function(x, y) {
4264       x = project(x, y);
4265       stream.point(x[0], x[1]);
4266     });
4267   }
4268
4269   function resampleRecursive(stream) {
4270     var λ00, φ00, x00, y00, a00, b00, c00, // first point
4271         λ0, x0, y0, a0, b0, c0; // previous point
4272
4273     var resample = {
4274       point: point,
4275       lineStart: lineStart,
4276       lineEnd: lineEnd,
4277       polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
4278       polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
4279     };
4280
4281     function point(x, y) {
4282       x = project(x, y);
4283       stream.point(x[0], x[1]);
4284     }
4285
4286     function lineStart() {
4287       x0 = NaN;
4288       resample.point = linePoint;
4289       stream.lineStart();
4290     }
4291
4292     function linePoint(λ, φ) {
4293       var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ);
4294       resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
4295       stream.point(x0, y0);
4296     }
4297
4298     function lineEnd() {
4299       resample.point = point;
4300       stream.lineEnd();
4301     }
4302
4303     function ringStart() {
4304       lineStart();
4305       resample.point = ringPoint;
4306       resample.lineEnd = ringEnd;
4307     }
4308
4309     function ringPoint(λ, φ) {
4310       linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4311       resample.point = linePoint;
4312     }
4313
4314     function ringEnd() {
4315       resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4316       resample.lineEnd = lineEnd;
4317       lineEnd();
4318     }
4319
4320     return resample;
4321   }
4322
4323   function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4324     var dx = x1 - x0,
4325         dy = y1 - y0,
4326         d2 = dx * dx + dy * dy;
4327     if (d2 > 4 * δ2 && depth--) {
4328       var a = a0 + a1,
4329           b = b0 + b1,
4330           c = c0 + c1,
4331           m = Math.sqrt(a * a + b * b + c * c),
4332           φ2 = Math.asin(c /= m),
4333           λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
4334           p = project(λ2, φ2),
4335           x2 = p[0],
4336           y2 = p[1],
4337           dx2 = x2 - x0,
4338           dy2 = y2 - y0,
4339           dz = dy * dx2 - dx * dy2;
4340       if (dz * dz / d2 > δ2 // perpendicular projected distance
4341           || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
4342           || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
4343         resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
4344         stream.point(x2, y2);
4345         resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
4346       }
4347     }
4348   }
4349
4350   resample.precision = function(_) {
4351     if (!arguments.length) return Math.sqrt(δ2);
4352     maxDepth = (δ2 = _ * _) > 0 && 16;
4353     return resample;
4354   };
4355
4356   return resample;
4357 }
4358
4359 d3.geo.path = function() {
4360   var pointRadius = 4.5,
4361       projection,
4362       context,
4363       projectStream,
4364       contextStream,
4365       cacheStream;
4366
4367   function path(object) {
4368     if (object) {
4369       if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
4370       if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
4371       d3.geo.stream(object, cacheStream);
4372     }
4373     return contextStream.result();
4374   }
4375
4376   path.area = function(object) {
4377     d3_geo_pathAreaSum = 0;
4378     d3.geo.stream(object, projectStream(d3_geo_pathArea));
4379     return d3_geo_pathAreaSum;
4380   };
4381
4382   path.centroid = function(object) {
4383     d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
4384     d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
4385     d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
4386     d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
4387     return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2]
4388         : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1]
4389         : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0]
4390         : [NaN, NaN];
4391   };
4392
4393   path.bounds = function(object) {
4394     d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
4395     d3.geo.stream(object, projectStream(d3_geo_pathBounds));
4396     return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]];
4397   };
4398
4399   path.projection = function(_) {
4400     if (!arguments.length) return projection;
4401     projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
4402     return reset();
4403   };
4404
4405   path.context = function(_) {
4406     if (!arguments.length) return context;
4407     contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_);
4408     if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
4409     return reset();
4410   };
4411
4412   path.pointRadius = function(_) {
4413     if (!arguments.length) return pointRadius;
4414     pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
4415     return path;
4416   };
4417
4418   function reset() {
4419     cacheStream = null;
4420     return path;
4421   }
4422
4423   return path.projection(d3.geo.albersUsa()).context(null);
4424 };
4425
4426 function d3_geo_pathProjectStream(project) {
4427   var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); });
4428   return function(stream) { return d3_geo_projectionRadians(resample(stream)); };
4429 }
4430
4431 d3.geo.transform = function(methods) {
4432   return {
4433     stream: function(stream) {
4434       var transform = new d3_geo_transform(stream);
4435       for (var k in methods) transform[k] = methods[k];
4436       return transform;
4437     }
4438   };
4439 };
4440
4441 function d3_geo_transform(stream) {
4442   this.stream = stream;
4443 }
4444
4445 d3_geo_transform.prototype = {
4446   point: function(x, y) { this.stream.point(x, y); },
4447   sphere: function() { this.stream.sphere(); },
4448   lineStart: function() { this.stream.lineStart(); },
4449   lineEnd: function() { this.stream.lineEnd(); },
4450   polygonStart: function() { this.stream.polygonStart(); },
4451   polygonEnd: function() { this.stream.polygonEnd(); }
4452 };
4453
4454 function d3_geo_transformPoint(stream, point) {
4455   return {
4456     point: point,
4457     sphere: function() { stream.sphere(); },
4458     lineStart: function() { stream.lineStart(); },
4459     lineEnd: function() { stream.lineEnd(); },
4460     polygonStart: function() { stream.polygonStart(); },
4461     polygonEnd: function() { stream.polygonEnd(); },
4462   };
4463 }
4464
4465 d3.geo.projection = d3_geo_projection;
4466 d3.geo.projectionMutator = d3_geo_projectionMutator;
4467
4468 function d3_geo_projection(project) {
4469   return d3_geo_projectionMutator(function() { return project; })();
4470 }
4471
4472 function d3_geo_projectionMutator(projectAt) {
4473   var project,
4474       rotate,
4475       projectRotate,
4476       projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
4477       k = 150, // scale
4478       x = 480, y = 250, // translate
4479       λ = 0, φ = 0, // center
4480       δλ = 0, δφ = 0, δγ = 0, // rotate
4481       δx, δy, // center
4482       preclip = d3_geo_clipAntimeridian,
4483       postclip = d3_identity,
4484       clipAngle = null,
4485       clipExtent = null,
4486       stream;
4487
4488   function projection(point) {
4489     point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
4490     return [point[0] * k + δx, δy - point[1] * k];
4491   }
4492
4493   function invert(point) {
4494     point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
4495     return point && [point[0] * d3_degrees, point[1] * d3_degrees];
4496   }
4497
4498   projection.stream = function(output) {
4499     if (stream) stream.valid = false;
4500     stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
4501     stream.valid = true; // allow caching by d3.geo.path
4502     return stream;
4503   };
4504
4505   projection.clipAngle = function(_) {
4506     if (!arguments.length) return clipAngle;
4507     preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
4508     return invalidate();
4509   };
4510
4511   projection.clipExtent = function(_) {
4512     if (!arguments.length) return clipExtent;
4513     clipExtent = _;
4514     postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
4515     return invalidate();
4516   };
4517
4518   projection.scale = function(_) {
4519     if (!arguments.length) return k;
4520     k = +_;
4521     return reset();
4522   };
4523
4524   projection.translate = function(_) {
4525     if (!arguments.length) return [x, y];
4526     x = +_[0];
4527     y = +_[1];
4528     return reset();
4529   };
4530
4531   projection.center = function(_) {
4532     if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
4533     λ = _[0] % 360 * d3_radians;
4534     φ = _[1] % 360 * d3_radians;
4535     return reset();
4536   };
4537
4538   projection.rotate = function(_) {
4539     if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees];
4540     δλ = _[0] % 360 * d3_radians;
4541     δφ = _[1] % 360 * d3_radians;
4542     δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
4543     return reset();
4544   };
4545
4546   d3.rebind(projection, projectResample, "precision");
4547
4548   function reset() {
4549     projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
4550     var center = project(λ, φ);
4551     δx = x - center[0] * k;
4552     δy = y + center[1] * k;
4553     return invalidate();
4554   }
4555
4556   function invalidate() {
4557     if (stream) stream.valid = false, stream = null;
4558     return projection;
4559   }
4560
4561   return function() {
4562     project = projectAt.apply(this, arguments);
4563     projection.invert = project.invert && invert;
4564     return reset();
4565   };
4566 }
4567
4568 function d3_geo_projectionRadians(stream) {
4569   return d3_geo_transformPoint(stream, function(x, y) {
4570     stream.point(x * d3_radians, y * d3_radians);
4571   });
4572 }
4573
4574 function d3_geo_mercator(λ, φ) {
4575   return [λ, Math.log(Math.tan(π / 4 + φ / 2))];
4576 }
4577
4578 d3_geo_mercator.invert = function(x, y) {
4579   return [x, 2 * Math.atan(Math.exp(y)) - halfπ];
4580 };
4581
4582 function d3_geo_mercatorProjection(project) {
4583   var m = d3_geo_projection(project),
4584       scale = m.scale,
4585       translate = m.translate,
4586       clipExtent = m.clipExtent,
4587       clipAuto;
4588
4589   m.scale = function() {
4590     var v = scale.apply(m, arguments);
4591     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4592   };
4593
4594   m.translate = function() {
4595     var v = translate.apply(m, arguments);
4596     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4597   };
4598
4599   m.clipExtent = function(_) {
4600     var v = clipExtent.apply(m, arguments);
4601     if (v === m) {
4602       if (clipAuto = _ == null) {
4603         var k = π * scale(), t = translate();
4604         clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]);
4605       }
4606     } else if (clipAuto) {
4607       v = null;
4608     }
4609     return v;
4610   };
4611
4612   return m.clipExtent(null);
4613 }
4614
4615 (d3.geo.mercator = function() {
4616   return d3_geo_mercatorProjection(d3_geo_mercator);
4617 }).raw = d3_geo_mercator;
4618 d3.geom = {};
4619
4620 d3.geom.polygon = function(coordinates) {
4621   d3_subclass(coordinates, d3_geom_polygonPrototype);
4622   return coordinates;
4623 };
4624
4625 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
4626
4627 d3_geom_polygonPrototype.area = function() {
4628   var i = -1,
4629       n = this.length,
4630       a,
4631       b = this[n - 1],
4632       area = 0;
4633
4634   while (++i < n) {
4635     a = b;
4636     b = this[i];
4637     area += a[1] * b[0] - a[0] * b[1];
4638   }
4639
4640   return area * .5;
4641 };
4642
4643 d3_geom_polygonPrototype.centroid = function(k) {
4644   var i = -1,
4645       n = this.length,
4646       x = 0,
4647       y = 0,
4648       a,
4649       b = this[n - 1],
4650       c;
4651
4652   if (!arguments.length) k = -1 / (6 * this.area());
4653
4654   while (++i < n) {
4655     a = b;
4656     b = this[i];
4657     c = a[0] * b[1] - b[0] * a[1];
4658     x += (a[0] + b[0]) * c;
4659     y += (a[1] + b[1]) * c;
4660   }
4661
4662   return [x * k, y * k];
4663 };
4664
4665 // The Sutherland-Hodgman clipping algorithm.
4666 // Note: requires the clip polygon to be counterclockwise and convex.
4667 d3_geom_polygonPrototype.clip = function(subject) {
4668   var input,
4669       closed = d3_geom_polygonClosed(subject),
4670       i = -1,
4671       n = this.length - d3_geom_polygonClosed(this),
4672       j,
4673       m,
4674       a = this[n - 1],
4675       b,
4676       c,
4677       d;
4678
4679   while (++i < n) {
4680     input = subject.slice();
4681     subject.length = 0;
4682     b = this[i];
4683     c = input[(m = input.length - closed) - 1];
4684     j = -1;
4685     while (++j < m) {
4686       d = input[j];
4687       if (d3_geom_polygonInside(d, a, b)) {
4688         if (!d3_geom_polygonInside(c, a, b)) {
4689           subject.push(d3_geom_polygonIntersect(c, d, a, b));
4690         }
4691         subject.push(d);
4692       } else if (d3_geom_polygonInside(c, a, b)) {
4693         subject.push(d3_geom_polygonIntersect(c, d, a, b));
4694       }
4695       c = d;
4696     }
4697     if (closed) subject.push(subject[0]);
4698     a = b;
4699   }
4700
4701   return subject;
4702 };
4703
4704 function d3_geom_polygonInside(p, a, b) {
4705   return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
4706 }
4707
4708 // Intersect two infinite lines cd and ab.
4709 function d3_geom_polygonIntersect(c, d, a, b) {
4710   var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3,
4711       y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3,
4712       ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
4713   return [x1 + ua * x21, y1 + ua * y21];
4714 }
4715
4716 // Returns true if the polygon is closed.
4717 function d3_geom_polygonClosed(coordinates) {
4718   var a = coordinates[0],
4719       b = coordinates[coordinates.length - 1];
4720   return !(a[0] - b[0] || a[1] - b[1]);
4721 }
4722 function d3_geom_pointX(d) {
4723   return d[0];
4724 }
4725
4726 function d3_geom_pointY(d) {
4727   return d[1];
4728 }
4729
4730 /**
4731  * Computes the 2D convex hull of a set of points using the monotone chain
4732  * algorithm:
4733  * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain)
4734  *
4735  * The runtime of this algorithm is O(n log n), where n is the number of input
4736  * points. However in practice it outperforms other O(n log n) hulls.
4737  *
4738  * @param vertices [[x1, y1], [x2, y2], ...]
4739  * @returns polygon [[x1, y1], [x2, y2], ...]
4740  */
4741 d3.geom.hull = function(vertices) {
4742   var x = d3_geom_pointX,
4743       y = d3_geom_pointY;
4744
4745   if (arguments.length) return hull(vertices);
4746
4747   function hull(data) {
4748     // Hull of < 3 points is not well-defined
4749     if (data.length < 3) return [];
4750
4751     var fx = d3_functor(x),
4752         fy = d3_functor(y),
4753         i,
4754         n = data.length,
4755         points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]]
4756         flippedPoints = [];
4757
4758     for (i = 0 ; i < n; i++) {
4759       points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]);
4760     }
4761
4762     // sort ascending by x-coord first, y-coord second
4763     points.sort(d3_geom_hullOrder);
4764
4765     // we flip bottommost points across y axis so we can use the upper hull routine on both
4766     for (i = 0; i < n; i++) flippedPoints.push([points[i][0], -points[i][1]]);
4767
4768     var upper = d3_geom_hullUpper(points),
4769         lower = d3_geom_hullUpper(flippedPoints);
4770
4771     // construct the polygon, removing possible duplicate endpoints
4772     var skipLeft = lower[0] === upper[0],
4773         skipRight  = lower[lower.length - 1] === upper[upper.length - 1],
4774         polygon = [];
4775
4776     // add upper hull in r->l order
4777     // then add lower hull in l->r order
4778     for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
4779     for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
4780
4781     return polygon;
4782   }
4783
4784   hull.x = function(_) {
4785     return arguments.length ? (x = _, hull) : x;
4786   };
4787
4788   hull.y = function(_) {
4789     return arguments.length ? (y = _, hull) : y;
4790   };
4791
4792   return hull;
4793 };
4794
4795 // finds the 'upper convex hull' (see wiki link above)
4796 // assumes points arg has >=3 elements, is sorted by x, unique in y
4797 // returns array of indices into points in left to right order
4798 function d3_geom_hullUpper(points) {
4799   var n = points.length,
4800       hull = [0, 1],
4801       hs = 2; // hull size
4802
4803   for (var i = 2; i < n; i++) {
4804     while (hs > 1 && d3_cross2d(points[hull[hs-2]], points[hull[hs-1]], points[i]) <= 0) --hs;
4805     hull[hs++] = i;
4806   }
4807
4808   // we slice to make sure that the points we 'popped' from hull don't stay behind
4809   return hull.slice(0, hs);
4810 }
4811
4812 // comparator for ascending sort by x-coord first, y-coord second
4813 function d3_geom_hullOrder(a, b) {
4814   return a[0] - b[0] || a[1] - b[1];
4815 }
4816 // import "../transition/transition";
4817
4818 d3_selectionPrototype.transition = function(name) {
4819   var id = d3_transitionInheritId || ++d3_transitionId,
4820       ns = d3_transitionNamespace(name),
4821       subgroups = [],
4822       subgroup,
4823       node,
4824       transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250};
4825
4826   for (var j = -1, m = this.length; ++j < m;) {
4827     subgroups.push(subgroup = []);
4828     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4829       if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
4830       subgroup.push(node);
4831     }
4832   }
4833
4834   return d3_transition(subgroups, ns, id);
4835 };
4836 // import "../transition/transition";
4837
4838 // TODO Interrupt transitions for all namespaces?
4839 d3_selectionPrototype.interrupt = function(name) {
4840   return this.each(name == null
4841       ? d3_selection_interrupt
4842       : d3_selection_interruptNS(d3_transitionNamespace(name)));
4843 };
4844
4845 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
4846
4847 function d3_selection_interruptNS(ns) {
4848   return function() {
4849     var lock, active;
4850     if ((lock = this[ns]) && (active = lock[lock.active])) {
4851       if (--lock.count) delete lock[lock.active];
4852       else delete this[ns];
4853       lock.active += .5;
4854       active.event && active.event.interrupt.call(this, this.__data__, active.index);
4855     }
4856   };
4857 }
4858
4859 function d3_transition(groups, ns, id) {
4860   d3_subclass(groups, d3_transitionPrototype);
4861
4862   // Note: read-only!
4863   groups.namespace = ns;
4864   groups.id = id;
4865
4866   return groups;
4867 }
4868
4869 var d3_transitionPrototype = [],
4870     d3_transitionId = 0,
4871     d3_transitionInheritId,
4872     d3_transitionInherit;
4873
4874 d3_transitionPrototype.call = d3_selectionPrototype.call;
4875 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
4876 d3_transitionPrototype.node = d3_selectionPrototype.node;
4877 d3_transitionPrototype.size = d3_selectionPrototype.size;
4878
4879 d3.transition = function(selection, name) {
4880   return selection && selection.transition
4881       ? (d3_transitionInheritId ? selection.transition(name) : selection)
4882       : d3.selection().transition(selection);
4883 };
4884
4885 d3.transition.prototype = d3_transitionPrototype;
4886
4887
4888 d3_transitionPrototype.select = function(selector) {
4889   var id = this.id,
4890       ns = this.namespace,
4891       subgroups = [],
4892       subgroup,
4893       subnode,
4894       node;
4895
4896   selector = d3_selection_selector(selector);
4897
4898   for (var j = -1, m = this.length; ++j < m;) {
4899     subgroups.push(subgroup = []);
4900     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4901       if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
4902         if ("__data__" in node) subnode.__data__ = node.__data__;
4903         d3_transitionNode(subnode, i, ns, id, node[ns][id]);
4904         subgroup.push(subnode);
4905       } else {
4906         subgroup.push(null);
4907       }
4908     }
4909   }
4910
4911   return d3_transition(subgroups, ns, id);
4912 };
4913
4914 d3_transitionPrototype.selectAll = function(selector) {
4915   var id = this.id,
4916       ns = this.namespace,
4917       subgroups = [],
4918       subgroup,
4919       subnodes,
4920       node,
4921       subnode,
4922       transition;
4923
4924   selector = d3_selection_selectorAll(selector);
4925
4926   for (var j = -1, m = this.length; ++j < m;) {
4927     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4928       if (node = group[i]) {
4929         transition = node[ns][id];
4930         subnodes = selector.call(node, node.__data__, i, j);
4931         subgroups.push(subgroup = []);
4932         for (var k = -1, o = subnodes.length; ++k < o;) {
4933           if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
4934           subgroup.push(subnode);
4935         }
4936       }
4937     }
4938   }
4939
4940   return d3_transition(subgroups, ns, id);
4941 };
4942
4943 d3_transitionPrototype.filter = function(filter) {
4944   var subgroups = [],
4945       subgroup,
4946       group,
4947       node;
4948
4949   if (typeof filter !== "function") filter = d3_selection_filter(filter);
4950
4951   for (var j = 0, m = this.length; j < m; j++) {
4952     subgroups.push(subgroup = []);
4953     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
4954       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
4955         subgroup.push(node);
4956       }
4957     }
4958   }
4959
4960   return d3_transition(subgroups, this.namespace, this.id);
4961 };
4962 d3.color = d3_color;
4963
4964 function d3_color() {}
4965
4966 d3_color.prototype.toString = function() {
4967   return this.rgb() + "";
4968 };
4969
4970 d3.hsl = d3_hsl;
4971
4972 function d3_hsl(h, s, l) {
4973   return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l)
4974       : arguments.length < 2 ? (h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l)
4975       : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl))
4976       : new d3_hsl(h, s, l);
4977 }
4978
4979 var d3_hslPrototype = d3_hsl.prototype = new d3_color;
4980
4981 d3_hslPrototype.brighter = function(k) {
4982   k = Math.pow(0.7, arguments.length ? k : 1);
4983   return new d3_hsl(this.h, this.s, this.l / k);
4984 };
4985
4986 d3_hslPrototype.darker = function(k) {
4987   k = Math.pow(0.7, arguments.length ? k : 1);
4988   return new d3_hsl(this.h, this.s, k * this.l);
4989 };
4990
4991 d3_hslPrototype.rgb = function() {
4992   return d3_hsl_rgb(this.h, this.s, this.l);
4993 };
4994
4995 function d3_hsl_rgb(h, s, l) {
4996   var m1,
4997       m2;
4998
4999   /* Some simple corrections for h, s and l. */
5000   h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
5001   s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
5002   l = l < 0 ? 0 : l > 1 ? 1 : l;
5003
5004   /* From FvD 13.37, CSS Color Module Level 3 */
5005   m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
5006   m1 = 2 * l - m2;
5007
5008   function v(h) {
5009     if (h > 360) h -= 360;
5010     else if (h < 0) h += 360;
5011     if (h < 60) return m1 + (m2 - m1) * h / 60;
5012     if (h < 180) return m2;
5013     if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
5014     return m1;
5015   }
5016
5017   function vv(h) {
5018     return Math.round(v(h) * 255);
5019   }
5020
5021   return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
5022 }
5023
5024 d3.hcl = d3_hcl;
5025
5026 function d3_hcl(h, c, l) {
5027   return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l)
5028       : arguments.length < 2 ? (h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l)
5029       : (h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b)
5030       : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b)))
5031       : new d3_hcl(h, c, l);
5032 }
5033
5034 var d3_hclPrototype = d3_hcl.prototype = new d3_color;
5035
5036 d3_hclPrototype.brighter = function(k) {
5037   return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
5038 };
5039
5040 d3_hclPrototype.darker = function(k) {
5041   return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
5042 };
5043
5044 d3_hclPrototype.rgb = function() {
5045   return d3_hcl_lab(this.h, this.c, this.l).rgb();
5046 };
5047
5048 function d3_hcl_lab(h, c, l) {
5049   if (isNaN(h)) h = 0;
5050   if (isNaN(c)) c = 0;
5051   return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
5052 }
5053
5054 d3.lab = d3_lab;
5055
5056 function d3_lab(l, a, b) {
5057   return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b)
5058       : arguments.length < 2 ? (l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b)
5059       : (l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l)
5060       : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b)))
5061       : new d3_lab(l, a, b);
5062 }
5063
5064 // Corresponds roughly to RGB brighter/darker
5065 var d3_lab_K = 18;
5066
5067 // D65 standard referent
5068 var d3_lab_X = 0.950470,
5069     d3_lab_Y = 1,
5070     d3_lab_Z = 1.088830;
5071
5072 var d3_labPrototype = d3_lab.prototype = new d3_color;
5073
5074 d3_labPrototype.brighter = function(k) {
5075   return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5076 };
5077
5078 d3_labPrototype.darker = function(k) {
5079   return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5080 };
5081
5082 d3_labPrototype.rgb = function() {
5083   return d3_lab_rgb(this.l, this.a, this.b);
5084 };
5085
5086 function d3_lab_rgb(l, a, b) {
5087   var y = (l + 16) / 116,
5088       x = y + a / 500,
5089       z = y - b / 200;
5090   x = d3_lab_xyz(x) * d3_lab_X;
5091   y = d3_lab_xyz(y) * d3_lab_Y;
5092   z = d3_lab_xyz(z) * d3_lab_Z;
5093   return new d3_rgb(
5094     d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
5095     d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
5096     d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
5097   );
5098 }
5099
5100 function d3_lab_hcl(l, a, b) {
5101   return l > 0
5102       ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l)
5103       : new d3_hcl(NaN, NaN, l);
5104 }
5105
5106 function d3_lab_xyz(x) {
5107   return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
5108 }
5109 function d3_xyz_lab(x) {
5110   return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
5111 }
5112
5113 function d3_xyz_rgb(r) {
5114   return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
5115 }
5116
5117 d3.rgb = d3_rgb;
5118
5119 function d3_rgb(r, g, b) {
5120   return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b)
5121       : arguments.length < 2 ? (r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b)
5122       : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb))
5123       : new d3_rgb(r, g, b);
5124 }
5125
5126 function d3_rgbNumber(value) {
5127   return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff);
5128 }
5129
5130 function d3_rgbString(value) {
5131   return d3_rgbNumber(value) + "";
5132 }
5133
5134 var d3_rgbPrototype = d3_rgb.prototype = new d3_color;
5135
5136 d3_rgbPrototype.brighter = function(k) {
5137   k = Math.pow(0.7, arguments.length ? k : 1);
5138   var r = this.r,
5139       g = this.g,
5140       b = this.b,
5141       i = 30;
5142   if (!r && !g && !b) return new d3_rgb(i, i, i);
5143   if (r && r < i) r = i;
5144   if (g && g < i) g = i;
5145   if (b && b < i) b = i;
5146   return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
5147 };
5148
5149 d3_rgbPrototype.darker = function(k) {
5150   k = Math.pow(0.7, arguments.length ? k : 1);
5151   return new d3_rgb(k * this.r, k * this.g, k * this.b);
5152 };
5153
5154 d3_rgbPrototype.hsl = function() {
5155   return d3_rgb_hsl(this.r, this.g, this.b);
5156 };
5157
5158 d3_rgbPrototype.toString = function() {
5159   return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
5160 };
5161
5162 function d3_rgb_hex(v) {
5163   return v < 0x10
5164       ? "0" + Math.max(0, v).toString(16)
5165       : Math.min(255, v).toString(16);
5166 }
5167
5168 function d3_rgb_parse(format, rgb, hsl) {
5169   var r = 0, // red channel; int in [0, 255]
5170       g = 0, // green channel; int in [0, 255]
5171       b = 0, // blue channel; int in [0, 255]
5172       m1, // CSS color specification match
5173       m2, // CSS color specification type (e.g., rgb)
5174       color;
5175
5176   /* Handle hsl, rgb. */
5177   m1 = /([a-z]+)\((.*)\)/i.exec(format);
5178   if (m1) {
5179     m2 = m1[2].split(",");
5180     switch (m1[1]) {
5181       case "hsl": {
5182         return hsl(
5183           parseFloat(m2[0]), // degrees
5184           parseFloat(m2[1]) / 100, // percentage
5185           parseFloat(m2[2]) / 100 // percentage
5186         );
5187       }
5188       case "rgb": {
5189         return rgb(
5190           d3_rgb_parseNumber(m2[0]),
5191           d3_rgb_parseNumber(m2[1]),
5192           d3_rgb_parseNumber(m2[2])
5193         );
5194       }
5195     }
5196   }
5197
5198   /* Named colors. */
5199   if (color = d3_rgb_names.get(format.toLowerCase())) {
5200     return rgb(color.r, color.g, color.b);
5201   }
5202
5203   /* Hexadecimal colors: #rgb and #rrggbb. */
5204   if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
5205     if (format.length === 4) {
5206       r = (color & 0xf00) >> 4; r = (r >> 4) | r;
5207       g = (color & 0xf0); g = (g >> 4) | g;
5208       b = (color & 0xf); b = (b << 4) | b;
5209     } else if (format.length === 7) {
5210       r = (color & 0xff0000) >> 16;
5211       g = (color & 0xff00) >> 8;
5212       b = (color & 0xff);
5213     }
5214   }
5215
5216   return rgb(r, g, b);
5217 }
5218
5219 function d3_rgb_hsl(r, g, b) {
5220   var min = Math.min(r /= 255, g /= 255, b /= 255),
5221       max = Math.max(r, g, b),
5222       d = max - min,
5223       h,
5224       s,
5225       l = (max + min) / 2;
5226   if (d) {
5227     s = l < .5 ? d / (max + min) : d / (2 - max - min);
5228     if (r == max) h = (g - b) / d + (g < b ? 6 : 0);
5229     else if (g == max) h = (b - r) / d + 2;
5230     else h = (r - g) / d + 4;
5231     h *= 60;
5232   } else {
5233     h = NaN;
5234     s = l > 0 && l < 1 ? 0 : h;
5235   }
5236   return new d3_hsl(h, s, l);
5237 }
5238
5239 function d3_rgb_lab(r, g, b) {
5240   r = d3_rgb_xyz(r);
5241   g = d3_rgb_xyz(g);
5242   b = d3_rgb_xyz(b);
5243   var x = d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / d3_lab_X),
5244       y = d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / d3_lab_Y),
5245       z = d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / d3_lab_Z);
5246   return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
5247 }
5248
5249 function d3_rgb_xyz(r) {
5250   return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
5251 }
5252
5253 function d3_rgb_parseNumber(c) { // either integer or percentage
5254   var f = parseFloat(c);
5255   return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
5256 }
5257
5258 var d3_rgb_names = d3.map({
5259   aliceblue: 0xf0f8ff,
5260   antiquewhite: 0xfaebd7,
5261   aqua: 0x00ffff,
5262   aquamarine: 0x7fffd4,
5263   azure: 0xf0ffff,
5264   beige: 0xf5f5dc,
5265   bisque: 0xffe4c4,
5266   black: 0x000000,
5267   blanchedalmond: 0xffebcd,
5268   blue: 0x0000ff,
5269   blueviolet: 0x8a2be2,
5270   brown: 0xa52a2a,
5271   burlywood: 0xdeb887,
5272   cadetblue: 0x5f9ea0,
5273   chartreuse: 0x7fff00,
5274   chocolate: 0xd2691e,
5275   coral: 0xff7f50,
5276   cornflowerblue: 0x6495ed,
5277   cornsilk: 0xfff8dc,
5278   crimson: 0xdc143c,
5279   cyan: 0x00ffff,
5280   darkblue: 0x00008b,
5281   darkcyan: 0x008b8b,
5282   darkgoldenrod: 0xb8860b,
5283   darkgray: 0xa9a9a9,
5284   darkgreen: 0x006400,
5285   darkgrey: 0xa9a9a9,
5286   darkkhaki: 0xbdb76b,
5287   darkmagenta: 0x8b008b,
5288   darkolivegreen: 0x556b2f,
5289   darkorange: 0xff8c00,
5290   darkorchid: 0x9932cc,
5291   darkred: 0x8b0000,
5292   darksalmon: 0xe9967a,
5293   darkseagreen: 0x8fbc8f,
5294   darkslateblue: 0x483d8b,
5295   darkslategray: 0x2f4f4f,
5296   darkslategrey: 0x2f4f4f,
5297   darkturquoise: 0x00ced1,
5298   darkviolet: 0x9400d3,
5299   deeppink: 0xff1493,
5300   deepskyblue: 0x00bfff,
5301   dimgray: 0x696969,
5302   dimgrey: 0x696969,
5303   dodgerblue: 0x1e90ff,
5304   firebrick: 0xb22222,
5305   floralwhite: 0xfffaf0,
5306   forestgreen: 0x228b22,
5307   fuchsia: 0xff00ff,
5308   gainsboro: 0xdcdcdc,
5309   ghostwhite: 0xf8f8ff,
5310   gold: 0xffd700,
5311   goldenrod: 0xdaa520,
5312   gray: 0x808080,
5313   green: 0x008000,
5314   greenyellow: 0xadff2f,
5315   grey: 0x808080,
5316   honeydew: 0xf0fff0,
5317   hotpink: 0xff69b4,
5318   indianred: 0xcd5c5c,
5319   indigo: 0x4b0082,
5320   ivory: 0xfffff0,
5321   khaki: 0xf0e68c,
5322   lavender: 0xe6e6fa,
5323   lavenderblush: 0xfff0f5,
5324   lawngreen: 0x7cfc00,
5325   lemonchiffon: 0xfffacd,
5326   lightblue: 0xadd8e6,
5327   lightcoral: 0xf08080,
5328   lightcyan: 0xe0ffff,
5329   lightgoldenrodyellow: 0xfafad2,
5330   lightgray: 0xd3d3d3,
5331   lightgreen: 0x90ee90,
5332   lightgrey: 0xd3d3d3,
5333   lightpink: 0xffb6c1,
5334   lightsalmon: 0xffa07a,
5335   lightseagreen: 0x20b2aa,
5336   lightskyblue: 0x87cefa,
5337   lightslategray: 0x778899,
5338   lightslategrey: 0x778899,
5339   lightsteelblue: 0xb0c4de,
5340   lightyellow: 0xffffe0,
5341   lime: 0x00ff00,
5342   limegreen: 0x32cd32,
5343   linen: 0xfaf0e6,
5344   magenta: 0xff00ff,
5345   maroon: 0x800000,
5346   mediumaquamarine: 0x66cdaa,
5347   mediumblue: 0x0000cd,
5348   mediumorchid: 0xba55d3,
5349   mediumpurple: 0x9370db,
5350   mediumseagreen: 0x3cb371,
5351   mediumslateblue: 0x7b68ee,
5352   mediumspringgreen: 0x00fa9a,
5353   mediumturquoise: 0x48d1cc,
5354   mediumvioletred: 0xc71585,
5355   midnightblue: 0x191970,
5356   mintcream: 0xf5fffa,
5357   mistyrose: 0xffe4e1,
5358   moccasin: 0xffe4b5,
5359   navajowhite: 0xffdead,
5360   navy: 0x000080,
5361   oldlace: 0xfdf5e6,
5362   olive: 0x808000,
5363   olivedrab: 0x6b8e23,
5364   orange: 0xffa500,
5365   orangered: 0xff4500,
5366   orchid: 0xda70d6,
5367   palegoldenrod: 0xeee8aa,
5368   palegreen: 0x98fb98,
5369   paleturquoise: 0xafeeee,
5370   palevioletred: 0xdb7093,
5371   papayawhip: 0xffefd5,
5372   peachpuff: 0xffdab9,
5373   peru: 0xcd853f,
5374   pink: 0xffc0cb,
5375   plum: 0xdda0dd,
5376   powderblue: 0xb0e0e6,
5377   purple: 0x800080,
5378   rebeccapurple: 0x663399,
5379   red: 0xff0000,
5380   rosybrown: 0xbc8f8f,
5381   royalblue: 0x4169e1,
5382   saddlebrown: 0x8b4513,
5383   salmon: 0xfa8072,
5384   sandybrown: 0xf4a460,
5385   seagreen: 0x2e8b57,
5386   seashell: 0xfff5ee,
5387   sienna: 0xa0522d,
5388   silver: 0xc0c0c0,
5389   skyblue: 0x87ceeb,
5390   slateblue: 0x6a5acd,
5391   slategray: 0x708090,
5392   slategrey: 0x708090,
5393   snow: 0xfffafa,
5394   springgreen: 0x00ff7f,
5395   steelblue: 0x4682b4,
5396   tan: 0xd2b48c,
5397   teal: 0x008080,
5398   thistle: 0xd8bfd8,
5399   tomato: 0xff6347,
5400   turquoise: 0x40e0d0,
5401   violet: 0xee82ee,
5402   wheat: 0xf5deb3,
5403   white: 0xffffff,
5404   whitesmoke: 0xf5f5f5,
5405   yellow: 0xffff00,
5406   yellowgreen: 0x9acd32
5407 });
5408
5409 d3_rgb_names.forEach(function(key, value) {
5410   d3_rgb_names.set(key, d3_rgbNumber(value));
5411 });
5412
5413 d3.interpolateRgb = d3_interpolateRgb;
5414
5415 function d3_interpolateRgb(a, b) {
5416   a = d3.rgb(a);
5417   b = d3.rgb(b);
5418   var ar = a.r,
5419       ag = a.g,
5420       ab = a.b,
5421       br = b.r - ar,
5422       bg = b.g - ag,
5423       bb = b.b - ab;
5424   return function(t) {
5425     return "#"
5426         + d3_rgb_hex(Math.round(ar + br * t))
5427         + d3_rgb_hex(Math.round(ag + bg * t))
5428         + d3_rgb_hex(Math.round(ab + bb * t));
5429   };
5430 }
5431
5432 d3.interpolateObject = d3_interpolateObject;
5433
5434 function d3_interpolateObject(a, b) {
5435   var i = {},
5436       c = {},
5437       k;
5438   for (k in a) {
5439     if (k in b) {
5440       i[k] = d3_interpolate(a[k], b[k]);
5441     } else {
5442       c[k] = a[k];
5443     }
5444   }
5445   for (k in b) {
5446     if (!(k in a)) {
5447       c[k] = b[k];
5448     }
5449   }
5450   return function(t) {
5451     for (k in i) c[k] = i[k](t);
5452     return c;
5453   };
5454 }
5455
5456 d3.interpolateArray = d3_interpolateArray;
5457
5458 function d3_interpolateArray(a, b) {
5459   var x = [],
5460       c = [],
5461       na = a.length,
5462       nb = b.length,
5463       n0 = Math.min(a.length, b.length),
5464       i;
5465   for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
5466   for (; i < na; ++i) c[i] = a[i];
5467   for (; i < nb; ++i) c[i] = b[i];
5468   return function(t) {
5469     for (i = 0; i < n0; ++i) c[i] = x[i](t);
5470     return c;
5471   };
5472 }
5473 d3.interpolateNumber = d3_interpolateNumber;
5474
5475 function d3_interpolateNumber(a, b) {
5476   a = +a, b = +b;
5477   return function(t) { return a * (1 - t) + b * t; };
5478 }
5479
5480 d3.interpolateString = d3_interpolateString;
5481
5482 function d3_interpolateString(a, b) {
5483   var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b
5484       am, // current match in a
5485       bm, // current match in b
5486       bs, // string preceding current number in b, if any
5487       i = -1, // index in s
5488       s = [], // string constants and placeholders
5489       q = []; // number interpolators
5490
5491   // Coerce inputs to strings.
5492   a = a + "", b = b + "";
5493
5494   // Interpolate pairs of numbers in a & b.
5495   while ((am = d3_interpolate_numberA.exec(a))
5496       && (bm = d3_interpolate_numberB.exec(b))) {
5497     if ((bs = bm.index) > bi) { // a string precedes the next number in b
5498       bs = b.slice(bi, bs);
5499       if (s[i]) s[i] += bs; // coalesce with previous string
5500       else s[++i] = bs;
5501     }
5502     if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
5503       if (s[i]) s[i] += bm; // coalesce with previous string
5504       else s[++i] = bm;
5505     } else { // interpolate non-matching numbers
5506       s[++i] = null;
5507       q.push({i: i, x: d3_interpolateNumber(am, bm)});
5508     }
5509     bi = d3_interpolate_numberB.lastIndex;
5510   }
5511
5512   // Add remains of b.
5513   if (bi < b.length) {
5514     bs = b.slice(bi);
5515     if (s[i]) s[i] += bs; // coalesce with previous string
5516     else s[++i] = bs;
5517   }
5518
5519   // Special optimization for only a single match.
5520   // Otherwise, interpolate each of the numbers and rejoin the string.
5521   return s.length < 2
5522       ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; })
5523       : function() { return b; })
5524       : (b = q.length, function(t) {
5525           for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
5526           return s.join("");
5527         });
5528 }
5529
5530 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
5531     d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
5532
5533 d3.interpolate = d3_interpolate;
5534
5535 function d3_interpolate(a, b) {
5536   var i = d3.interpolators.length, f;
5537   while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
5538   return f;
5539 }
5540
5541 d3.interpolators = [
5542   function(a, b) {
5543     var t = typeof b;
5544     return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString)
5545         : b instanceof d3_color ? d3_interpolateRgb
5546         : Array.isArray(b) ? d3_interpolateArray
5547         : t === "object" && isNaN(b) ? d3_interpolateObject
5548         : d3_interpolateNumber)(a, b);
5549   }
5550 ];
5551
5552 d3.transform = function(string) {
5553   var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
5554   return (d3.transform = function(string) {
5555     if (string != null) {
5556       g.setAttribute("transform", string);
5557       var t = g.transform.baseVal.consolidate();
5558     }
5559     return new d3_transform(t ? t.matrix : d3_transformIdentity);
5560   })(string);
5561 };
5562
5563 // Compute x-scale and normalize the first row.
5564 // Compute shear and make second row orthogonal to first.
5565 // Compute y-scale and normalize the second row.
5566 // Finally, compute the rotation.
5567 function d3_transform(m) {
5568   var r0 = [m.a, m.b],
5569       r1 = [m.c, m.d],
5570       kx = d3_transformNormalize(r0),
5571       kz = d3_transformDot(r0, r1),
5572       ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
5573   if (r0[0] * r1[1] < r1[0] * r0[1]) {
5574     r0[0] *= -1;
5575     r0[1] *= -1;
5576     kx *= -1;
5577     kz *= -1;
5578   }
5579   this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
5580   this.translate = [m.e, m.f];
5581   this.scale = [kx, ky];
5582   this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
5583 };
5584
5585 d3_transform.prototype.toString = function() {
5586   return "translate(" + this.translate
5587       + ")rotate(" + this.rotate
5588       + ")skewX(" + this.skew
5589       + ")scale(" + this.scale
5590       + ")";
5591 };
5592
5593 function d3_transformDot(a, b) {
5594   return a[0] * b[0] + a[1] * b[1];
5595 }
5596
5597 function d3_transformNormalize(a) {
5598   var k = Math.sqrt(d3_transformDot(a, a));
5599   if (k) {
5600     a[0] /= k;
5601     a[1] /= k;
5602   }
5603   return k;
5604 }
5605
5606 function d3_transformCombine(a, b, k) {
5607   a[0] += k * b[0];
5608   a[1] += k * b[1];
5609   return a;
5610 }
5611
5612 var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
5613
5614 d3.interpolateTransform = d3_interpolateTransform;
5615
5616 function d3_interpolateTransform(a, b) {
5617   var s = [], // string constants and placeholders
5618       q = [], // number interpolators
5619       n,
5620       A = d3.transform(a),
5621       B = d3.transform(b),
5622       ta = A.translate,
5623       tb = B.translate,
5624       ra = A.rotate,
5625       rb = B.rotate,
5626       wa = A.skew,
5627       wb = B.skew,
5628       ka = A.scale,
5629       kb = B.scale;
5630
5631   if (ta[0] != tb[0] || ta[1] != tb[1]) {
5632     s.push("translate(", null, ",", null, ")");
5633     q.push({i: 1, x: d3_interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3_interpolateNumber(ta[1], tb[1])});
5634   } else if (tb[0] || tb[1]) {
5635     s.push("translate(" + tb + ")");
5636   } else {
5637     s.push("");
5638   }
5639
5640   if (ra != rb) {
5641     if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
5642     q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb)});
5643   } else if (rb) {
5644     s.push(s.pop() + "rotate(" + rb + ")");
5645   }
5646
5647   if (wa != wb) {
5648     q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)});
5649   } else if (wb) {
5650     s.push(s.pop() + "skewX(" + wb + ")");
5651   }
5652
5653   if (ka[0] != kb[0] || ka[1] != kb[1]) {
5654     n = s.push(s.pop() + "scale(", null, ",", null, ")");
5655     q.push({i: n - 4, x: d3_interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3_interpolateNumber(ka[1], kb[1])});
5656   } else if (kb[0] != 1 || kb[1] != 1) {
5657     s.push(s.pop() + "scale(" + kb + ")");
5658   }
5659
5660   n = q.length;
5661   return function(t) {
5662     var i = -1, o;
5663     while (++i < n) s[(o = q[i]).i] = o.x(t);
5664     return s.join("");
5665   };
5666 }
5667
5668 d3_transitionPrototype.tween = function(name, tween) {
5669   var id = this.id, ns = this.namespace;
5670   if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
5671   return d3_selection_each(this, tween == null
5672         ? function(node) { node[ns][id].tween.remove(name); }
5673         : function(node) { node[ns][id].tween.set(name, tween); });
5674 };
5675
5676 function d3_transition_tween(groups, name, value, tween) {
5677   var id = groups.id, ns = groups.namespace;
5678   return d3_selection_each(groups, typeof value === "function"
5679       ? function(node, i, j) { node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); }
5680       : (value = tween(value), function(node) { node[ns][id].tween.set(name, value); }));
5681 }
5682
5683 d3_transitionPrototype.attr = function(nameNS, value) {
5684   if (arguments.length < 2) {
5685
5686     // For attr(object), the object specifies the names and values of the
5687     // attributes to transition. The values may be functions that are
5688     // evaluated for each element.
5689     for (value in nameNS) this.attr(value, nameNS[value]);
5690     return this;
5691   }
5692
5693   var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
5694       name = d3.ns.qualify(nameNS);
5695
5696   // For attr(string, null), remove the attribute with the specified name.
5697   function attrNull() {
5698     this.removeAttribute(name);
5699   }
5700   function attrNullNS() {
5701     this.removeAttributeNS(name.space, name.local);
5702   }
5703
5704   // For attr(string, string), set the attribute with the specified name.
5705   function attrTween(b) {
5706     return b == null ? attrNull : (b += "", function() {
5707       var a = this.getAttribute(name), i;
5708       return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
5709     });
5710   }
5711   function attrTweenNS(b) {
5712     return b == null ? attrNullNS : (b += "", function() {
5713       var a = this.getAttributeNS(name.space, name.local), i;
5714       return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
5715     });
5716   }
5717
5718   return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
5719 };
5720
5721 d3_transitionPrototype.attrTween = function(nameNS, tween) {
5722   var name = d3.ns.qualify(nameNS);
5723
5724   function attrTween(d, i) {
5725     var f = tween.call(this, d, i, this.getAttribute(name));
5726     return f && function(t) { this.setAttribute(name, f(t)); };
5727   }
5728   function attrTweenNS(d, i) {
5729     var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
5730     return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
5731   }
5732
5733   return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
5734 };
5735
5736 d3_transitionPrototype.style = function(name, value, priority) {
5737   var n = arguments.length;
5738   if (n < 3) {
5739
5740     // For style(object) or style(object, string), the object specifies the
5741     // names and values of the attributes to set or remove. The values may be
5742     // functions that are evaluated for each element. The optional string
5743     // specifies the priority.
5744     if (typeof name !== "string") {
5745       if (n < 2) value = "";
5746       for (priority in name) this.style(priority, name[priority], value);
5747       return this;
5748     }
5749
5750     // For style(string, string) or style(string, function), use the default
5751     // priority. The priority is ignored for style(string, null).
5752     priority = "";
5753   }
5754
5755   // For style(name, null) or style(name, null, priority), remove the style
5756   // property with the specified name. The priority is ignored.
5757   function styleNull() {
5758     this.style.removeProperty(name);
5759   }
5760
5761   // For style(name, string) or style(name, string, priority), set the style
5762   // property with the specified name, using the specified priority.
5763   // Otherwise, a name, value and priority are specified, and handled as below.
5764   function styleString(b) {
5765     return b == null ? styleNull : (b += "", function() {
5766       var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
5767       return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
5768     });
5769   }
5770
5771   return d3_transition_tween(this, "style." + name, value, styleString);
5772 };
5773
5774 d3_transitionPrototype.styleTween = function(name, tween, priority) {
5775   if (arguments.length < 3) priority = "";
5776
5777   function styleTween(d, i) {
5778     var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
5779     return f && function(t) { this.style.setProperty(name, f(t), priority); };
5780   }
5781
5782   return this.tween("style." + name, styleTween);
5783 };
5784
5785 d3_transitionPrototype.text = function(value) {
5786   return d3_transition_tween(this, "text", value, d3_transition_text);
5787 };
5788
5789 function d3_transition_text(b) {
5790   if (b == null) b = "";
5791   return function() { this.textContent = b; };
5792 }
5793
5794 d3_transitionPrototype.remove = function() {
5795   var ns = this.namespace;
5796   return this.each("end.transition", function() {
5797     var p;
5798     if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
5799   });
5800 };
5801
5802 var d3_ease_default = function() { return d3_identity; };
5803
5804 var d3_ease = d3.map({
5805   linear: d3_ease_default,
5806   poly: d3_ease_poly,
5807   quad: function() { return d3_ease_quad; },
5808   cubic: function() { return d3_ease_cubic; },
5809   sin: function() { return d3_ease_sin; },
5810   exp: function() { return d3_ease_exp; },
5811   circle: function() { return d3_ease_circle; },
5812   elastic: d3_ease_elastic,
5813   back: d3_ease_back,
5814   bounce: function() { return d3_ease_bounce; }
5815 });
5816
5817 var d3_ease_mode = d3.map({
5818   "in": d3_identity,
5819   "out": d3_ease_reverse,
5820   "in-out": d3_ease_reflect,
5821   "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); }
5822 });
5823
5824 d3.ease = function(name) {
5825   var i = name.indexOf("-"),
5826       t = i >= 0 ? name.slice(0, i) : name,
5827       m = i >= 0 ? name.slice(i + 1) : "in";
5828   t = d3_ease.get(t) || d3_ease_default;
5829   m = d3_ease_mode.get(m) || d3_identity;
5830   return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
5831 };
5832
5833 function d3_ease_clamp(f) {
5834   return function(t) {
5835     return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
5836   };
5837 }
5838
5839 function d3_ease_reverse(f) {
5840   return function(t) {
5841     return 1 - f(1 - t);
5842   };
5843 }
5844
5845 function d3_ease_reflect(f) {
5846   return function(t) {
5847     return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
5848   };
5849 }
5850
5851 function d3_ease_quad(t) {
5852   return t * t;
5853 }
5854
5855 function d3_ease_cubic(t) {
5856   return t * t * t;
5857 }
5858
5859 // Optimized clamp(reflect(poly(3))).
5860 function d3_ease_cubicInOut(t) {
5861   if (t <= 0) return 0;
5862   if (t >= 1) return 1;
5863   var t2 = t * t, t3 = t2 * t;
5864   return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
5865 }
5866
5867 function d3_ease_poly(e) {
5868   return function(t) {
5869     return Math.pow(t, e);
5870   };
5871 }
5872
5873 function d3_ease_sin(t) {
5874   return 1 - Math.cos(t * halfπ);
5875 }
5876
5877 function d3_ease_exp(t) {
5878   return Math.pow(2, 10 * (t - 1));
5879 }
5880
5881 function d3_ease_circle(t) {
5882   return 1 - Math.sqrt(1 - t * t);
5883 }
5884
5885 function d3_ease_elastic(a, p) {
5886   var s;
5887   if (arguments.length < 2) p = 0.45;
5888   if (arguments.length) s = p / τ * Math.asin(1 / a);
5889   else a = 1, s = p / 4;
5890   return function(t) {
5891     return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
5892   };
5893 }
5894
5895 function d3_ease_back(s) {
5896   if (!s) s = 1.70158;
5897   return function(t) {
5898     return t * t * ((s + 1) * t - s);
5899   };
5900 }
5901
5902 function d3_ease_bounce(t) {
5903   return t < 1 / 2.75 ? 7.5625 * t * t
5904       : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75
5905       : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375
5906       : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
5907 }
5908
5909 d3_transitionPrototype.ease = function(value) {
5910   var id = this.id, ns = this.namespace;
5911   if (arguments.length < 1) return this.node()[ns][id].ease;
5912   if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
5913   return d3_selection_each(this, function(node) { node[ns][id].ease = value; });
5914 };
5915
5916 d3_transitionPrototype.delay = function(value) {
5917   var id = this.id, ns = this.namespace;
5918   if (arguments.length < 1) return this.node()[ns][id].delay;
5919   return d3_selection_each(this, typeof value === "function"
5920       ? function(node, i, j) { node[ns][id].delay = +value.call(node, node.__data__, i, j); }
5921       : (value = +value, function(node) { node[ns][id].delay = value; }));
5922 };
5923
5924 d3_transitionPrototype.duration = function(value) {
5925   var id = this.id, ns = this.namespace;
5926   if (arguments.length < 1) return this.node()[ns][id].duration;
5927   return d3_selection_each(this, typeof value === "function"
5928       ? function(node, i, j) { node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); }
5929       : (value = Math.max(1, value), function(node) { node[ns][id].duration = value; }));
5930 };
5931
5932 d3_transitionPrototype.each = function(type, listener) {
5933   var id = this.id, ns = this.namespace;
5934   if (arguments.length < 2) {
5935     var inherit = d3_transitionInherit,
5936         inheritId = d3_transitionInheritId;
5937     try {
5938       d3_transitionInheritId = id;
5939       d3_selection_each(this, function(node, i, j) {
5940         d3_transitionInherit = node[ns][id];
5941         type.call(node, node.__data__, i, j);
5942       });
5943     } finally {
5944       d3_transitionInherit = inherit;
5945       d3_transitionInheritId = inheritId;
5946     }
5947   } else {
5948     d3_selection_each(this, function(node) {
5949       var transition = node[ns][id];
5950       (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
5951     });
5952   }
5953   return this;
5954 };
5955
5956 d3_transitionPrototype.transition = function() {
5957   var id0 = this.id,
5958       id1 = ++d3_transitionId,
5959       ns = this.namespace,
5960       subgroups = [],
5961       subgroup,
5962       group,
5963       node,
5964       transition;
5965
5966   for (var j = 0, m = this.length; j < m; j++) {
5967     subgroups.push(subgroup = []);
5968     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
5969       if (node = group[i]) {
5970         transition = node[ns][id0];
5971         d3_transitionNode(node, i, ns, id1, {time: transition.time, ease: transition.ease, delay: transition.delay + transition.duration, duration: transition.duration});
5972       }
5973       subgroup.push(node);
5974     }
5975   }
5976
5977   return d3_transition(subgroups, ns, id1);
5978 };
5979
5980 function d3_transitionNamespace(name) {
5981   return name == null ? "__transition__" : "__transition_" + name + "__";
5982 }
5983
5984 function d3_transitionNode(node, i, ns, id, inherit) {
5985   var lock = node[ns] || (node[ns] = {active: 0, count: 0}),
5986       transition = lock[id];
5987
5988   if (!transition) {
5989     var time = inherit.time;
5990
5991     transition = lock[id] = {
5992       tween: new d3_Map,
5993       time: time,
5994       delay: inherit.delay,
5995       duration: inherit.duration,
5996       ease: inherit.ease,
5997       index: i
5998     };
5999
6000     inherit = null; // allow gc
6001
6002     ++lock.count;
6003
6004     d3.timer(function(elapsed) {
6005       var delay = transition.delay,
6006           duration,
6007           ease,
6008           timer = d3_timer_active,
6009           tweened = [];
6010
6011       timer.t = delay + time;
6012       if (delay <= elapsed) return start(elapsed - delay);
6013       timer.c = start;
6014
6015       function start(elapsed) {
6016         if (lock.active > id) return stop();
6017
6018         var active = lock[lock.active];
6019         if (active) {
6020           --lock.count;
6021           delete lock[lock.active];
6022           active.event && active.event.interrupt.call(node, node.__data__, active.index);
6023         }
6024
6025         lock.active = id;
6026
6027         transition.event && transition.event.start.call(node, node.__data__, i);
6028
6029         transition.tween.forEach(function(key, value) {
6030           if (value = value.call(node, node.__data__, i)) {
6031             tweened.push(value);
6032           }
6033         });
6034
6035         // Deferred capture to allow tweens to initialize ease & duration.
6036         ease = transition.ease;
6037         duration = transition.duration;
6038
6039         d3.timer(function() { // defer to end of current frame
6040           timer.c = tick(elapsed || 1) ? d3_true : tick;
6041           return 1;
6042         }, 0, time);
6043       }
6044
6045       function tick(elapsed) {
6046         if (lock.active !== id) return 1;
6047
6048         var t = elapsed / duration,
6049             e = ease(t),
6050             n = tweened.length;
6051
6052         while (n > 0) {
6053           tweened[--n].call(node, e);
6054         }
6055
6056         if (t >= 1) {
6057           transition.event && transition.event.end.call(node, node.__data__, i);
6058           return stop();
6059         }
6060       }
6061
6062       function stop() {
6063         if (--lock.count) delete lock[id];
6064         else delete node[ns];
6065         return 1;
6066       }
6067     }, 0, time);
6068   }
6069 }
6070
6071 d3.xhr = d3_xhrType(d3_identity);
6072
6073 function d3_xhrType(response) {
6074   return function(url, mimeType, callback) {
6075     if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null;
6076     return d3_xhr(url, mimeType, response, callback);
6077   };
6078 }
6079
6080 function d3_xhr(url, mimeType, response, callback) {
6081   var xhr = {},
6082       dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
6083       headers = {},
6084       request = new XMLHttpRequest,
6085       responseType = null;
6086
6087   // If IE does not support CORS, use XDomainRequest.
6088   if (this.XDomainRequest
6089       && !("withCredentials" in request)
6090       && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest;
6091
6092   "onload" in request
6093       ? request.onload = request.onerror = respond
6094       : request.onreadystatechange = function() { request.readyState > 3 && respond(); };
6095
6096   function respond() {
6097     var status = request.status, result;
6098     if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
6099       try {
6100         result = response.call(xhr, request);
6101       } catch (e) {
6102         dispatch.error.call(xhr, e);
6103         return;
6104       }
6105       dispatch.load.call(xhr, result);
6106     } else {
6107       dispatch.error.call(xhr, request);
6108     }
6109   }
6110
6111   request.onprogress = function(event) {
6112     var o = d3.event;
6113     d3.event = event;
6114     try { dispatch.progress.call(xhr, request); }
6115     finally { d3.event = o; }
6116   };
6117
6118   xhr.header = function(name, value) {
6119     name = (name + "").toLowerCase();
6120     if (arguments.length < 2) return headers[name];
6121     if (value == null) delete headers[name];
6122     else headers[name] = value + "";
6123     return xhr;
6124   };
6125
6126   // If mimeType is non-null and no Accept header is set, a default is used.
6127   xhr.mimeType = function(value) {
6128     if (!arguments.length) return mimeType;
6129     mimeType = value == null ? null : value + "";
6130     return xhr;
6131   };
6132
6133   // Specifies what type the response value should take;
6134   // for instance, arraybuffer, blob, document, or text.
6135   xhr.responseType = function(value) {
6136     if (!arguments.length) return responseType;
6137     responseType = value;
6138     return xhr;
6139   };
6140
6141   // Specify how to convert the response content to a specific type;
6142   // changes the callback value on "load" events.
6143   xhr.response = function(value) {
6144     response = value;
6145     return xhr;
6146   };
6147
6148   // Convenience methods.
6149   ["get", "post"].forEach(function(method) {
6150     xhr[method] = function() {
6151       return xhr.send.apply(xhr, [method].concat(d3_array(arguments)));
6152     };
6153   });
6154
6155   // If callback is non-null, it will be used for error and load events.
6156   xhr.send = function(method, data, callback) {
6157     if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
6158     request.open(method, url, true);
6159     if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
6160     if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
6161     if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
6162     if (responseType != null) request.responseType = responseType;
6163     if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
6164     dispatch.beforesend.call(xhr, request);
6165     request.send(data == null ? null : data);
6166     return xhr;
6167   };
6168
6169   xhr.abort = function() {
6170     request.abort();
6171     return xhr;
6172   };
6173
6174   d3.rebind(xhr, dispatch, "on");
6175
6176   return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
6177 };
6178
6179 function d3_xhr_fixCallback(callback) {
6180   return callback.length === 1
6181       ? function(error, request) { callback(error == null ? request : null); }
6182       : callback;
6183 }
6184
6185 function d3_xhrHasResponse(request) {
6186   var type = request.responseType;
6187   return type && type !== "text"
6188       ? request.response // null on error
6189       : request.responseText; // "" on error
6190 }
6191
6192 d3.text = d3_xhrType(function(request) {
6193   return request.responseText;
6194 });
6195
6196 d3.json = function(url, callback) {
6197   return d3_xhr(url, "application/json", d3_json, callback);
6198 };
6199
6200 function d3_json(request) {
6201   return JSON.parse(request.responseText);
6202 }
6203
6204 d3.html = function(url, callback) {
6205   return d3_xhr(url, "text/html", d3_html, callback);
6206 };
6207
6208 function d3_html(request) {
6209   var range = d3_document.createRange();
6210   range.selectNode(d3_document.body);
6211   return range.createContextualFragment(request.responseText);
6212 }
6213
6214 d3.xml = d3_xhrType(function(request) {
6215   return request.responseXML;
6216 });
6217   if (typeof define === "function" && define.amd) define(d3);
6218   else if (typeof module === "object" && module.exports) module.exports = d3;
6219   this.d3 = d3;
6220 }();
6221 d3.combobox = function() {
6222     var event = d3.dispatch('accept'),
6223         data = [],
6224         suggestions = [],
6225         minItems = 2,
6226         caseSensitive = false;
6227
6228     var fetcher = function(val, cb) {
6229         cb(data.filter(function(d) {
6230             return d.value
6231                 .toString()
6232                 .toLowerCase()
6233                 .indexOf(val.toLowerCase()) !== -1;
6234         }));
6235     };
6236
6237     var combobox = function(input, attachTo) {
6238         var idx = -1,
6239             container = d3.select(document.body)
6240                 .selectAll('div.combobox')
6241                 .filter(function(d) { return d === input.node(); }),
6242             shown = !container.empty();
6243
6244         input
6245             .classed('combobox-input', true)
6246             .on('focus.typeahead', focus)
6247             .on('blur.typeahead', blur)
6248             .on('keydown.typeahead', keydown)
6249             .on('keyup.typeahead', keyup)
6250             .on('input.typeahead', change)
6251             .each(function() {
6252                 var parent = this.parentNode,
6253                     sibling = this.nextSibling;
6254
6255                 var caret = d3.select(parent).selectAll('.combobox-caret')
6256                     .filter(function(d) { return d === input.node(); })
6257                     .data([input.node()]);
6258
6259                 caret.enter().insert('div', function() { return sibling; })
6260                     .attr('class', 'combobox-caret');
6261
6262                 caret
6263                     .on('mousedown', function () {
6264                         // prevent the form element from blurring. it blurs
6265                         // on mousedown
6266                         d3.event.stopPropagation();
6267                         d3.event.preventDefault();
6268                         if (!shown) {
6269                             input.node().focus();
6270                             fetch('', render);
6271                         } else {
6272                             hide();
6273                         }
6274                     });
6275             });
6276
6277         function focus() {
6278             fetch(value(), render);
6279         }
6280
6281         function blur() {
6282             window.setTimeout(hide, 150);
6283         }
6284
6285         function show() {
6286             if (!shown) {
6287                 container = d3.select(document.body)
6288                     .insert('div', ':first-child')
6289                     .datum(input.node())
6290                     .attr('class', 'combobox')
6291                     .style({
6292                         position: 'absolute',
6293                         display: 'block',
6294                         left: '0px'
6295                     })
6296                     .on('mousedown', function () {
6297                         // prevent moving focus out of the text field
6298                         d3.event.preventDefault();
6299                     });
6300
6301                 d3.select(document.body)
6302                     .on('scroll.combobox', render, true);
6303
6304                 shown = true;
6305             }
6306         }
6307
6308         function hide() {
6309             if (shown) {
6310                 idx = -1;
6311                 container.remove();
6312
6313                 d3.select(document.body)
6314                     .on('scroll.combobox', null);
6315
6316                 shown = false;
6317             }
6318         }
6319
6320         function keydown() {
6321            switch (d3.event.keyCode) {
6322                // backspace, delete
6323                case 8:
6324                case 46:
6325                    input.on('input.typeahead', function() {
6326                        idx = -1;
6327                        render();
6328                        var start = input.property('selectionStart');
6329                        input.node().setSelectionRange(start, start);
6330                        input.on('input.typeahead', change);
6331                    });
6332                    break;
6333                // tab
6334                case 9:
6335                    container.selectAll('a.selected').each(event.accept);
6336                    break;
6337                // return
6338                case 13:
6339                    d3.event.preventDefault();
6340                    break;
6341                // up arrow
6342                case 38:
6343                    nav(-1);
6344                    d3.event.preventDefault();
6345                    break;
6346                // down arrow
6347                case 40:
6348                    nav(+1);
6349                    d3.event.preventDefault();
6350                    break;
6351            }
6352            d3.event.stopPropagation();
6353         }
6354
6355         function keyup() {
6356             switch (d3.event.keyCode) {
6357                 // escape
6358                 case 27:
6359                     hide();
6360                     break;
6361                 // return
6362                 case 13:
6363                     container.selectAll('a.selected').each(event.accept);
6364                     hide();
6365                     break;
6366             }
6367         }
6368
6369         function change() {
6370             fetch(value(), function() {
6371                 if (input.property('selectionEnd') === input.property('value').length) {
6372                     autocomplete();
6373                 }
6374                 render();
6375             });
6376         }
6377
6378         function nav(dir) {
6379             if (!suggestions.length) return;
6380             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6381             input.property('value', suggestions[idx].value);
6382             render();
6383             ensureVisible();
6384         }
6385
6386         function value() {
6387             var value = input.property('value'),
6388                 start = input.property('selectionStart'),
6389                 end = input.property('selectionEnd');
6390
6391             if (start && end) {
6392                 value = value.substring(0, start);
6393             }
6394
6395             return value;
6396         }
6397
6398         function fetch(v, cb) {
6399             fetcher.call(input, v, function(_) {
6400                 suggestions = _;
6401                 cb();
6402             });
6403         }
6404
6405         function autocomplete() {
6406             var v = caseSensitive ? value() : value().toLowerCase();
6407             idx = -1;
6408             if (!v) return;
6409
6410             for (var i = 0; i < suggestions.length; i++) {
6411                 var suggestion = suggestions[i].value,
6412                     compare = caseSensitive ? suggestion : suggestion.toLowerCase();
6413
6414                 if (compare.indexOf(v) === 0) {
6415                     idx = i;
6416                     input.property('value', suggestion);
6417                     input.node().setSelectionRange(v.length, suggestion.length);
6418                     return;
6419                 }
6420             }
6421         }
6422
6423         function render() {
6424             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6425                 show();
6426             } else {
6427                 hide();
6428                 return;
6429             }
6430
6431             var options = container
6432                 .selectAll('a.combobox-option')
6433                 .data(suggestions, function(d) { return d.value; });
6434
6435             options.enter().append('a')
6436                 .attr('class', 'combobox-option')
6437                 .text(function(d) { return d.value; });
6438
6439             options
6440                 .attr('title', function(d) { return d.title; })
6441                 .classed('selected', function(d, i) { return i == idx; })
6442                 .on('mouseover', select)
6443                 .on('click', accept)
6444                 .order();
6445
6446             options.exit()
6447                 .remove();
6448
6449             var node = attachTo ? attachTo.node() : input.node(),
6450                 rect = node.getBoundingClientRect();
6451
6452             container.style({
6453                 'left': rect.left + 'px',
6454                 'width': rect.width + 'px',
6455                 'top': rect.height + rect.top + 'px'
6456             });
6457         }
6458
6459         function select(d, i) {
6460             idx = i;
6461             render();
6462         }
6463
6464         function ensureVisible() {
6465             var node = container.selectAll('a.selected').node();
6466             if (node) node.scrollIntoView();
6467         }
6468
6469         function accept(d) {
6470             if (!shown) return;
6471             input
6472                 .property('value', d.value)
6473                 .trigger('change');
6474             event.accept(d);
6475             hide();
6476         }
6477     };
6478
6479     combobox.fetcher = function(_) {
6480         if (!arguments.length) return fetcher;
6481         fetcher = _;
6482         return combobox;
6483     };
6484
6485     combobox.data = function(_) {
6486         if (!arguments.length) return data;
6487         data = _;
6488         return combobox;
6489     };
6490
6491     combobox.minItems = function(_) {
6492         if (!arguments.length) return minItems;
6493         minItems = _;
6494         return combobox;
6495     };
6496
6497     combobox.caseSensitive = function(_) {
6498         if (!arguments.length) return caseSensitive;
6499         caseSensitive = _;
6500         return combobox;
6501     };
6502
6503     return d3.rebind(combobox, event, 'on');
6504 };
6505
6506 d3.combobox.off = function(input) {
6507     data = null;
6508     fetcher = null;
6509
6510     input
6511         .on('focus.typeahead', null)
6512         .on('blur.typeahead', null)
6513         .on('keydown.typeahead', null)
6514         .on('keyup.typeahead', null)
6515         .on('input.typeahead', null)
6516         .each(function() {
6517             d3.select(this.parentNode).selectAll('.combobox-caret')
6518                 .filter(function(d) { return d === input.node(); })
6519                 .on('mousedown', null);
6520         });
6521
6522     d3.select(document.body)
6523         .on('scroll.combobox', null);
6524 };
6525 d3.geo.tile = function() {
6526   var size = [960, 500],
6527       scale = 256,
6528       scaleExtent = [0, 20],
6529       translate = [size[0] / 2, size[1] / 2],
6530       zoomDelta = 0;
6531
6532   function bound(_) {
6533       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6534   }
6535
6536   function tile() {
6537     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6538         z0 = bound(Math.round(z + zoomDelta)),
6539         k = Math.pow(2, z - z0 + 8),
6540         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6541         tiles = [],
6542         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6543         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6544
6545     rows.forEach(function(y) {
6546       cols.forEach(function(x) {
6547         tiles.push([x, y, z0]);
6548       });
6549     });
6550
6551     tiles.translate = origin;
6552     tiles.scale = k;
6553
6554     return tiles;
6555   }
6556
6557   tile.scaleExtent = function(_) {
6558     if (!arguments.length) return scaleExtent;
6559     scaleExtent = _;
6560     return tile;
6561   };
6562
6563   tile.size = function(_) {
6564     if (!arguments.length) return size;
6565     size = _;
6566     return tile;
6567   };
6568
6569   tile.scale = function(_) {
6570     if (!arguments.length) return scale;
6571     scale = _;
6572     return tile;
6573   };
6574
6575   tile.translate = function(_) {
6576     if (!arguments.length) return translate;
6577     translate = _;
6578     return tile;
6579   };
6580
6581   tile.zoomDelta = function(_) {
6582     if (!arguments.length) return zoomDelta;
6583     zoomDelta = +_;
6584     return tile;
6585   };
6586
6587   return tile;
6588 };
6589 d3.jsonp = function (url, callback) {
6590   function rand() {
6591     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6592       c = '', i = -1;
6593     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6594     return c;
6595   }
6596
6597   function create(url) {
6598     var e = url.match(/callback=d3.jsonp.(\w+)/),
6599       c = e ? e[1] : rand();
6600     d3.jsonp[c] = function(data) {
6601       callback(data);
6602       delete d3.jsonp[c];
6603       script.remove();
6604     };
6605     return 'd3.jsonp.' + c;
6606   }
6607
6608   var cb = create(url),
6609     script = d3.select('head')
6610     .append('script')
6611     .attr('type', 'text/javascript')
6612     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6613 };
6614 /*
6615  * This code is licensed under the MIT license.
6616  *
6617  * Copyright © 2013, iD authors.
6618  *
6619  * Portions copyright © 2011, Keith Cirkel
6620  * See https://github.com/keithamus/jwerty
6621  *
6622  */
6623 d3.keybinding = function(namespace) {
6624     var bindings = [];
6625
6626     function matches(binding, event) {
6627         for (var p in binding.event) {
6628             if (event[p] != binding.event[p])
6629                 return false;
6630         }
6631         return true;
6632     }
6633
6634     function testBindings(isCapturing) {
6635         for (var i = 0; i < bindings.length; i++) {
6636             var binding = bindings[i];
6637
6638             if (!!binding.capture === isCapturing && matches(binding, d3.event)) {
6639                 binding.callback();
6640             }
6641         }
6642     }
6643
6644     function capture() {
6645         testBindings(true);
6646     }
6647
6648     function bubble() {
6649         var tagName = d3.select(d3.event.target).node().tagName;
6650         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6651             return;
6652         }
6653         testBindings(false);
6654     }
6655
6656     function keybinding(selection) {
6657         selection = selection || d3.select(document);
6658         selection.on('keydown.capture' + namespace, capture, true);
6659         selection.on('keydown.bubble' + namespace, bubble, false);
6660         return keybinding;
6661     }
6662
6663     keybinding.off = function(selection) {
6664         bindings = [];
6665         selection = selection || d3.select(document);
6666         selection.on('keydown.capture' + namespace, null);
6667         selection.on('keydown.bubble' + namespace, null);
6668         return keybinding;
6669     };
6670
6671     keybinding.on = function(code, callback, capture) {
6672         var binding = {
6673             event: {
6674                 keyCode: 0,
6675                 shiftKey: false,
6676                 ctrlKey: false,
6677                 altKey: false,
6678                 metaKey: false
6679             },
6680             capture: capture,
6681             callback: callback
6682         };
6683
6684         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6685
6686         for (var i = 0; i < code.length; i++) {
6687             // Normalise matching errors
6688             if (code[i] === '++') code[i] = '+';
6689
6690             if (code[i] in d3.keybinding.modifierCodes) {
6691                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6692             } else if (code[i] in d3.keybinding.keyCodes) {
6693                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6694             }
6695         }
6696
6697         bindings.push(binding);
6698
6699         return keybinding;
6700     };
6701
6702     return keybinding;
6703 };
6704
6705 (function () {
6706     d3.keybinding.modifierCodes = {
6707         // Shift key, ⇧
6708         '⇧': 16, shift: 16,
6709         // CTRL key, on Mac: ⌃
6710         '⌃': 17, ctrl: 17,
6711         // ALT key, on Mac: ⌥ (Alt)
6712         '⌥': 18, alt: 18, option: 18,
6713         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6714         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6715     };
6716
6717     d3.keybinding.modifierProperties = {
6718         16: 'shiftKey',
6719         17: 'ctrlKey',
6720         18: 'altKey',
6721         91: 'metaKey'
6722     };
6723
6724     d3.keybinding.keyCodes = {
6725         // Backspace key, on Mac: ⌫ (Backspace)
6726         '⌫': 8, backspace: 8,
6727         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6728         '⇥': 9, '⇆': 9, tab: 9,
6729         // Return key, ↩
6730         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6731         // Pause/Break key
6732         'pause': 19, 'pause-break': 19,
6733         // Caps Lock key, ⇪
6734         '⇪': 20, caps: 20, 'caps-lock': 20,
6735         // Escape key, on Mac: ⎋, on Windows: Esc
6736         '⎋': 27, escape: 27, esc: 27,
6737         // Space key
6738         space: 32,
6739         // Page-Up key, or pgup, on Mac: ↖
6740         '↖': 33, pgup: 33, 'page-up': 33,
6741         // Page-Down key, or pgdown, on Mac: ↘
6742         '↘': 34, pgdown: 34, 'page-down': 34,
6743         // END key, on Mac: ⇟
6744         '⇟': 35, end: 35,
6745         // HOME key, on Mac: ⇞
6746         '⇞': 36, home: 36,
6747         // Insert key, or ins
6748         ins: 45, insert: 45,
6749         // Delete key, on Mac: ⌦ (Delete)
6750         '⌦': 46, del: 46, 'delete': 46,
6751         // Left Arrow Key, or ←
6752         '←': 37, left: 37, 'arrow-left': 37,
6753         // Up Arrow Key, or ↑
6754         '↑': 38, up: 38, 'arrow-up': 38,
6755         // Right Arrow Key, or →
6756         '→': 39, right: 39, 'arrow-right': 39,
6757         // Up Arrow Key, or ↓
6758         '↓': 40, down: 40, 'arrow-down': 40,
6759         // odities, printing characters that come out wrong:
6760         // Firefox Equals
6761         'ffequals': 61,
6762         // Num-Multiply, or *
6763         '*': 106, star: 106, asterisk: 106, multiply: 106,
6764         // Num-Plus or +
6765         '+': 107, 'plus': 107,
6766         // Num-Subtract, or -
6767         '-': 109, subtract: 109,
6768         // Firefox Plus
6769         'ffplus': 171,
6770         // Firefox Minus
6771         'ffminus': 173,
6772         // Semicolon
6773         ';': 186, semicolon: 186,
6774         // = or equals
6775         '=': 187, 'equals': 187,
6776         // Comma, or ,
6777         ',': 188, comma: 188,
6778         'dash': 189, //???
6779         // Period, or ., or full-stop
6780         '.': 190, period: 190, 'full-stop': 190,
6781         // Slash, or /, or forward-slash
6782         '/': 191, slash: 191, 'forward-slash': 191,
6783         // Tick, or `, or back-quote
6784         '`': 192, tick: 192, 'back-quote': 192,
6785         // Open bracket, or [
6786         '[': 219, 'open-bracket': 219,
6787         // Back slash, or \
6788         '\\': 220, 'back-slash': 220,
6789         // Close backet, or ]
6790         ']': 221, 'close-bracket': 221,
6791         // Apostrophe, or Quote, or '
6792         '\'': 222, quote: 222, apostrophe: 222
6793     };
6794
6795     // NUMPAD 0-9
6796     var i = 95, n = 0;
6797     while (++i < 106) {
6798         d3.keybinding.keyCodes['num-' + n] = i;
6799         ++n;
6800     }
6801
6802     // 0-9
6803     i = 47; n = 0;
6804     while (++i < 58) {
6805         d3.keybinding.keyCodes[n] = i;
6806         ++n;
6807     }
6808
6809     // F1-F25
6810     i = 111; n = 1;
6811     while (++i < 136) {
6812         d3.keybinding.keyCodes['f' + n] = i;
6813         ++n;
6814     }
6815
6816     // a-z
6817     i = 64;
6818     while (++i < 91) {
6819         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6820     }
6821 })();
6822 d3.selection.prototype.one = function (type, listener, capture) {
6823     var target = this, typeOnce = type + ".once";
6824     function one() {
6825         target.on(typeOnce, null);
6826         listener.apply(this, arguments);
6827     }
6828     target.on(typeOnce, one, capture);
6829     return this;
6830 };
6831 d3.selection.prototype.dimensions = function (dimensions) {
6832     var refresh = (function(node) {
6833         var cr = node.getBoundingClientRect();
6834         prop = [cr.width, cr.height];
6835         this.property('__dimensions__', prop);
6836         return prop;
6837     }).bind(this);
6838
6839     var node = this.node();
6840
6841     if (!arguments.length) {
6842         if (!node) return [0,0];
6843         return this.property('__dimensions__') || refresh(node);
6844     }
6845     if (dimensions === null) {
6846         if (!node) return [0,0];
6847         return refresh(node);
6848     }
6849
6850     return this
6851         .property('__dimensions__', [dimensions[0], dimensions[1]])
6852         .attr({width: dimensions[0], height: dimensions[1]});
6853 };
6854 d3.selection.prototype.trigger = function (type) {
6855     this.each(function() {
6856         var evt = document.createEvent('HTMLEvents');
6857         evt.initEvent(type, true, true);
6858         this.dispatchEvent(evt);
6859     });
6860 };
6861 // Tooltips and svg mask used to highlight certain features
6862 d3.curtain = function() {
6863
6864     var event = d3.dispatch(),
6865         surface,
6866         tooltip,
6867         darkness;
6868
6869     function curtain(selection) {
6870
6871         surface = selection.append('svg')
6872             .attr('id', 'curtain')
6873             .style({
6874                 'z-index': 1000,
6875                 'pointer-events': 'none',
6876                 'position': 'absolute',
6877                 'top': 0,
6878                 'left': 0
6879             });
6880
6881         darkness = surface.append('path')
6882             .attr({
6883                 x: 0,
6884                 y: 0,
6885                 'class': 'curtain-darkness'
6886             });
6887
6888         d3.select(window).on('resize.curtain', resize);
6889
6890         tooltip = selection.append('div')
6891             .attr('class', 'tooltip')
6892             .style('z-index', 1002);
6893
6894         tooltip.append('div').attr('class', 'tooltip-arrow');
6895         tooltip.append('div').attr('class', 'tooltip-inner');
6896
6897         resize();
6898
6899         function resize() {
6900             surface.attr({
6901                 width: window.innerWidth,
6902                 height: window.innerHeight
6903             });
6904             curtain.cut(darkness.datum());
6905         }
6906     }
6907
6908     curtain.reveal = function(box, text, tooltipclass, duration) {
6909         if (typeof box === 'string') box = d3.select(box).node();
6910         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6911
6912         curtain.cut(box, duration);
6913
6914         if (text) {
6915             // pseudo markdown bold text hack
6916             var parts = text.split('**');
6917             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6918             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6919
6920             var dimensions = tooltip.classed('in', true)
6921                 .select('.tooltip-inner')
6922                     .html(html)
6923                     .dimensions();
6924
6925             var pos;
6926
6927             var w = window.innerWidth,
6928                 h = window.innerHeight;
6929
6930             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6931                 side = 'bottom';
6932                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6933
6934             } else if (box.left + box.width + 300 < window.innerWidth) {
6935                 side = 'right';
6936                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6937
6938             } else if (box.left > 300) {
6939                 side = 'left';
6940                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6941             } else {
6942                 side = 'bottom';
6943                 pos = [box.left, box.top + box.height];
6944             }
6945
6946             pos = [
6947                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6948                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6949             ];
6950
6951
6952             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6953
6954             tooltip
6955                 .style('top', pos[1] + 'px')
6956                 .style('left', pos[0] + 'px')
6957                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6958                 .select('.tooltip-inner')
6959                     .html(html);
6960
6961         } else {
6962             tooltip.call(iD.ui.Toggle(false));
6963         }
6964     };
6965
6966     curtain.cut = function(datum, duration) {
6967         darkness.datum(datum);
6968
6969         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6970             .attr('d', function(d) {
6971                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6972                     window.innerWidth + "," + window.innerHeight + "L" +
6973                     window.innerWidth + ",0 Z";
6974
6975                 if (!d) return string;
6976                 return string + 'M' +
6977                     d.left + ',' + d.top + 'L' +
6978                     d.left + ',' + (d.top + d.height) + 'L' +
6979                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6980                     (d.left + d.width) + ',' + (d.top) + 'Z';
6981
6982             });
6983     };
6984
6985     curtain.remove = function() {
6986         surface.remove();
6987         tooltip.remove();
6988     };
6989
6990     return d3.rebind(curtain, event, 'on');
6991 };
6992 // Like selection.property('value', ...), but avoids no-op value sets,
6993 // which can result in layout/repaint thrashing in some situations.
6994 d3.selection.prototype.value = function(value) {
6995     function d3_selection_value(value) {
6996       function valueNull() {
6997         delete this.value;
6998       }
6999
7000       function valueConstant() {
7001         if (this.value !== value) this.value = value;
7002       }
7003
7004       function valueFunction() {
7005         var x = value.apply(this, arguments);
7006         if (x == null) delete this.value;
7007         else if (this.value !== x) this.value = x;
7008       }
7009
7010       return value == null
7011           ? valueNull : (typeof value === "function"
7012           ? valueFunction : valueConstant);
7013     }
7014
7015     if (!arguments.length) return this.property('value');
7016     return this.each(d3_selection_value(value));
7017 };
7018 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7019 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7020 //
7021 // Permission is hereby granted, free of charge, to any person
7022 // obtaining a copy of this software and associated documentation files
7023 // (the "Software"), to deal in the Software without restriction,
7024 // including without limitation the rights to use, copy, modify, merge,
7025 // publish, distribute, sublicense, and/or sell copies of the Software,
7026 // and to permit persons to whom the Software is furnished to do so,
7027 // subject to the following conditions:
7028 //
7029 // The above copyright notice and this permission notice shall be
7030 // included in all copies or substantial portions of the Software.
7031 //
7032 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7033 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7034 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7035 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7036 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7037 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7038 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7039 // SOFTWARE.
7040
7041 // source:  https://bitbucket.org/lshift/synchrotron/src
7042
7043 Diff3 = (function() {
7044     'use strict';
7045
7046     var diff3 = {
7047         longest_common_subsequence: function(file1, file2) {
7048             /* Text diff algorithm following Hunt and McIlroy 1976.
7049              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7050              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7051              * http://www.cs.dartmouth.edu/~doug/
7052              *
7053              * Expects two arrays of strings.
7054              */
7055             var equivalenceClasses;
7056             var file2indices;
7057             var newCandidate;
7058             var candidates;
7059             var line;
7060             var c, i, j, jX, r, s;
7061
7062             equivalenceClasses = {};
7063             for (j = 0; j < file2.length; j++) {
7064                 line = file2[j];
7065                 if (equivalenceClasses[line]) {
7066                     equivalenceClasses[line].push(j);
7067                 } else {
7068                     equivalenceClasses[line] = [j];
7069                 }
7070             }
7071
7072             candidates = [{file1index: -1,
7073                            file2index: -1,
7074                            chain: null}];
7075
7076             for (i = 0; i < file1.length; i++) {
7077                 line = file1[i];
7078                 file2indices = equivalenceClasses[line] || [];
7079
7080                 r = 0;
7081                 c = candidates[0];
7082
7083                 for (jX = 0; jX < file2indices.length; jX++) {
7084                     j = file2indices[jX];
7085
7086                     for (s = 0; s < candidates.length; s++) {
7087                         if ((candidates[s].file2index < j) &&
7088                             ((s == candidates.length - 1) ||
7089                              (candidates[s + 1].file2index > j)))
7090                             break;
7091                     }
7092
7093                     if (s < candidates.length) {
7094                         newCandidate = {file1index: i,
7095                                         file2index: j,
7096                                         chain: candidates[s]};
7097                         if (r == candidates.length) {
7098                             candidates.push(c);
7099                         } else {
7100                             candidates[r] = c;
7101                         }
7102                         r = s + 1;
7103                         c = newCandidate;
7104                         if (r == candidates.length) {
7105                             break; // no point in examining further (j)s
7106                         }
7107                     }
7108                 }
7109
7110                 candidates[r] = c;
7111             }
7112
7113             // At this point, we know the LCS: it's in the reverse of the
7114             // linked-list through .chain of
7115             // candidates[candidates.length - 1].
7116
7117             return candidates[candidates.length - 1];
7118         },
7119
7120         diff_comm: function(file1, file2) {
7121             // We apply the LCS to build a "comm"-style picture of the
7122             // differences between file1 and file2.
7123
7124             var result = [];
7125             var tail1 = file1.length;
7126             var tail2 = file2.length;
7127             var common = {common: []};
7128
7129             function processCommon() {
7130                 if (common.common.length) {
7131                     common.common.reverse();
7132                     result.push(common);
7133                     common = {common: []};
7134                 }
7135             }
7136
7137             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7138                  candidate !== null;
7139                  candidate = candidate.chain)
7140             {
7141                 var different = {file1: [], file2: []};
7142
7143                 while (--tail1 > candidate.file1index) {
7144                     different.file1.push(file1[tail1]);
7145                 }
7146
7147                 while (--tail2 > candidate.file2index) {
7148                     different.file2.push(file2[tail2]);
7149                 }
7150
7151                 if (different.file1.length || different.file2.length) {
7152                     processCommon();
7153                     different.file1.reverse();
7154                     different.file2.reverse();
7155                     result.push(different);
7156                 }
7157
7158                 if (tail1 >= 0) {
7159                     common.common.push(file1[tail1]);
7160                 }
7161             }
7162
7163             processCommon();
7164
7165             result.reverse();
7166             return result;
7167         },
7168
7169         diff_patch: function(file1, file2) {
7170             // We apply the LCD to build a JSON representation of a
7171             // diff(1)-style patch.
7172
7173             var result = [];
7174             var tail1 = file1.length;
7175             var tail2 = file2.length;
7176
7177             function chunkDescription(file, offset, length) {
7178                 var chunk = [];
7179                 for (var i = 0; i < length; i++) {
7180                     chunk.push(file[offset + i]);
7181                 }
7182                 return {offset: offset,
7183                         length: length,
7184                         chunk: chunk};
7185             }
7186
7187             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7188                  candidate !== null;
7189                  candidate = candidate.chain)
7190             {
7191                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7192                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7193                 tail1 = candidate.file1index;
7194                 tail2 = candidate.file2index;
7195
7196                 if (mismatchLength1 || mismatchLength2) {
7197                     result.push({file1: chunkDescription(file1,
7198                                                          candidate.file1index + 1,
7199                                                          mismatchLength1),
7200                                  file2: chunkDescription(file2,
7201                                                          candidate.file2index + 1,
7202                                                          mismatchLength2)});
7203                 }
7204             }
7205
7206             result.reverse();
7207             return result;
7208         },
7209
7210         strip_patch: function(patch) {
7211         // Takes the output of Diff3.diff_patch(), and removes
7212         // information from it. It can still be used by patch(),
7213         // below, but can no longer be inverted.
7214         var newpatch = [];
7215         for (var i = 0; i < patch.length; i++) {
7216             var chunk = patch[i];
7217             newpatch.push({file1: {offset: chunk.file1.offset,
7218                        length: chunk.file1.length},
7219                    file2: {chunk: chunk.file2.chunk}});
7220         }
7221         return newpatch;
7222         },
7223
7224         invert_patch: function(patch) {
7225             // Takes the output of Diff3.diff_patch(), and inverts the
7226             // sense of it, so that it can be applied to file2 to give
7227             // file1 rather than the other way around.
7228
7229             for (var i = 0; i < patch.length; i++) {
7230                 var chunk = patch[i];
7231                 var tmp = chunk.file1;
7232                 chunk.file1 = chunk.file2;
7233                 chunk.file2 = tmp;
7234             }
7235         },
7236
7237         patch: function (file, patch) {
7238             // Applies a patch to a file.
7239             //
7240             // Given file1 and file2, Diff3.patch(file1,
7241             // Diff3.diff_patch(file1, file2)) should give file2.
7242
7243             var result = [];
7244             var commonOffset = 0;
7245
7246             function copyCommon(targetOffset) {
7247                 while (commonOffset < targetOffset) {
7248                     result.push(file[commonOffset]);
7249                     commonOffset++;
7250                 }
7251             }
7252
7253             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7254                 var chunk = patch[chunkIndex];
7255                 copyCommon(chunk.file1.offset);
7256                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7257                     result.push(chunk.file2.chunk[lineIndex]);
7258                 }
7259                 commonOffset += chunk.file1.length;
7260             }
7261
7262             copyCommon(file.length);
7263             return result;
7264         },
7265
7266         diff_indices: function(file1, file2) {
7267             // We apply the LCS to give a simple representation of the
7268             // offsets and lengths of mismatched chunks in the input
7269             // files. This is used by diff3_merge_indices below.
7270
7271             var result = [];
7272             var tail1 = file1.length;
7273             var tail2 = file2.length;
7274
7275             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7276                  candidate !== null;
7277                  candidate = candidate.chain)
7278             {
7279                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7280                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7281                 tail1 = candidate.file1index;
7282                 tail2 = candidate.file2index;
7283
7284                 if (mismatchLength1 || mismatchLength2) {
7285                     result.push({file1: [tail1 + 1, mismatchLength1],
7286                                  file2: [tail2 + 1, mismatchLength2]});
7287                 }
7288             }
7289
7290             result.reverse();
7291             return result;
7292         },
7293
7294         diff3_merge_indices: function (a, o, b) {
7295             // Given three files, A, O, and B, where both A and B are
7296             // independently derived from O, returns a fairly complicated
7297             // internal representation of merge decisions it's taken. The
7298             // interested reader may wish to consult
7299             //
7300             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7301             // Formal Investigation of Diff3." In Arvind and Prasad,
7302             // editors, Foundations of Software Technology and Theoretical
7303             // Computer Science (FSTTCS), December 2007.
7304             //
7305             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7306             var i;
7307
7308             var m1 = Diff3.diff_indices(o, a);
7309             var m2 = Diff3.diff_indices(o, b);
7310
7311             var hunks = [];
7312             function addHunk(h, side) {
7313                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7314             }
7315             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7316             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7317             hunks.sort();
7318
7319             var result = [];
7320             var commonOffset = 0;
7321             function copyCommon(targetOffset) {
7322                 if (targetOffset > commonOffset) {
7323                     result.push([1, commonOffset, targetOffset - commonOffset]);
7324                     commonOffset = targetOffset;
7325                 }
7326             }
7327
7328             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7329                 var firstHunkIndex = hunkIndex;
7330                 var hunk = hunks[hunkIndex];
7331                 var regionLhs = hunk[0];
7332                 var regionRhs = regionLhs + hunk[2];
7333                 while (hunkIndex < hunks.length - 1) {
7334                     var maybeOverlapping = hunks[hunkIndex + 1];
7335                     var maybeLhs = maybeOverlapping[0];
7336                     if (maybeLhs > regionRhs) break;
7337                     regionRhs = maybeLhs + maybeOverlapping[2];
7338                     hunkIndex++;
7339                 }
7340
7341                 copyCommon(regionLhs);
7342                 if (firstHunkIndex == hunkIndex) {
7343             // The "overlap" was only one hunk long, meaning that
7344             // there's no conflict here. Either a and o were the
7345             // same, or b and o were the same.
7346                     if (hunk[4] > 0) {
7347                         result.push([hunk[1], hunk[3], hunk[4]]);
7348                     }
7349                 } else {
7350             // A proper conflict. Determine the extents of the
7351             // regions involved from a, o and b. Effectively merge
7352             // all the hunks on the left into one giant hunk, and
7353             // do the same for the right; then, correct for skew
7354             // in the regions of o that each side changed, and
7355             // report appropriate spans for the three sides.
7356             var regions = {
7357                 0: [a.length, -1, o.length, -1],
7358                 2: [b.length, -1, o.length, -1]
7359             };
7360                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7361                 hunk = hunks[i];
7362                         var side = hunk[1];
7363                 var r = regions[side];
7364                 var oLhs = hunk[0];
7365                 var oRhs = oLhs + hunk[2];
7366                         var abLhs = hunk[3];
7367                         var abRhs = abLhs + hunk[4];
7368                 r[0] = Math.min(abLhs, r[0]);
7369                 r[1] = Math.max(abRhs, r[1]);
7370                 r[2] = Math.min(oLhs, r[2]);
7371                 r[3] = Math.max(oRhs, r[3]);
7372                     }
7373             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7374             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7375             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7376             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7377                     result.push([-1,
7378                      aLhs,      aRhs      - aLhs,
7379                      regionLhs, regionRhs - regionLhs,
7380                      bLhs,      bRhs      - bLhs]);
7381                 }
7382                 commonOffset = regionRhs;
7383             }
7384
7385             copyCommon(o.length);
7386             return result;
7387         },
7388
7389         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7390             // Applies the output of Diff3.diff3_merge_indices to actually
7391             // construct the merged file; the returned result alternates
7392             // between "ok" and "conflict" blocks.
7393
7394             var result = [];
7395             var files = [a, o, b];
7396             var indices = Diff3.diff3_merge_indices(a, o, b);
7397
7398             var okLines = [];
7399             function flushOk() {
7400                 if (okLines.length) {
7401                     result.push({ok: okLines});
7402                 }
7403                 okLines = [];
7404             }
7405             function pushOk(xs) {
7406                 for (var j = 0; j < xs.length; j++) {
7407                     okLines.push(xs[j]);
7408                 }
7409             }
7410
7411             function isTrueConflict(rec) {
7412                 if (rec[2] != rec[6]) return true;
7413                 var aoff = rec[1];
7414                 var boff = rec[5];
7415                 for (var j = 0; j < rec[2]; j++) {
7416                     if (a[j + aoff] != b[j + boff]) return true;
7417                 }
7418                 return false;
7419             }
7420
7421             for (var i = 0; i < indices.length; i++) {
7422                 var x = indices[i];
7423                 var side = x[0];
7424                 if (side == -1) {
7425                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7426                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7427                     } else {
7428                         flushOk();
7429                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7430                                                 aIndex: x[1],
7431                                                 o: o.slice(x[3], x[3] + x[4]),
7432                                                 oIndex: x[3],
7433                                                 b: b.slice(x[5], x[5] + x[6]),
7434                                                 bIndex: x[5]}});
7435                     }
7436                 } else {
7437                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7438                 }
7439             }
7440
7441             flushOk();
7442             return result;
7443         }
7444     };
7445     return diff3;
7446 })();
7447
7448 if (typeof module !== 'undefined') module.exports = Diff3;
7449 var JXON = new (function () {
7450   var
7451     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7452     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7453
7454   function parseText (sValue) {
7455     if (rIsNull.test(sValue)) { return null; }
7456     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7457     if (isFinite(sValue)) { return parseFloat(sValue); }
7458     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7459     return sValue;
7460   }
7461
7462   function EmptyTree () { }
7463   EmptyTree.prototype.toString = function () { return "null"; };
7464   EmptyTree.prototype.valueOf = function () { return null; };
7465
7466   function objectify (vValue) {
7467     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7468   }
7469
7470   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7471     var
7472       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7473       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7474
7475     var
7476       sProp, vContent, nLength = 0, sCollectedTxt = "",
7477       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7478
7479     if (bChildren) {
7480       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7481         oNode = oParentNode.childNodes.item(nItem);
7482         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7483         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7484         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7485       }
7486     }
7487
7488     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7489
7490     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7491
7492     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7493       sProp = aCache[nElId].nodeName.toLowerCase();
7494       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7495       if (vResult.hasOwnProperty(sProp)) {
7496         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7497         vResult[sProp].push(vContent);
7498       } else {
7499         vResult[sProp] = vContent;
7500         nLength++;
7501       }
7502     }
7503
7504     if (bAttributes) {
7505       var
7506         nAttrLen = oParentNode.attributes.length,
7507         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7508
7509       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7510         oAttrib = oParentNode.attributes.item(nAttrib);
7511         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7512       }
7513
7514       if (bNesteAttr) {
7515         if (bFreeze) { Object.freeze(oAttrParent); }
7516         vResult[sAttributesProp] = oAttrParent;
7517         nLength -= nAttrLen - 1;
7518       }
7519     }
7520
7521     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7522       vResult[sValueProp] = vBuiltVal;
7523     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7524       vResult = vBuiltVal;
7525     }
7526
7527     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7528
7529     aCache.length = nLevelStart;
7530
7531     return vResult;
7532   }
7533
7534   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7535     var vValue, oChild;
7536
7537     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7538       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7539     } else if (oParentObj.constructor === Date) {
7540       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7541     }
7542
7543     for (var sName in oParentObj) {
7544       vValue = oParentObj[sName];
7545       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7546       if (sName === sValueProp) {
7547         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7548       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7549         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7550       } else if (sName.charAt(0) === sAttrPref) {
7551         oParentEl.setAttribute(sName.slice(1), vValue);
7552       } else if (vValue.constructor === Array) {
7553         for (var nItem = 0; nItem < vValue.length; nItem++) {
7554           oChild = oXMLDoc.createElement(sName);
7555           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7556           oParentEl.appendChild(oChild);
7557         }
7558       } else {
7559         oChild = oXMLDoc.createElement(sName);
7560         if (vValue instanceof Object) {
7561           loadObjTree(oXMLDoc, oChild, vValue);
7562         } else if (vValue !== null && vValue !== true) {
7563           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7564         }
7565         oParentEl.appendChild(oChild);
7566      }
7567    }
7568   }
7569
7570   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7571     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7572     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7573   };
7574
7575   this.unbuild = function (oObjTree) {    
7576     var oNewDoc = document.implementation.createDocument("", "", null);
7577     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7578     return oNewDoc;
7579   };
7580
7581   this.stringify = function (oObjTree) {
7582     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7583   };
7584 })();
7585 // var myObject = JXON.build(doc);
7586 // we got our javascript object! try: alert(JSON.stringify(myObject));
7587
7588 // var newDoc = JXON.unbuild(myObject);
7589 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7590 /**
7591  * @license
7592  * lodash (Custom Build) <https://lodash.com/>
7593  * Build: `lodash --development --output js/lib/lodash.js include="includes,toPairs,assign,bind,chunk,clone,compact,debounce,difference,each,every,extend,filter,find,first,forEach,forOwn,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,reject,some,throttle,union,uniq,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"`
7594  * Copyright jQuery Foundation and other contributors <https://jquery.org/>
7595  * Released under MIT license <https://lodash.com/license>
7596  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7597  * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7598  */
7599 ;(function() {
7600
7601   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7602   var undefined;
7603
7604   /** Used as the semantic version number. */
7605   var VERSION = '4.12.0';
7606
7607   /** Used as the size to enable large array optimizations. */
7608   var LARGE_ARRAY_SIZE = 200;
7609
7610   /** Used as the `TypeError` message for "Functions" methods. */
7611   var FUNC_ERROR_TEXT = 'Expected a function';
7612
7613   /** Used to stand-in for `undefined` hash values. */
7614   var HASH_UNDEFINED = '__lodash_hash_undefined__';
7615
7616   /** Used as the internal argument placeholder. */
7617   var PLACEHOLDER = '__lodash_placeholder__';
7618
7619   /** Used to compose bitmasks for wrapper metadata. */
7620   var BIND_FLAG = 1,
7621       BIND_KEY_FLAG = 2,
7622       CURRY_BOUND_FLAG = 4,
7623       CURRY_FLAG = 8,
7624       CURRY_RIGHT_FLAG = 16,
7625       PARTIAL_FLAG = 32,
7626       PARTIAL_RIGHT_FLAG = 64,
7627       ARY_FLAG = 128,
7628       REARG_FLAG = 256,
7629       FLIP_FLAG = 512;
7630
7631   /** Used to compose bitmasks for comparison styles. */
7632   var UNORDERED_COMPARE_FLAG = 1,
7633       PARTIAL_COMPARE_FLAG = 2;
7634
7635   /** Used to detect hot functions by number of calls within a span of milliseconds. */
7636   var HOT_COUNT = 150,
7637       HOT_SPAN = 16;
7638
7639   /** Used to indicate the type of lazy iteratees. */
7640   var LAZY_FILTER_FLAG = 1,
7641       LAZY_MAP_FLAG = 2,
7642       LAZY_WHILE_FLAG = 3;
7643
7644   /** Used as references for various `Number` constants. */
7645   var INFINITY = 1 / 0,
7646       MAX_SAFE_INTEGER = 9007199254740991,
7647       MAX_INTEGER = 1.7976931348623157e+308,
7648       NAN = 0 / 0;
7649
7650   /** Used as references for the maximum length and index of an array. */
7651   var MAX_ARRAY_LENGTH = 4294967295;
7652
7653   /** `Object#toString` result references. */
7654   var argsTag = '[object Arguments]',
7655       arrayTag = '[object Array]',
7656       boolTag = '[object Boolean]',
7657       dateTag = '[object Date]',
7658       errorTag = '[object Error]',
7659       funcTag = '[object Function]',
7660       genTag = '[object GeneratorFunction]',
7661       mapTag = '[object Map]',
7662       numberTag = '[object Number]',
7663       objectTag = '[object Object]',
7664       promiseTag = '[object Promise]',
7665       regexpTag = '[object RegExp]',
7666       setTag = '[object Set]',
7667       stringTag = '[object String]',
7668       symbolTag = '[object Symbol]',
7669       weakMapTag = '[object WeakMap]';
7670
7671   var arrayBufferTag = '[object ArrayBuffer]',
7672       dataViewTag = '[object DataView]',
7673       float32Tag = '[object Float32Array]',
7674       float64Tag = '[object Float64Array]',
7675       int8Tag = '[object Int8Array]',
7676       int16Tag = '[object Int16Array]',
7677       int32Tag = '[object Int32Array]',
7678       uint8Tag = '[object Uint8Array]',
7679       uint8ClampedTag = '[object Uint8ClampedArray]',
7680       uint16Tag = '[object Uint16Array]',
7681       uint32Tag = '[object Uint32Array]';
7682
7683   /** Used to match property names within property paths. */
7684   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
7685       reIsPlainProp = /^\w*$/,
7686       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g;
7687
7688   /**
7689    * Used to match `RegExp`
7690    * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns).
7691    */
7692   var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
7693
7694   /** Used to match leading and trailing whitespace. */
7695   var reTrim = /^\s+|\s+$/g;
7696
7697   /** Used to match backslashes in property paths. */
7698   var reEscapeChar = /\\(\\)?/g;
7699
7700   /** Used to match `RegExp` flags from their coerced string values. */
7701   var reFlags = /\w*$/;
7702
7703   /** Used to detect bad signed hexadecimal string values. */
7704   var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
7705
7706   /** Used to detect binary string values. */
7707   var reIsBinary = /^0b[01]+$/i;
7708
7709   /** Used to detect host constructors (Safari). */
7710   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7711
7712   /** Used to detect octal string values. */
7713   var reIsOctal = /^0o[0-7]+$/i;
7714
7715   /** Used to detect unsigned integer values. */
7716   var reIsUint = /^(?:0|[1-9]\d*)$/;
7717
7718   /** Used to compose unicode character classes. */
7719   var rsAstralRange = '\\ud800-\\udfff',
7720       rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
7721       rsComboSymbolsRange = '\\u20d0-\\u20f0',
7722       rsVarRange = '\\ufe0e\\ufe0f';
7723
7724   /** Used to compose unicode capture groups. */
7725   var rsAstral = '[' + rsAstralRange + ']',
7726       rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
7727       rsFitz = '\\ud83c[\\udffb-\\udfff]',
7728       rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
7729       rsNonAstral = '[^' + rsAstralRange + ']',
7730       rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
7731       rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
7732       rsZWJ = '\\u200d';
7733
7734   /** Used to compose unicode regexes. */
7735   var reOptMod = rsModifier + '?',
7736       rsOptVar = '[' + rsVarRange + ']?',
7737       rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
7738       rsSeq = rsOptVar + reOptMod + rsOptJoin,
7739       rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
7740
7741   /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
7742   var reComplexSymbol = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
7743
7744   /** Used to identify `toStringTag` values of typed arrays. */
7745   var typedArrayTags = {};
7746   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7747   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7748   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7749   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7750   typedArrayTags[uint32Tag] = true;
7751   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7752   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7753   typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
7754   typedArrayTags[errorTag] = typedArrayTags[funcTag] =
7755   typedArrayTags[mapTag] = typedArrayTags[numberTag] =
7756   typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
7757   typedArrayTags[setTag] = typedArrayTags[stringTag] =
7758   typedArrayTags[weakMapTag] = false;
7759
7760   /** Used to identify `toStringTag` values supported by `_.clone`. */
7761   var cloneableTags = {};
7762   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7763   cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
7764   cloneableTags[boolTag] = cloneableTags[dateTag] =
7765   cloneableTags[float32Tag] = cloneableTags[float64Tag] =
7766   cloneableTags[int8Tag] = cloneableTags[int16Tag] =
7767   cloneableTags[int32Tag] = cloneableTags[mapTag] =
7768   cloneableTags[numberTag] = cloneableTags[objectTag] =
7769   cloneableTags[regexpTag] = cloneableTags[setTag] =
7770   cloneableTags[stringTag] = cloneableTags[symbolTag] =
7771   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7772   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7773   cloneableTags[errorTag] = cloneableTags[funcTag] =
7774   cloneableTags[weakMapTag] = false;
7775
7776   /** Used to determine if values are of the language type `Object`. */
7777   var objectTypes = {
7778     'function': true,
7779     'object': true
7780   };
7781
7782   /** Built-in method references without a dependency on `root`. */
7783   var freeParseInt = parseInt;
7784
7785   /** Detect free variable `exports`. */
7786   var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)
7787     ? exports
7788     : undefined;
7789
7790   /** Detect free variable `module`. */
7791   var freeModule = (objectTypes[typeof module] && module && !module.nodeType)
7792     ? module
7793     : undefined;
7794
7795   /** Detect the popular CommonJS extension `module.exports`. */
7796   var moduleExports = (freeModule && freeModule.exports === freeExports)
7797     ? freeExports
7798     : undefined;
7799
7800   /** Detect free variable `global` from Node.js. */
7801   var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);
7802
7803   /** Detect free variable `self`. */
7804   var freeSelf = checkGlobal(objectTypes[typeof self] && self);
7805
7806   /** Detect free variable `window`. */
7807   var freeWindow = checkGlobal(objectTypes[typeof window] && window);
7808
7809   /** Detect `this` as the global object. */
7810   var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
7811
7812   /**
7813    * Used as a reference to the global object.
7814    *
7815    * The `this` value is used if it's the global object to avoid Greasemonkey's
7816    * restricted `window` object, otherwise the `window` object is used.
7817    */
7818   var root = freeGlobal ||
7819     ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||
7820       freeSelf || thisGlobal || Function('return this')();
7821
7822   /*--------------------------------------------------------------------------*/
7823
7824   /**
7825    * Adds the key-value `pair` to `map`.
7826    *
7827    * @private
7828    * @param {Object} map The map to modify.
7829    * @param {Array} pair The key-value pair to add.
7830    * @returns {Object} Returns `map`.
7831    */
7832   function addMapEntry(map, pair) {
7833     // Don't return `Map#set` because it doesn't return the map instance in IE 11.
7834     map.set(pair[0], pair[1]);
7835     return map;
7836   }
7837
7838   /**
7839    * Adds `value` to `set`.
7840    *
7841    * @private
7842    * @param {Object} set The set to modify.
7843    * @param {*} value The value to add.
7844    * @returns {Object} Returns `set`.
7845    */
7846   function addSetEntry(set, value) {
7847     set.add(value);
7848     return set;
7849   }
7850
7851   /**
7852    * A faster alternative to `Function#apply`, this function invokes `func`
7853    * with the `this` binding of `thisArg` and the arguments of `args`.
7854    *
7855    * @private
7856    * @param {Function} func The function to invoke.
7857    * @param {*} thisArg The `this` binding of `func`.
7858    * @param {Array} args The arguments to invoke `func` with.
7859    * @returns {*} Returns the result of `func`.
7860    */
7861   function apply(func, thisArg, args) {
7862     var length = args.length;
7863     switch (length) {
7864       case 0: return func.call(thisArg);
7865       case 1: return func.call(thisArg, args[0]);
7866       case 2: return func.call(thisArg, args[0], args[1]);
7867       case 3: return func.call(thisArg, args[0], args[1], args[2]);
7868     }
7869     return func.apply(thisArg, args);
7870   }
7871
7872   /**
7873    * A specialized version of `baseAggregator` for arrays.
7874    *
7875    * @private
7876    * @param {Array} array The array to iterate over.
7877    * @param {Function} setter The function to set `accumulator` values.
7878    * @param {Function} iteratee The iteratee to transform keys.
7879    * @param {Object} accumulator The initial aggregated object.
7880    * @returns {Function} Returns `accumulator`.
7881    */
7882   function arrayAggregator(array, setter, iteratee, accumulator) {
7883     var index = -1,
7884         length = array.length;
7885
7886     while (++index < length) {
7887       var value = array[index];
7888       setter(accumulator, value, iteratee(value), array);
7889     }
7890     return accumulator;
7891   }
7892
7893   /**
7894    * A specialized version of `_.forEach` for arrays without support for
7895    * iteratee shorthands.
7896    *
7897    * @private
7898    * @param {Array} array The array to iterate over.
7899    * @param {Function} iteratee The function invoked per iteration.
7900    * @returns {Array} Returns `array`.
7901    */
7902   function arrayEach(array, iteratee) {
7903     var index = -1,
7904         length = array.length;
7905
7906     while (++index < length) {
7907       if (iteratee(array[index], index, array) === false) {
7908         break;
7909       }
7910     }
7911     return array;
7912   }
7913
7914   /**
7915    * A specialized version of `_.every` for arrays without support for
7916    * iteratee shorthands.
7917    *
7918    * @private
7919    * @param {Array} array The array to iterate over.
7920    * @param {Function} predicate The function invoked per iteration.
7921    * @returns {boolean} Returns `true` if all elements pass the predicate check,
7922    *  else `false`.
7923    */
7924   function arrayEvery(array, predicate) {
7925     var index = -1,
7926         length = array.length;
7927
7928     while (++index < length) {
7929       if (!predicate(array[index], index, array)) {
7930         return false;
7931       }
7932     }
7933     return true;
7934   }
7935
7936   /**
7937    * A specialized version of `_.filter` for arrays without support for
7938    * iteratee shorthands.
7939    *
7940    * @private
7941    * @param {Array} array The array to iterate over.
7942    * @param {Function} predicate The function invoked per iteration.
7943    * @returns {Array} Returns the new filtered array.
7944    */
7945   function arrayFilter(array, predicate) {
7946     var index = -1,
7947         length = array.length,
7948         resIndex = 0,
7949         result = [];
7950
7951     while (++index < length) {
7952       var value = array[index];
7953       if (predicate(value, index, array)) {
7954         result[resIndex++] = value;
7955       }
7956     }
7957     return result;
7958   }
7959
7960   /**
7961    * A specialized version of `_.includes` for arrays without support for
7962    * specifying an index to search from.
7963    *
7964    * @private
7965    * @param {Array} array The array to search.
7966    * @param {*} target The value to search for.
7967    * @returns {boolean} Returns `true` if `target` is found, else `false`.
7968    */
7969   function arrayIncludes(array, value) {
7970     return !!array.length && baseIndexOf(array, value, 0) > -1;
7971   }
7972
7973   /**
7974    * This function is like `arrayIncludes` except that it accepts a comparator.
7975    *
7976    * @private
7977    * @param {Array} array The array to search.
7978    * @param {*} target The value to search for.
7979    * @param {Function} comparator The comparator invoked per element.
7980    * @returns {boolean} Returns `true` if `target` is found, else `false`.
7981    */
7982   function arrayIncludesWith(array, value, comparator) {
7983     var index = -1,
7984         length = array.length;
7985
7986     while (++index < length) {
7987       if (comparator(value, array[index])) {
7988         return true;
7989       }
7990     }
7991     return false;
7992   }
7993
7994   /**
7995    * A specialized version of `_.map` for arrays without support for iteratee
7996    * shorthands.
7997    *
7998    * @private
7999    * @param {Array} array The array to iterate over.
8000    * @param {Function} iteratee The function invoked per iteration.
8001    * @returns {Array} Returns the new mapped array.
8002    */
8003   function arrayMap(array, iteratee) {
8004     var index = -1,
8005         length = array.length,
8006         result = Array(length);
8007
8008     while (++index < length) {
8009       result[index] = iteratee(array[index], index, array);
8010     }
8011     return result;
8012   }
8013
8014   /**
8015    * Appends the elements of `values` to `array`.
8016    *
8017    * @private
8018    * @param {Array} array The array to modify.
8019    * @param {Array} values The values to append.
8020    * @returns {Array} Returns `array`.
8021    */
8022   function arrayPush(array, values) {
8023     var index = -1,
8024         length = values.length,
8025         offset = array.length;
8026
8027     while (++index < length) {
8028       array[offset + index] = values[index];
8029     }
8030     return array;
8031   }
8032
8033   /**
8034    * A specialized version of `_.reduce` for arrays without support for
8035    * iteratee shorthands.
8036    *
8037    * @private
8038    * @param {Array} array The array to iterate over.
8039    * @param {Function} iteratee The function invoked per iteration.
8040    * @param {*} [accumulator] The initial value.
8041    * @param {boolean} [initAccum] Specify using the first element of `array` as
8042    *  the initial value.
8043    * @returns {*} Returns the accumulated value.
8044    */
8045   function arrayReduce(array, iteratee, accumulator, initAccum) {
8046     var index = -1,
8047         length = array.length;
8048
8049     if (initAccum && length) {
8050       accumulator = array[++index];
8051     }
8052     while (++index < length) {
8053       accumulator = iteratee(accumulator, array[index], index, array);
8054     }
8055     return accumulator;
8056   }
8057
8058   /**
8059    * A specialized version of `_.some` for arrays without support for iteratee
8060    * shorthands.
8061    *
8062    * @private
8063    * @param {Array} array The array to iterate over.
8064    * @param {Function} predicate The function invoked per iteration.
8065    * @returns {boolean} Returns `true` if any element passes the predicate check,
8066    *  else `false`.
8067    */
8068   function arraySome(array, predicate) {
8069     var index = -1,
8070         length = array.length;
8071
8072     while (++index < length) {
8073       if (predicate(array[index], index, array)) {
8074         return true;
8075       }
8076     }
8077     return false;
8078   }
8079
8080   /**
8081    * The base implementation of methods like `_.find` and `_.findKey`, without
8082    * support for iteratee shorthands, which iterates over `collection` using
8083    * `eachFunc`.
8084    *
8085    * @private
8086    * @param {Array|Object} collection The collection to search.
8087    * @param {Function} predicate The function invoked per iteration.
8088    * @param {Function} eachFunc The function to iterate over `collection`.
8089    * @param {boolean} [retKey] Specify returning the key of the found element
8090    *  instead of the element itself.
8091    * @returns {*} Returns the found element or its key, else `undefined`.
8092    */
8093   function baseFind(collection, predicate, eachFunc, retKey) {
8094     var result;
8095     eachFunc(collection, function(value, key, collection) {
8096       if (predicate(value, key, collection)) {
8097         result = retKey ? key : value;
8098         return false;
8099       }
8100     });
8101     return result;
8102   }
8103
8104   /**
8105    * The base implementation of `_.findIndex` and `_.findLastIndex` without
8106    * support for iteratee shorthands.
8107    *
8108    * @private
8109    * @param {Array} array The array to search.
8110    * @param {Function} predicate The function invoked per iteration.
8111    * @param {boolean} [fromRight] Specify iterating from right to left.
8112    * @returns {number} Returns the index of the matched value, else `-1`.
8113    */
8114   function baseFindIndex(array, predicate, fromRight) {
8115     var length = array.length,
8116         index = fromRight ? length : -1;
8117
8118     while ((fromRight ? index-- : ++index < length)) {
8119       if (predicate(array[index], index, array)) {
8120         return index;
8121       }
8122     }
8123     return -1;
8124   }
8125
8126   /**
8127    * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
8128    *
8129    * @private
8130    * @param {Array} array The array to search.
8131    * @param {*} value The value to search for.
8132    * @param {number} fromIndex The index to search from.
8133    * @returns {number} Returns the index of the matched value, else `-1`.
8134    */
8135   function baseIndexOf(array, value, fromIndex) {
8136     if (value !== value) {
8137       return indexOfNaN(array, fromIndex);
8138     }
8139     var index = fromIndex - 1,
8140         length = array.length;
8141
8142     while (++index < length) {
8143       if (array[index] === value) {
8144         return index;
8145       }
8146     }
8147     return -1;
8148   }
8149
8150   /**
8151    * The base implementation of `_.reduce` and `_.reduceRight`, without support
8152    * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
8153    *
8154    * @private
8155    * @param {Array|Object} collection The collection to iterate over.
8156    * @param {Function} iteratee The function invoked per iteration.
8157    * @param {*} accumulator The initial value.
8158    * @param {boolean} initAccum Specify using the first or last element of
8159    *  `collection` as the initial value.
8160    * @param {Function} eachFunc The function to iterate over `collection`.
8161    * @returns {*} Returns the accumulated value.
8162    */
8163   function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
8164     eachFunc(collection, function(value, index, collection) {
8165       accumulator = initAccum
8166         ? (initAccum = false, value)
8167         : iteratee(accumulator, value, index, collection);
8168     });
8169     return accumulator;
8170   }
8171
8172   /**
8173    * The base implementation of `_.times` without support for iteratee shorthands
8174    * or max array length checks.
8175    *
8176    * @private
8177    * @param {number} n The number of times to invoke `iteratee`.
8178    * @param {Function} iteratee The function invoked per iteration.
8179    * @returns {Array} Returns the array of results.
8180    */
8181   function baseTimes(n, iteratee) {
8182     var index = -1,
8183         result = Array(n);
8184
8185     while (++index < n) {
8186       result[index] = iteratee(index);
8187     }
8188     return result;
8189   }
8190
8191   /**
8192    * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
8193    * of key-value pairs for `object` corresponding to the property names of `props`.
8194    *
8195    * @private
8196    * @param {Object} object The object to query.
8197    * @param {Array} props The property names to get values for.
8198    * @returns {Object} Returns the key-value pairs.
8199    */
8200   function baseToPairs(object, props) {
8201     return arrayMap(props, function(key) {
8202       return [key, object[key]];
8203     });
8204   }
8205
8206   /**
8207    * The base implementation of `_.unary` without support for storing wrapper metadata.
8208    *
8209    * @private
8210    * @param {Function} func The function to cap arguments for.
8211    * @returns {Function} Returns the new capped function.
8212    */
8213   function baseUnary(func) {
8214     return function(value) {
8215       return func(value);
8216     };
8217   }
8218
8219   /**
8220    * The base implementation of `_.values` and `_.valuesIn` which creates an
8221    * array of `object` property values corresponding to the property names
8222    * of `props`.
8223    *
8224    * @private
8225    * @param {Object} object The object to query.
8226    * @param {Array} props The property names to get values for.
8227    * @returns {Object} Returns the array of property values.
8228    */
8229   function baseValues(object, props) {
8230     return arrayMap(props, function(key) {
8231       return object[key];
8232     });
8233   }
8234
8235   /**
8236    * Checks if a cache value for `key` exists.
8237    *
8238    * @private
8239    * @param {Object} cache The cache to query.
8240    * @param {string} key The key of the entry to check.
8241    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8242    */
8243   function cacheHas(cache, key) {
8244     return cache.has(key);
8245   }
8246
8247   /**
8248    * Checks if `value` is a global object.
8249    *
8250    * @private
8251    * @param {*} value The value to check.
8252    * @returns {null|Object} Returns `value` if it's a global object, else `null`.
8253    */
8254   function checkGlobal(value) {
8255     return (value && value.Object === Object) ? value : null;
8256   }
8257
8258   /**
8259    * Gets the number of `placeholder` occurrences in `array`.
8260    *
8261    * @private
8262    * @param {Array} array The array to inspect.
8263    * @param {*} placeholder The placeholder to search for.
8264    * @returns {number} Returns the placeholder count.
8265    */
8266   function countHolders(array, placeholder) {
8267     var length = array.length,
8268         result = 0;
8269
8270     while (length--) {
8271       if (array[length] === placeholder) {
8272         result++;
8273       }
8274     }
8275     return result;
8276   }
8277
8278   /**
8279    * Gets the index at which the first occurrence of `NaN` is found in `array`.
8280    *
8281    * @private
8282    * @param {Array} array The array to search.
8283    * @param {number} fromIndex The index to search from.
8284    * @param {boolean} [fromRight] Specify iterating from right to left.
8285    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
8286    */
8287   function indexOfNaN(array, fromIndex, fromRight) {
8288     var length = array.length,
8289         index = fromIndex + (fromRight ? 0 : -1);
8290
8291     while ((fromRight ? index-- : ++index < length)) {
8292       var other = array[index];
8293       if (other !== other) {
8294         return index;
8295       }
8296     }
8297     return -1;
8298   }
8299
8300   /**
8301    * Checks if `value` is a host object in IE < 9.
8302    *
8303    * @private
8304    * @param {*} value The value to check.
8305    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
8306    */
8307   function isHostObject(value) {
8308     // Many host objects are `Object` objects that can coerce to strings
8309     // despite having improperly defined `toString` methods.
8310     var result = false;
8311     if (value != null && typeof value.toString != 'function') {
8312       try {
8313         result = !!(value + '');
8314       } catch (e) {}
8315     }
8316     return result;
8317   }
8318
8319   /**
8320    * Converts `iterator` to an array.
8321    *
8322    * @private
8323    * @param {Object} iterator The iterator to convert.
8324    * @returns {Array} Returns the converted array.
8325    */
8326   function iteratorToArray(iterator) {
8327     var data,
8328         result = [];
8329
8330     while (!(data = iterator.next()).done) {
8331       result.push(data.value);
8332     }
8333     return result;
8334   }
8335
8336   /**
8337    * Converts `map` to its key-value pairs.
8338    *
8339    * @private
8340    * @param {Object} map The map to convert.
8341    * @returns {Array} Returns the key-value pairs.
8342    */
8343   function mapToArray(map) {
8344     var index = -1,
8345         result = Array(map.size);
8346
8347     map.forEach(function(value, key) {
8348       result[++index] = [key, value];
8349     });
8350     return result;
8351   }
8352
8353   /**
8354    * Replaces all `placeholder` elements in `array` with an internal placeholder
8355    * and returns an array of their indexes.
8356    *
8357    * @private
8358    * @param {Array} array The array to modify.
8359    * @param {*} placeholder The placeholder to replace.
8360    * @returns {Array} Returns the new array of placeholder indexes.
8361    */
8362   function replaceHolders(array, placeholder) {
8363     var index = -1,
8364         length = array.length,
8365         resIndex = 0,
8366         result = [];
8367
8368     while (++index < length) {
8369       var value = array[index];
8370       if (value === placeholder || value === PLACEHOLDER) {
8371         array[index] = PLACEHOLDER;
8372         result[resIndex++] = index;
8373       }
8374     }
8375     return result;
8376   }
8377
8378   /**
8379    * Converts `set` to an array of its values.
8380    *
8381    * @private
8382    * @param {Object} set The set to convert.
8383    * @returns {Array} Returns the values.
8384    */
8385   function setToArray(set) {
8386     var index = -1,
8387         result = Array(set.size);
8388
8389     set.forEach(function(value) {
8390       result[++index] = value;
8391     });
8392     return result;
8393   }
8394
8395   /**
8396    * Converts `set` to its value-value pairs.
8397    *
8398    * @private
8399    * @param {Object} set The set to convert.
8400    * @returns {Array} Returns the value-value pairs.
8401    */
8402   function setToPairs(set) {
8403     var index = -1,
8404         result = Array(set.size);
8405
8406     set.forEach(function(value) {
8407       result[++index] = [value, value];
8408     });
8409     return result;
8410   }
8411
8412   /**
8413    * Converts `string` to an array.
8414    *
8415    * @private
8416    * @param {string} string The string to convert.
8417    * @returns {Array} Returns the converted array.
8418    */
8419   function stringToArray(string) {
8420     return string.match(reComplexSymbol);
8421   }
8422
8423   /*--------------------------------------------------------------------------*/
8424
8425   /** Used for built-in method references. */
8426   var arrayProto = Array.prototype,
8427       objectProto = Object.prototype;
8428
8429   /** Used to resolve the decompiled source of functions. */
8430   var funcToString = Function.prototype.toString;
8431
8432   /** Used to check objects for own properties. */
8433   var hasOwnProperty = objectProto.hasOwnProperty;
8434
8435   /** Used to infer the `Object` constructor. */
8436   var objectCtorString = funcToString.call(Object);
8437
8438   /**
8439    * Used to resolve the
8440    * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
8441    * of values.
8442    */
8443   var objectToString = objectProto.toString;
8444
8445   /** Used to detect if a method is native. */
8446   var reIsNative = RegExp('^' +
8447     funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
8448     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
8449   );
8450
8451   /** Built-in value references. */
8452   var Buffer = moduleExports ? root.Buffer : undefined,
8453       Reflect = root.Reflect,
8454       Symbol = root.Symbol,
8455       Uint8Array = root.Uint8Array,
8456       enumerate = Reflect ? Reflect.enumerate : undefined,
8457       getOwnPropertySymbols = Object.getOwnPropertySymbols,
8458       iteratorSymbol = typeof (iteratorSymbol = Symbol && Symbol.iterator) == 'symbol' ? iteratorSymbol : undefined,
8459       objectCreate = Object.create,
8460       propertyIsEnumerable = objectProto.propertyIsEnumerable,
8461       splice = arrayProto.splice;
8462
8463   /* Built-in method references for those with the same name as other `lodash` methods. */
8464   var nativeCeil = Math.ceil,
8465       nativeGetPrototype = Object.getPrototypeOf,
8466       nativeKeys = Object.keys,
8467       nativeMax = Math.max,
8468       nativeMin = Math.min,
8469       nativeReverse = arrayProto.reverse;
8470
8471   /* Built-in method references that are verified to be native. */
8472   var DataView = getNative(root, 'DataView'),
8473       Map = getNative(root, 'Map'),
8474       Promise = getNative(root, 'Promise'),
8475       Set = getNative(root, 'Set'),
8476       WeakMap = getNative(root, 'WeakMap'),
8477       nativeCreate = getNative(Object, 'create');
8478
8479   /** Used to store function metadata. */
8480   var metaMap = WeakMap && new WeakMap;
8481
8482   /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */
8483   var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');
8484
8485   /** Used to lookup unminified function names. */
8486   var realNames = {};
8487
8488   /** Used to detect maps, sets, and weakmaps. */
8489   var dataViewCtorString = toSource(DataView),
8490       mapCtorString = toSource(Map),
8491       promiseCtorString = toSource(Promise),
8492       setCtorString = toSource(Set),
8493       weakMapCtorString = toSource(WeakMap);
8494
8495   /** Used to convert symbols to primitives and strings. */
8496   var symbolProto = Symbol ? Symbol.prototype : undefined,
8497       symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
8498       symbolToString = symbolProto ? symbolProto.toString : undefined;
8499
8500   /*------------------------------------------------------------------------*/
8501
8502   /**
8503    * Creates a `lodash` object which wraps `value` to enable implicit method
8504    * chain sequences. Methods that operate on and return arrays, collections,
8505    * and functions can be chained together. Methods that retrieve a single value
8506    * or may return a primitive value will automatically end the chain sequence
8507    * and return the unwrapped value. Otherwise, the value must be unwrapped
8508    * with `_#value`.
8509    *
8510    * Explicit chain sequences, which must be unwrapped with `_#value`, may be
8511    * enabled using `_.chain`.
8512    *
8513    * The execution of chained methods is lazy, that is, it's deferred until
8514    * `_#value` is implicitly or explicitly called.
8515    *
8516    * Lazy evaluation allows several methods to support shortcut fusion.
8517    * Shortcut fusion is an optimization to merge iteratee calls; this avoids
8518    * the creation of intermediate arrays and can greatly reduce the number of
8519    * iteratee executions. Sections of a chain sequence qualify for shortcut
8520    * fusion if the section is applied to an array of at least `200` elements
8521    * and any iteratees accept only one argument. The heuristic for whether a
8522    * section qualifies for shortcut fusion is subject to change.
8523    *
8524    * Chaining is supported in custom builds as long as the `_#value` method is
8525    * directly or indirectly included in the build.
8526    *
8527    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8528    *
8529    * The wrapper `Array` methods are:
8530    * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
8531    *
8532    * The wrapper `String` methods are:
8533    * `replace` and `split`
8534    *
8535    * The wrapper methods that support shortcut fusion are:
8536    * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
8537    * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
8538    * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
8539    *
8540    * The chainable wrapper methods are:
8541    * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
8542    * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
8543    * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
8544    * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
8545    * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
8546    * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
8547    * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
8548    * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
8549    * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
8550    * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
8551    * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8552    * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
8553    * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
8554    * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
8555    * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
8556    * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
8557    * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
8558    * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
8559    * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
8560    * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
8561    * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
8562    * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
8563    * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
8564    * `zipObject`, `zipObjectDeep`, and `zipWith`
8565    *
8566    * The wrapper methods that are **not** chainable by default are:
8567    * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
8568    * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `deburr`, `divide`, `each`,
8569    * `eachRight`, `endsWith`, `eq`, `escape`, `escapeRegExp`, `every`, `find`,
8570    * `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `first`,
8571    * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
8572    * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,
8573    * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`,
8574    * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`,
8575    * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`,
8576    * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`,
8577    * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,
8578    * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,
8579    * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,
8580    * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`,
8581    * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`,
8582    * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`,
8583    * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,
8584    * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8585    * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`,
8586    * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`,
8587    * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`,
8588    * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,
8589    * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`
8590    *
8591    * @name _
8592    * @constructor
8593    * @category Seq
8594    * @param {*} value The value to wrap in a `lodash` instance.
8595    * @returns {Object} Returns the new `lodash` wrapper instance.
8596    * @example
8597    *
8598    * function square(n) {
8599    *   return n * n;
8600    * }
8601    *
8602    * var wrapped = _([1, 2, 3]);
8603    *
8604    * // Returns an unwrapped value.
8605    * wrapped.reduce(_.add);
8606    * // => 6
8607    *
8608    * // Returns a wrapped value.
8609    * var squares = wrapped.map(square);
8610    *
8611    * _.isArray(squares);
8612    * // => false
8613    *
8614    * _.isArray(squares.value());
8615    * // => true
8616    */
8617   function lodash(value) {
8618     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8619       if (value instanceof LodashWrapper) {
8620         return value;
8621       }
8622       if (hasOwnProperty.call(value, '__wrapped__')) {
8623         return wrapperClone(value);
8624       }
8625     }
8626     return new LodashWrapper(value);
8627   }
8628
8629   /**
8630    * The function whose prototype chain sequence wrappers inherit from.
8631    *
8632    * @private
8633    */
8634   function baseLodash() {
8635     // No operation performed.
8636   }
8637
8638   /**
8639    * The base constructor for creating `lodash` wrapper objects.
8640    *
8641    * @private
8642    * @param {*} value The value to wrap.
8643    * @param {boolean} [chainAll] Enable explicit method chain sequences.
8644    */
8645   function LodashWrapper(value, chainAll) {
8646     this.__wrapped__ = value;
8647     this.__actions__ = [];
8648     this.__chain__ = !!chainAll;
8649     this.__index__ = 0;
8650     this.__values__ = undefined;
8651   }
8652
8653   // Ensure wrappers are instances of `baseLodash`.
8654   lodash.prototype = baseLodash.prototype;
8655   lodash.prototype.constructor = lodash;
8656
8657   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
8658   LodashWrapper.prototype.constructor = LodashWrapper;
8659
8660   /*------------------------------------------------------------------------*/
8661
8662   /**
8663    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8664    *
8665    * @private
8666    * @constructor
8667    * @param {*} value The value to wrap.
8668    */
8669   function LazyWrapper(value) {
8670     this.__wrapped__ = value;
8671     this.__actions__ = [];
8672     this.__dir__ = 1;
8673     this.__filtered__ = false;
8674     this.__iteratees__ = [];
8675     this.__takeCount__ = MAX_ARRAY_LENGTH;
8676     this.__views__ = [];
8677   }
8678
8679   /**
8680    * Creates a clone of the lazy wrapper object.
8681    *
8682    * @private
8683    * @name clone
8684    * @memberOf LazyWrapper
8685    * @returns {Object} Returns the cloned `LazyWrapper` object.
8686    */
8687   function lazyClone() {
8688     var result = new LazyWrapper(this.__wrapped__);
8689     result.__actions__ = copyArray(this.__actions__);
8690     result.__dir__ = this.__dir__;
8691     result.__filtered__ = this.__filtered__;
8692     result.__iteratees__ = copyArray(this.__iteratees__);
8693     result.__takeCount__ = this.__takeCount__;
8694     result.__views__ = copyArray(this.__views__);
8695     return result;
8696   }
8697
8698   /**
8699    * Reverses the direction of lazy iteration.
8700    *
8701    * @private
8702    * @name reverse
8703    * @memberOf LazyWrapper
8704    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8705    */
8706   function lazyReverse() {
8707     if (this.__filtered__) {
8708       var result = new LazyWrapper(this);
8709       result.__dir__ = -1;
8710       result.__filtered__ = true;
8711     } else {
8712       result = this.clone();
8713       result.__dir__ *= -1;
8714     }
8715     return result;
8716   }
8717
8718   /**
8719    * Extracts the unwrapped value from its lazy wrapper.
8720    *
8721    * @private
8722    * @name value
8723    * @memberOf LazyWrapper
8724    * @returns {*} Returns the unwrapped value.
8725    */
8726   function lazyValue() {
8727     var array = this.__wrapped__.value(),
8728         dir = this.__dir__,
8729         isArr = isArray(array),
8730         isRight = dir < 0,
8731         arrLength = isArr ? array.length : 0,
8732         view = getView(0, arrLength, this.__views__),
8733         start = view.start,
8734         end = view.end,
8735         length = end - start,
8736         index = isRight ? end : (start - 1),
8737         iteratees = this.__iteratees__,
8738         iterLength = iteratees.length,
8739         resIndex = 0,
8740         takeCount = nativeMin(length, this.__takeCount__);
8741
8742     if (!isArr || arrLength < LARGE_ARRAY_SIZE ||
8743         (arrLength == length && takeCount == length)) {
8744       return baseWrapperValue(array, this.__actions__);
8745     }
8746     var result = [];
8747
8748     outer:
8749     while (length-- && resIndex < takeCount) {
8750       index += dir;
8751
8752       var iterIndex = -1,
8753           value = array[index];
8754
8755       while (++iterIndex < iterLength) {
8756         var data = iteratees[iterIndex],
8757             iteratee = data.iteratee,
8758             type = data.type,
8759             computed = iteratee(value);
8760
8761         if (type == LAZY_MAP_FLAG) {
8762           value = computed;
8763         } else if (!computed) {
8764           if (type == LAZY_FILTER_FLAG) {
8765             continue outer;
8766           } else {
8767             break outer;
8768           }
8769         }
8770       }
8771       result[resIndex++] = value;
8772     }
8773     return result;
8774   }
8775
8776   // Ensure `LazyWrapper` is an instance of `baseLodash`.
8777   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
8778   LazyWrapper.prototype.constructor = LazyWrapper;
8779
8780   /*------------------------------------------------------------------------*/
8781
8782   /**
8783    * Creates a hash object.
8784    *
8785    * @private
8786    * @constructor
8787    * @param {Array} [entries] The key-value pairs to cache.
8788    */
8789   function Hash(entries) {
8790     var index = -1,
8791         length = entries ? entries.length : 0;
8792
8793     this.clear();
8794     while (++index < length) {
8795       var entry = entries[index];
8796       this.set(entry[0], entry[1]);
8797     }
8798   }
8799
8800   /**
8801    * Removes all key-value entries from the hash.
8802    *
8803    * @private
8804    * @name clear
8805    * @memberOf Hash
8806    */
8807   function hashClear() {
8808     this.__data__ = nativeCreate ? nativeCreate(null) : {};
8809   }
8810
8811   /**
8812    * Removes `key` and its value from the hash.
8813    *
8814    * @private
8815    * @name delete
8816    * @memberOf Hash
8817    * @param {Object} hash The hash to modify.
8818    * @param {string} key The key of the value to remove.
8819    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8820    */
8821   function hashDelete(key) {
8822     return this.has(key) && delete this.__data__[key];
8823   }
8824
8825   /**
8826    * Gets the hash value for `key`.
8827    *
8828    * @private
8829    * @name get
8830    * @memberOf Hash
8831    * @param {string} key The key of the value to get.
8832    * @returns {*} Returns the entry value.
8833    */
8834   function hashGet(key) {
8835     var data = this.__data__;
8836     if (nativeCreate) {
8837       var result = data[key];
8838       return result === HASH_UNDEFINED ? undefined : result;
8839     }
8840     return hasOwnProperty.call(data, key) ? data[key] : undefined;
8841   }
8842
8843   /**
8844    * Checks if a hash value for `key` exists.
8845    *
8846    * @private
8847    * @name has
8848    * @memberOf Hash
8849    * @param {string} key The key of the entry to check.
8850    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8851    */
8852   function hashHas(key) {
8853     var data = this.__data__;
8854     return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
8855   }
8856
8857   /**
8858    * Sets the hash `key` to `value`.
8859    *
8860    * @private
8861    * @name set
8862    * @memberOf Hash
8863    * @param {string} key The key of the value to set.
8864    * @param {*} value The value to set.
8865    * @returns {Object} Returns the hash instance.
8866    */
8867   function hashSet(key, value) {
8868     var data = this.__data__;
8869     data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
8870     return this;
8871   }
8872
8873   // Add methods to `Hash`.
8874   Hash.prototype.clear = hashClear;
8875   Hash.prototype['delete'] = hashDelete;
8876   Hash.prototype.get = hashGet;
8877   Hash.prototype.has = hashHas;
8878   Hash.prototype.set = hashSet;
8879
8880   /*------------------------------------------------------------------------*/
8881
8882   /**
8883    * Creates an list cache object.
8884    *
8885    * @private
8886    * @constructor
8887    * @param {Array} [entries] The key-value pairs to cache.
8888    */
8889   function ListCache(entries) {
8890     var index = -1,
8891         length = entries ? entries.length : 0;
8892
8893     this.clear();
8894     while (++index < length) {
8895       var entry = entries[index];
8896       this.set(entry[0], entry[1]);
8897     }
8898   }
8899
8900   /**
8901    * Removes all key-value entries from the list cache.
8902    *
8903    * @private
8904    * @name clear
8905    * @memberOf ListCache
8906    */
8907   function listCacheClear() {
8908     this.__data__ = [];
8909   }
8910
8911   /**
8912    * Removes `key` and its value from the list cache.
8913    *
8914    * @private
8915    * @name delete
8916    * @memberOf ListCache
8917    * @param {string} key The key of the value to remove.
8918    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8919    */
8920   function listCacheDelete(key) {
8921     var data = this.__data__,
8922         index = assocIndexOf(data, key);
8923
8924     if (index < 0) {
8925       return false;
8926     }
8927     var lastIndex = data.length - 1;
8928     if (index == lastIndex) {
8929       data.pop();
8930     } else {
8931       splice.call(data, index, 1);
8932     }
8933     return true;
8934   }
8935
8936   /**
8937    * Gets the list cache value for `key`.
8938    *
8939    * @private
8940    * @name get
8941    * @memberOf ListCache
8942    * @param {string} key The key of the value to get.
8943    * @returns {*} Returns the entry value.
8944    */
8945   function listCacheGet(key) {
8946     var data = this.__data__,
8947         index = assocIndexOf(data, key);
8948
8949     return index < 0 ? undefined : data[index][1];
8950   }
8951
8952   /**
8953    * Checks if a list cache value for `key` exists.
8954    *
8955    * @private
8956    * @name has
8957    * @memberOf ListCache
8958    * @param {string} key The key of the entry to check.
8959    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8960    */
8961   function listCacheHas(key) {
8962     return assocIndexOf(this.__data__, key) > -1;
8963   }
8964
8965   /**
8966    * Sets the list cache `key` to `value`.
8967    *
8968    * @private
8969    * @name set
8970    * @memberOf ListCache
8971    * @param {string} key The key of the value to set.
8972    * @param {*} value The value to set.
8973    * @returns {Object} Returns the list cache instance.
8974    */
8975   function listCacheSet(key, value) {
8976     var data = this.__data__,
8977         index = assocIndexOf(data, key);
8978
8979     if (index < 0) {
8980       data.push([key, value]);
8981     } else {
8982       data[index][1] = value;
8983     }
8984     return this;
8985   }
8986
8987   // Add methods to `ListCache`.
8988   ListCache.prototype.clear = listCacheClear;
8989   ListCache.prototype['delete'] = listCacheDelete;
8990   ListCache.prototype.get = listCacheGet;
8991   ListCache.prototype.has = listCacheHas;
8992   ListCache.prototype.set = listCacheSet;
8993
8994   /*------------------------------------------------------------------------*/
8995
8996   /**
8997    * Creates a map cache object to store key-value pairs.
8998    *
8999    * @private
9000    * @constructor
9001    * @param {Array} [entries] The key-value pairs to cache.
9002    */
9003   function MapCache(entries) {
9004     var index = -1,
9005         length = entries ? entries.length : 0;
9006
9007     this.clear();
9008     while (++index < length) {
9009       var entry = entries[index];
9010       this.set(entry[0], entry[1]);
9011     }
9012   }
9013
9014   /**
9015    * Removes all key-value entries from the map.
9016    *
9017    * @private
9018    * @name clear
9019    * @memberOf MapCache
9020    */
9021   function mapCacheClear() {
9022     this.__data__ = {
9023       'hash': new Hash,
9024       'map': new (Map || ListCache),
9025       'string': new Hash
9026     };
9027   }
9028
9029   /**
9030    * Removes `key` and its value from the map.
9031    *
9032    * @private
9033    * @name delete
9034    * @memberOf MapCache
9035    * @param {string} key The key of the value to remove.
9036    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
9037    */
9038   function mapCacheDelete(key) {
9039     return getMapData(this, key)['delete'](key);
9040   }
9041
9042   /**
9043    * Gets the map value for `key`.
9044    *
9045    * @private
9046    * @name get
9047    * @memberOf MapCache
9048    * @param {string} key The key of the value to get.
9049    * @returns {*} Returns the entry value.
9050    */
9051   function mapCacheGet(key) {
9052     return getMapData(this, key).get(key);
9053   }
9054
9055   /**
9056    * Checks if a map value for `key` exists.
9057    *
9058    * @private
9059    * @name has
9060    * @memberOf MapCache
9061    * @param {string} key The key of the entry to check.
9062    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
9063    */
9064   function mapCacheHas(key) {
9065     return getMapData(this, key).has(key);
9066   }
9067
9068   /**
9069    * Sets the map `key` to `value`.
9070    *
9071    * @private
9072    * @name set
9073    * @memberOf MapCache
9074    * @param {string} key The key of the value to set.
9075    * @param {*} value The value to set.
9076    * @returns {Object} Returns the map cache instance.
9077    */
9078   function mapCacheSet(key, value) {
9079     getMapData(this, key).set(key, value);
9080     return this;
9081   }
9082
9083   // Add methods to `MapCache`.
9084   MapCache.prototype.clear = mapCacheClear;
9085   MapCache.prototype['delete'] = mapCacheDelete;
9086   MapCache.prototype.get = mapCacheGet;
9087   MapCache.prototype.has = mapCacheHas;
9088   MapCache.prototype.set = mapCacheSet;
9089
9090   /*------------------------------------------------------------------------*/
9091
9092   /**
9093    *
9094    * Creates an array cache object to store unique values.
9095    *
9096    * @private
9097    * @constructor
9098    * @param {Array} [values] The values to cache.
9099    */
9100   function SetCache(values) {
9101     var index = -1,
9102         length = values ? values.length : 0;
9103
9104     this.__data__ = new MapCache;
9105     while (++index < length) {
9106       this.add(values[index]);
9107     }
9108   }
9109
9110   /**
9111    * Adds `value` to the array cache.
9112    *
9113    * @private
9114    * @name add
9115    * @memberOf SetCache
9116    * @alias push
9117    * @param {*} value The value to cache.
9118    * @returns {Object} Returns the cache instance.
9119    */
9120   function setCacheAdd(value) {
9121     this.__data__.set(value, HASH_UNDEFINED);
9122     return this;
9123   }
9124
9125   /**
9126    * Checks if `value` is in the array cache.
9127    *
9128    * @private
9129    * @name has
9130    * @memberOf SetCache
9131    * @param {*} value The value to search for.
9132    * @returns {number} Returns `true` if `value` is found, else `false`.
9133    */
9134   function setCacheHas(value) {
9135     return this.__data__.has(value);
9136   }
9137
9138   // Add methods to `SetCache`.
9139   SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
9140   SetCache.prototype.has = setCacheHas;
9141
9142   /*------------------------------------------------------------------------*/
9143
9144   /**
9145    * Creates a stack cache object to store key-value pairs.
9146    *
9147    * @private
9148    * @constructor
9149    * @param {Array} [entries] The key-value pairs to cache.
9150    */
9151   function Stack(entries) {
9152     this.__data__ = new ListCache(entries);
9153   }
9154
9155   /**
9156    * Removes all key-value entries from the stack.
9157    *
9158    * @private
9159    * @name clear
9160    * @memberOf Stack
9161    */
9162   function stackClear() {
9163     this.__data__ = new ListCache;
9164   }
9165
9166   /**
9167    * Removes `key` and its value from the stack.
9168    *
9169    * @private
9170    * @name delete
9171    * @memberOf Stack
9172    * @param {string} key The key of the value to remove.
9173    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
9174    */
9175   function stackDelete(key) {
9176     return this.__data__['delete'](key);
9177   }
9178
9179   /**
9180    * Gets the stack value for `key`.
9181    *
9182    * @private
9183    * @name get
9184    * @memberOf Stack
9185    * @param {string} key The key of the value to get.
9186    * @returns {*} Returns the entry value.
9187    */
9188   function stackGet(key) {
9189     return this.__data__.get(key);
9190   }
9191
9192   /**
9193    * Checks if a stack value for `key` exists.
9194    *
9195    * @private
9196    * @name has
9197    * @memberOf Stack
9198    * @param {string} key The key of the entry to check.
9199    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
9200    */
9201   function stackHas(key) {
9202     return this.__data__.has(key);
9203   }
9204
9205   /**
9206    * Sets the stack `key` to `value`.
9207    *
9208    * @private
9209    * @name set
9210    * @memberOf Stack
9211    * @param {string} key The key of the value to set.
9212    * @param {*} value The value to set.
9213    * @returns {Object} Returns the stack cache instance.
9214    */
9215   function stackSet(key, value) {
9216     var cache = this.__data__;
9217     if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) {
9218       cache = this.__data__ = new MapCache(cache.__data__);
9219     }
9220     cache.set(key, value);
9221     return this;
9222   }
9223
9224   // Add methods to `Stack`.
9225   Stack.prototype.clear = stackClear;
9226   Stack.prototype['delete'] = stackDelete;
9227   Stack.prototype.get = stackGet;
9228   Stack.prototype.has = stackHas;
9229   Stack.prototype.set = stackSet;
9230
9231   /*------------------------------------------------------------------------*/
9232
9233   /**
9234    * This function is like `assignValue` except that it doesn't assign
9235    * `undefined` values.
9236    *
9237    * @private
9238    * @param {Object} object The object to modify.
9239    * @param {string} key The key of the property to assign.
9240    * @param {*} value The value to assign.
9241    */
9242   function assignMergeValue(object, key, value) {
9243     if ((value !== undefined && !eq(object[key], value)) ||
9244         (typeof key == 'number' && value === undefined && !(key in object))) {
9245       object[key] = value;
9246     }
9247   }
9248
9249   /**
9250    * Assigns `value` to `key` of `object` if the existing value is not equivalent
9251    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
9252    * for equality comparisons.
9253    *
9254    * @private
9255    * @param {Object} object The object to modify.
9256    * @param {string} key The key of the property to assign.
9257    * @param {*} value The value to assign.
9258    */
9259   function assignValue(object, key, value) {
9260     var objValue = object[key];
9261     if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
9262         (value === undefined && !(key in object))) {
9263       object[key] = value;
9264     }
9265   }
9266
9267   /**
9268    * Gets the index at which the `key` is found in `array` of key-value pairs.
9269    *
9270    * @private
9271    * @param {Array} array The array to search.
9272    * @param {*} key The key to search for.
9273    * @returns {number} Returns the index of the matched value, else `-1`.
9274    */
9275   function assocIndexOf(array, key) {
9276     var length = array.length;
9277     while (length--) {
9278       if (eq(array[length][0], key)) {
9279         return length;
9280       }
9281     }
9282     return -1;
9283   }
9284
9285   /**
9286    * Aggregates elements of `collection` on `accumulator` with keys transformed
9287    * by `iteratee` and values set by `setter`.
9288    *
9289    * @private
9290    * @param {Array|Object} collection The collection to iterate over.
9291    * @param {Function} setter The function to set `accumulator` values.
9292    * @param {Function} iteratee The iteratee to transform keys.
9293    * @param {Object} accumulator The initial aggregated object.
9294    * @returns {Function} Returns `accumulator`.
9295    */
9296   function baseAggregator(collection, setter, iteratee, accumulator) {
9297     baseEach(collection, function(value, key, collection) {
9298       setter(accumulator, value, iteratee(value), collection);
9299     });
9300     return accumulator;
9301   }
9302
9303   /**
9304    * The base implementation of `_.assign` without support for multiple sources
9305    * or `customizer` functions.
9306    *
9307    * @private
9308    * @param {Object} object The destination object.
9309    * @param {Object} source The source object.
9310    * @returns {Object} Returns `object`.
9311    */
9312   function baseAssign(object, source) {
9313     return object && copyObject(source, keys(source), object);
9314   }
9315
9316   /**
9317    * The base implementation of `_.at` without support for individual paths.
9318    *
9319    * @private
9320    * @param {Object} object The object to iterate over.
9321    * @param {string[]} paths The property paths of elements to pick.
9322    * @returns {Array} Returns the picked elements.
9323    */
9324   function baseAt(object, paths) {
9325     var index = -1,
9326         isNil = object == null,
9327         length = paths.length,
9328         result = Array(length);
9329
9330     while (++index < length) {
9331       result[index] = isNil ? undefined : get(object, paths[index]);
9332     }
9333     return result;
9334   }
9335
9336   /**
9337    * The base implementation of `_.clone` and `_.cloneDeep` which tracks
9338    * traversed objects.
9339    *
9340    * @private
9341    * @param {*} value The value to clone.
9342    * @param {boolean} [isDeep] Specify a deep clone.
9343    * @param {boolean} [isFull] Specify a clone including symbols.
9344    * @param {Function} [customizer] The function to customize cloning.
9345    * @param {string} [key] The key of `value`.
9346    * @param {Object} [object] The parent object of `value`.
9347    * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
9348    * @returns {*} Returns the cloned value.
9349    */
9350   function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
9351     var result;
9352     if (customizer) {
9353       result = object ? customizer(value, key, object, stack) : customizer(value);
9354     }
9355     if (result !== undefined) {
9356       return result;
9357     }
9358     if (!isObject(value)) {
9359       return value;
9360     }
9361     var isArr = isArray(value);
9362     if (isArr) {
9363       result = initCloneArray(value);
9364       if (!isDeep) {
9365         return copyArray(value, result);
9366       }
9367     } else {
9368       var tag = getTag(value),
9369           isFunc = tag == funcTag || tag == genTag;
9370
9371       if (isBuffer(value)) {
9372         return cloneBuffer(value, isDeep);
9373       }
9374       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
9375         if (isHostObject(value)) {
9376           return object ? value : {};
9377         }
9378         result = initCloneObject(isFunc ? {} : value);
9379         if (!isDeep) {
9380           return copySymbols(value, baseAssign(result, value));
9381         }
9382       } else {
9383         if (!cloneableTags[tag]) {
9384           return object ? value : {};
9385         }
9386         result = initCloneByTag(value, tag, baseClone, isDeep);
9387       }
9388     }
9389     // Check for circular references and return its corresponding clone.
9390     stack || (stack = new Stack);
9391     var stacked = stack.get(value);
9392     if (stacked) {
9393       return stacked;
9394     }
9395     stack.set(value, result);
9396
9397     if (!isArr) {
9398       var props = isFull ? getAllKeys(value) : keys(value);
9399     }
9400     // Recursively populate clone (susceptible to call stack limits).
9401     arrayEach(props || value, function(subValue, key) {
9402       if (props) {
9403         key = subValue;
9404         subValue = value[key];
9405       }
9406       assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
9407     });
9408     return result;
9409   }
9410
9411   /**
9412    * The base implementation of `_.create` without support for assigning
9413    * properties to the created object.
9414    *
9415    * @private
9416    * @param {Object} prototype The object to inherit from.
9417    * @returns {Object} Returns the new object.
9418    */
9419   function baseCreate(proto) {
9420     return isObject(proto) ? objectCreate(proto) : {};
9421   }
9422
9423   /**
9424    * The base implementation of methods like `_.difference` without support
9425    * for excluding multiple arrays or iteratee shorthands.
9426    *
9427    * @private
9428    * @param {Array} array The array to inspect.
9429    * @param {Array} values The values to exclude.
9430    * @param {Function} [iteratee] The iteratee invoked per element.
9431    * @param {Function} [comparator] The comparator invoked per element.
9432    * @returns {Array} Returns the new array of filtered values.
9433    */
9434   function baseDifference(array, values, iteratee, comparator) {
9435     var index = -1,
9436         includes = arrayIncludes,
9437         isCommon = true,
9438         length = array.length,
9439         result = [],
9440         valuesLength = values.length;
9441
9442     if (!length) {
9443       return result;
9444     }
9445     if (iteratee) {
9446       values = arrayMap(values, baseUnary(iteratee));
9447     }
9448     if (comparator) {
9449       includes = arrayIncludesWith;
9450       isCommon = false;
9451     }
9452     else if (values.length >= LARGE_ARRAY_SIZE) {
9453       includes = cacheHas;
9454       isCommon = false;
9455       values = new SetCache(values);
9456     }
9457     outer:
9458     while (++index < length) {
9459       var value = array[index],
9460           computed = iteratee ? iteratee(value) : value;
9461
9462       value = (comparator || value !== 0) ? value : 0;
9463       if (isCommon && computed === computed) {
9464         var valuesIndex = valuesLength;
9465         while (valuesIndex--) {
9466           if (values[valuesIndex] === computed) {
9467             continue outer;
9468           }
9469         }
9470         result.push(value);
9471       }
9472       else if (!includes(values, computed, comparator)) {
9473         result.push(value);
9474       }
9475     }
9476     return result;
9477   }
9478
9479   /**
9480    * The base implementation of `_.forEach` without support for iteratee shorthands.
9481    *
9482    * @private
9483    * @param {Array|Object} collection The collection to iterate over.
9484    * @param {Function} iteratee The function invoked per iteration.
9485    * @returns {Array|Object} Returns `collection`.
9486    */
9487   var baseEach = createBaseEach(baseForOwn);
9488
9489   /**
9490    * The base implementation of `_.every` without support for iteratee shorthands.
9491    *
9492    * @private
9493    * @param {Array|Object} collection The collection to iterate over.
9494    * @param {Function} predicate The function invoked per iteration.
9495    * @returns {boolean} Returns `true` if all elements pass the predicate check,
9496    *  else `false`
9497    */
9498   function baseEvery(collection, predicate) {
9499     var result = true;
9500     baseEach(collection, function(value, index, collection) {
9501       result = !!predicate(value, index, collection);
9502       return result;
9503     });
9504     return result;
9505   }
9506
9507   /**
9508    * The base implementation of `_.filter` without support for iteratee shorthands.
9509    *
9510    * @private
9511    * @param {Array|Object} collection The collection to iterate over.
9512    * @param {Function} predicate The function invoked per iteration.
9513    * @returns {Array} Returns the new filtered array.
9514    */
9515   function baseFilter(collection, predicate) {
9516     var result = [];
9517     baseEach(collection, function(value, index, collection) {
9518       if (predicate(value, index, collection)) {
9519         result.push(value);
9520       }
9521     });
9522     return result;
9523   }
9524
9525   /**
9526    * The base implementation of `_.flatten` with support for restricting flattening.
9527    *
9528    * @private
9529    * @param {Array} array The array to flatten.
9530    * @param {number} depth The maximum recursion depth.
9531    * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
9532    * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
9533    * @param {Array} [result=[]] The initial result value.
9534    * @returns {Array} Returns the new flattened array.
9535    */
9536   function baseFlatten(array, depth, predicate, isStrict, result) {
9537     var index = -1,
9538         length = array.length;
9539
9540     predicate || (predicate = isFlattenable);
9541     result || (result = []);
9542
9543     while (++index < length) {
9544       var value = array[index];
9545       if (depth > 0 && predicate(value)) {
9546         if (depth > 1) {
9547           // Recursively flatten arrays (susceptible to call stack limits).
9548           baseFlatten(value, depth - 1, predicate, isStrict, result);
9549         } else {
9550           arrayPush(result, value);
9551         }
9552       } else if (!isStrict) {
9553         result[result.length] = value;
9554       }
9555     }
9556     return result;
9557   }
9558
9559   /**
9560    * The base implementation of `baseForOwn` which iterates over `object`
9561    * properties returned by `keysFunc` and invokes `iteratee` for each property.
9562    * Iteratee functions may exit iteration early by explicitly returning `false`.
9563    *
9564    * @private
9565    * @param {Object} object The object to iterate over.
9566    * @param {Function} iteratee The function invoked per iteration.
9567    * @param {Function} keysFunc The function to get the keys of `object`.
9568    * @returns {Object} Returns `object`.
9569    */
9570   var baseFor = createBaseFor();
9571
9572   /**
9573    * The base implementation of `_.forOwn` without support for iteratee shorthands.
9574    *
9575    * @private
9576    * @param {Object} object The object to iterate over.
9577    * @param {Function} iteratee The function invoked per iteration.
9578    * @returns {Object} Returns `object`.
9579    */
9580   function baseForOwn(object, iteratee) {
9581     return object && baseFor(object, iteratee, keys);
9582   }
9583
9584   /**
9585    * The base implementation of `_.functions` which creates an array of
9586    * `object` function property names filtered from `props`.
9587    *
9588    * @private
9589    * @param {Object} object The object to inspect.
9590    * @param {Array} props The property names to filter.
9591    * @returns {Array} Returns the function names.
9592    */
9593   function baseFunctions(object, props) {
9594     return arrayFilter(props, function(key) {
9595       return isFunction(object[key]);
9596     });
9597   }
9598
9599   /**
9600    * The base implementation of `_.get` without support for default values.
9601    *
9602    * @private
9603    * @param {Object} object The object to query.
9604    * @param {Array|string} path The path of the property to get.
9605    * @returns {*} Returns the resolved value.
9606    */
9607   function baseGet(object, path) {
9608     path = isKey(path, object) ? [path] : castPath(path);
9609
9610     var index = 0,
9611         length = path.length;
9612
9613     while (object != null && index < length) {
9614       object = object[toKey(path[index++])];
9615     }
9616     return (index && index == length) ? object : undefined;
9617   }
9618
9619   /**
9620    * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
9621    * `keysFunc` and `symbolsFunc` to get the enumerable property names and
9622    * symbols of `object`.
9623    *
9624    * @private
9625    * @param {Object} object The object to query.
9626    * @param {Function} keysFunc The function to get the keys of `object`.
9627    * @param {Function} symbolsFunc The function to get the symbols of `object`.
9628    * @returns {Array} Returns the array of property names and symbols.
9629    */
9630   function baseGetAllKeys(object, keysFunc, symbolsFunc) {
9631     var result = keysFunc(object);
9632     return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
9633   }
9634
9635   /**
9636    * The base implementation of `_.has` without support for deep paths.
9637    *
9638    * @private
9639    * @param {Object} object The object to query.
9640    * @param {Array|string} key The key to check.
9641    * @returns {boolean} Returns `true` if `key` exists, else `false`.
9642    */
9643   function baseHas(object, key) {
9644     // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`,
9645     // that are composed entirely of index properties, return `false` for
9646     // `hasOwnProperty` checks of them.
9647     return hasOwnProperty.call(object, key) ||
9648       (typeof object == 'object' && key in object && getPrototype(object) === null);
9649   }
9650
9651   /**
9652    * The base implementation of `_.hasIn` without support for deep paths.
9653    *
9654    * @private
9655    * @param {Object} object The object to query.
9656    * @param {Array|string} key The key to check.
9657    * @returns {boolean} Returns `true` if `key` exists, else `false`.
9658    */
9659   function baseHasIn(object, key) {
9660     return key in Object(object);
9661   }
9662
9663   /**
9664    * The base implementation of methods like `_.intersection`, without support
9665    * for iteratee shorthands, that accepts an array of arrays to inspect.
9666    *
9667    * @private
9668    * @param {Array} arrays The arrays to inspect.
9669    * @param {Function} [iteratee] The iteratee invoked per element.
9670    * @param {Function} [comparator] The comparator invoked per element.
9671    * @returns {Array} Returns the new array of shared values.
9672    */
9673   function baseIntersection(arrays, iteratee, comparator) {
9674     var includes = comparator ? arrayIncludesWith : arrayIncludes,
9675         length = arrays[0].length,
9676         othLength = arrays.length,
9677         othIndex = othLength,
9678         caches = Array(othLength),
9679         maxLength = Infinity,
9680         result = [];
9681
9682     while (othIndex--) {
9683       var array = arrays[othIndex];
9684       if (othIndex && iteratee) {
9685         array = arrayMap(array, baseUnary(iteratee));
9686       }
9687       maxLength = nativeMin(array.length, maxLength);
9688       caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
9689         ? new SetCache(othIndex && array)
9690         : undefined;
9691     }
9692     array = arrays[0];
9693
9694     var index = -1,
9695         seen = caches[0];
9696
9697     outer:
9698     while (++index < length && result.length < maxLength) {
9699       var value = array[index],
9700           computed = iteratee ? iteratee(value) : value;
9701
9702       value = (comparator || value !== 0) ? value : 0;
9703       if (!(seen
9704             ? cacheHas(seen, computed)
9705             : includes(result, computed, comparator)
9706           )) {
9707         othIndex = othLength;
9708         while (--othIndex) {
9709           var cache = caches[othIndex];
9710           if (!(cache
9711                 ? cacheHas(cache, computed)
9712                 : includes(arrays[othIndex], computed, comparator))
9713               ) {
9714             continue outer;
9715           }
9716         }
9717         if (seen) {
9718           seen.push(computed);
9719         }
9720         result.push(value);
9721       }
9722     }
9723     return result;
9724   }
9725
9726   /**
9727    * The base implementation of `_.invoke` without support for individual
9728    * method arguments.
9729    *
9730    * @private
9731    * @param {Object} object The object to query.
9732    * @param {Array|string} path The path of the method to invoke.
9733    * @param {Array} args The arguments to invoke the method with.
9734    * @returns {*} Returns the result of the invoked method.
9735    */
9736   function baseInvoke(object, path, args) {
9737     if (!isKey(path, object)) {
9738       path = castPath(path);
9739       object = parent(object, path);
9740       path = last(path);
9741     }
9742     var func = object == null ? object : object[toKey(path)];
9743     return func == null ? undefined : apply(func, object, args);
9744   }
9745
9746   /**
9747    * The base implementation of `_.isEqual` which supports partial comparisons
9748    * and tracks traversed objects.
9749    *
9750    * @private
9751    * @param {*} value The value to compare.
9752    * @param {*} other The other value to compare.
9753    * @param {Function} [customizer] The function to customize comparisons.
9754    * @param {boolean} [bitmask] The bitmask of comparison flags.
9755    *  The bitmask may be composed of the following flags:
9756    *     1 - Unordered comparison
9757    *     2 - Partial comparison
9758    * @param {Object} [stack] Tracks traversed `value` and `other` objects.
9759    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9760    */
9761   function baseIsEqual(value, other, customizer, bitmask, stack) {
9762     if (value === other) {
9763       return true;
9764     }
9765     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9766       return value !== value && other !== other;
9767     }
9768     return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);
9769   }
9770
9771   /**
9772    * A specialized version of `baseIsEqual` for arrays and objects which performs
9773    * deep comparisons and tracks traversed objects enabling objects with circular
9774    * references to be compared.
9775    *
9776    * @private
9777    * @param {Object} object The object to compare.
9778    * @param {Object} other The other object to compare.
9779    * @param {Function} equalFunc The function to determine equivalents of values.
9780    * @param {Function} [customizer] The function to customize comparisons.
9781    * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`
9782    *  for more details.
9783    * @param {Object} [stack] Tracks traversed `object` and `other` objects.
9784    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9785    */
9786   function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {
9787     var objIsArr = isArray(object),
9788         othIsArr = isArray(other),
9789         objTag = arrayTag,
9790         othTag = arrayTag;
9791
9792     if (!objIsArr) {
9793       objTag = getTag(object);
9794       objTag = objTag == argsTag ? objectTag : objTag;
9795     }
9796     if (!othIsArr) {
9797       othTag = getTag(other);
9798       othTag = othTag == argsTag ? objectTag : othTag;
9799     }
9800     var objIsObj = objTag == objectTag && !isHostObject(object),
9801         othIsObj = othTag == objectTag && !isHostObject(other),
9802         isSameTag = objTag == othTag;
9803
9804     if (isSameTag && !objIsObj) {
9805       stack || (stack = new Stack);
9806       return (objIsArr || isTypedArray(object))
9807         ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)
9808         : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);
9809     }
9810     if (!(bitmask & PARTIAL_COMPARE_FLAG)) {
9811       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9812           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9813
9814       if (objIsWrapped || othIsWrapped) {
9815         var objUnwrapped = objIsWrapped ? object.value() : object,
9816             othUnwrapped = othIsWrapped ? other.value() : other;
9817
9818         stack || (stack = new Stack);
9819         return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);
9820       }
9821     }
9822     if (!isSameTag) {
9823       return false;
9824     }
9825     stack || (stack = new Stack);
9826     return equalObjects(object, other, equalFunc, customizer, bitmask, stack);
9827   }
9828
9829   /**
9830    * The base implementation of `_.isMatch` without support for iteratee shorthands.
9831    *
9832    * @private
9833    * @param {Object} object The object to inspect.
9834    * @param {Object} source The object of property values to match.
9835    * @param {Array} matchData The property names, values, and compare flags to match.
9836    * @param {Function} [customizer] The function to customize comparisons.
9837    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9838    */
9839   function baseIsMatch(object, source, matchData, customizer) {
9840     var index = matchData.length,
9841         length = index,
9842         noCustomizer = !customizer;
9843
9844     if (object == null) {
9845       return !length;
9846     }
9847     object = Object(object);
9848     while (index--) {
9849       var data = matchData[index];
9850       if ((noCustomizer && data[2])
9851             ? data[1] !== object[data[0]]
9852             : !(data[0] in object)
9853           ) {
9854         return false;
9855       }
9856     }
9857     while (++index < length) {
9858       data = matchData[index];
9859       var key = data[0],
9860           objValue = object[key],
9861           srcValue = data[1];
9862
9863       if (noCustomizer && data[2]) {
9864         if (objValue === undefined && !(key in object)) {
9865           return false;
9866         }
9867       } else {
9868         var stack = new Stack;
9869         if (customizer) {
9870           var result = customizer(objValue, srcValue, key, object, source, stack);
9871         }
9872         if (!(result === undefined
9873               ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)
9874               : result
9875             )) {
9876           return false;
9877         }
9878       }
9879     }
9880     return true;
9881   }
9882
9883   /**
9884    * The base implementation of `_.iteratee`.
9885    *
9886    * @private
9887    * @param {*} [value=_.identity] The value to convert to an iteratee.
9888    * @returns {Function} Returns the iteratee.
9889    */
9890   function baseIteratee(value) {
9891     // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
9892     // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
9893     if (typeof value == 'function') {
9894       return value;
9895     }
9896     if (value == null) {
9897       return identity;
9898     }
9899     if (typeof value == 'object') {
9900       return isArray(value)
9901         ? baseMatchesProperty(value[0], value[1])
9902         : baseMatches(value);
9903     }
9904     return property(value);
9905   }
9906
9907   /**
9908    * The base implementation of `_.keys` which doesn't skip the constructor
9909    * property of prototypes or treat sparse arrays as dense.
9910    *
9911    * @private
9912    * @param {Object} object The object to query.
9913    * @returns {Array} Returns the array of property names.
9914    */
9915   function baseKeys(object) {
9916     return nativeKeys(Object(object));
9917   }
9918
9919   /**
9920    * The base implementation of `_.keysIn` which doesn't skip the constructor
9921    * property of prototypes or treat sparse arrays as dense.
9922    *
9923    * @private
9924    * @param {Object} object The object to query.
9925    * @returns {Array} Returns the array of property names.
9926    */
9927   function baseKeysIn(object) {
9928     object = object == null ? object : Object(object);
9929
9930     var result = [];
9931     for (var key in object) {
9932       result.push(key);
9933     }
9934     return result;
9935   }
9936
9937   // Fallback for IE < 9 with es6-shim.
9938   if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
9939     baseKeysIn = function(object) {
9940       return iteratorToArray(enumerate(object));
9941     };
9942   }
9943
9944   /**
9945    * The base implementation of `_.map` without support for iteratee shorthands.
9946    *
9947    * @private
9948    * @param {Array|Object} collection The collection to iterate over.
9949    * @param {Function} iteratee The function invoked per iteration.
9950    * @returns {Array} Returns the new mapped array.
9951    */
9952   function baseMap(collection, iteratee) {
9953     var index = -1,
9954         result = isArrayLike(collection) ? Array(collection.length) : [];
9955
9956     baseEach(collection, function(value, key, collection) {
9957       result[++index] = iteratee(value, key, collection);
9958     });
9959     return result;
9960   }
9961
9962   /**
9963    * The base implementation of `_.matches` which doesn't clone `source`.
9964    *
9965    * @private
9966    * @param {Object} source The object of property values to match.
9967    * @returns {Function} Returns the new spec function.
9968    */
9969   function baseMatches(source) {
9970     var matchData = getMatchData(source);
9971     if (matchData.length == 1 && matchData[0][2]) {
9972       return matchesStrictComparable(matchData[0][0], matchData[0][1]);
9973     }
9974     return function(object) {
9975       return object === source || baseIsMatch(object, source, matchData);
9976     };
9977   }
9978
9979   /**
9980    * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
9981    *
9982    * @private
9983    * @param {string} path The path of the property to get.
9984    * @param {*} srcValue The value to match.
9985    * @returns {Function} Returns the new spec function.
9986    */
9987   function baseMatchesProperty(path, srcValue) {
9988     if (isKey(path) && isStrictComparable(srcValue)) {
9989       return matchesStrictComparable(toKey(path), srcValue);
9990     }
9991     return function(object) {
9992       var objValue = get(object, path);
9993       return (objValue === undefined && objValue === srcValue)
9994         ? hasIn(object, path)
9995         : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
9996     };
9997   }
9998
9999   /**
10000    * The base implementation of `_.merge` without support for multiple sources.
10001    *
10002    * @private
10003    * @param {Object} object The destination object.
10004    * @param {Object} source The source object.
10005    * @param {number} srcIndex The index of `source`.
10006    * @param {Function} [customizer] The function to customize merged values.
10007    * @param {Object} [stack] Tracks traversed source values and their merged
10008    *  counterparts.
10009    */
10010   function baseMerge(object, source, srcIndex, customizer, stack) {
10011     if (object === source) {
10012       return;
10013     }
10014     if (!(isArray(source) || isTypedArray(source))) {
10015       var props = keysIn(source);
10016     }
10017     arrayEach(props || source, function(srcValue, key) {
10018       if (props) {
10019         key = srcValue;
10020         srcValue = source[key];
10021       }
10022       if (isObject(srcValue)) {
10023         stack || (stack = new Stack);
10024         baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
10025       }
10026       else {
10027         var newValue = customizer
10028           ? customizer(object[key], srcValue, (key + ''), object, source, stack)
10029           : undefined;
10030
10031         if (newValue === undefined) {
10032           newValue = srcValue;
10033         }
10034         assignMergeValue(object, key, newValue);
10035       }
10036     });
10037   }
10038
10039   /**
10040    * A specialized version of `baseMerge` for arrays and objects which performs
10041    * deep merges and tracks traversed objects enabling objects with circular
10042    * references to be merged.
10043    *
10044    * @private
10045    * @param {Object} object The destination object.
10046    * @param {Object} source The source object.
10047    * @param {string} key The key of the value to merge.
10048    * @param {number} srcIndex The index of `source`.
10049    * @param {Function} mergeFunc The function to merge values.
10050    * @param {Function} [customizer] The function to customize assigned values.
10051    * @param {Object} [stack] Tracks traversed source values and their merged
10052    *  counterparts.
10053    */
10054   function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
10055     var objValue = object[key],
10056         srcValue = source[key],
10057         stacked = stack.get(srcValue);
10058
10059     if (stacked) {
10060       assignMergeValue(object, key, stacked);
10061       return;
10062     }
10063     var newValue = customizer
10064       ? customizer(objValue, srcValue, (key + ''), object, source, stack)
10065       : undefined;
10066
10067     var isCommon = newValue === undefined;
10068
10069     if (isCommon) {
10070       newValue = srcValue;
10071       if (isArray(srcValue) || isTypedArray(srcValue)) {
10072         if (isArray(objValue)) {
10073           newValue = objValue;
10074         }
10075         else if (isArrayLikeObject(objValue)) {
10076           newValue = copyArray(objValue);
10077         }
10078         else {
10079           isCommon = false;
10080           newValue = baseClone(srcValue, true);
10081         }
10082       }
10083       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
10084         if (isArguments(objValue)) {
10085           newValue = toPlainObject(objValue);
10086         }
10087         else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
10088           isCommon = false;
10089           newValue = baseClone(srcValue, true);
10090         }
10091         else {
10092           newValue = objValue;
10093         }
10094       }
10095       else {
10096         isCommon = false;
10097       }
10098     }
10099     stack.set(srcValue, newValue);
10100
10101     if (isCommon) {
10102       // Recursively merge objects and arrays (susceptible to call stack limits).
10103       mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
10104     }
10105     stack['delete'](srcValue);
10106     assignMergeValue(object, key, newValue);
10107   }
10108
10109   /**
10110    * The base implementation of `_.pick` without support for individual
10111    * property identifiers.
10112    *
10113    * @private
10114    * @param {Object} object The source object.
10115    * @param {string[]} props The property identifiers to pick.
10116    * @returns {Object} Returns the new object.
10117    */
10118   function basePick(object, props) {
10119     object = Object(object);
10120     return arrayReduce(props, function(result, key) {
10121       if (key in object) {
10122         result[key] = object[key];
10123       }
10124       return result;
10125     }, {});
10126   }
10127
10128   /**
10129    * The base implementation of `_.property` without support for deep paths.
10130    *
10131    * @private
10132    * @param {string} key The key of the property to get.
10133    * @returns {Function} Returns the new accessor function.
10134    */
10135   function baseProperty(key) {
10136     return function(object) {
10137       return object == null ? undefined : object[key];
10138     };
10139   }
10140
10141   /**
10142    * A specialized version of `baseProperty` which supports deep paths.
10143    *
10144    * @private
10145    * @param {Array|string} path The path of the property to get.
10146    * @returns {Function} Returns the new accessor function.
10147    */
10148   function basePropertyDeep(path) {
10149     return function(object) {
10150       return baseGet(object, path);
10151     };
10152   }
10153
10154   /**
10155    * The base implementation of `setData` without support for hot loop detection.
10156    *
10157    * @private
10158    * @param {Function} func The function to associate metadata with.
10159    * @param {*} data The metadata.
10160    * @returns {Function} Returns `func`.
10161    */
10162   var baseSetData = !metaMap ? identity : function(func, data) {
10163     metaMap.set(func, data);
10164     return func;
10165   };
10166
10167   /**
10168    * The base implementation of `_.slice` without an iteratee call guard.
10169    *
10170    * @private
10171    * @param {Array} array The array to slice.
10172    * @param {number} [start=0] The start position.
10173    * @param {number} [end=array.length] The end position.
10174    * @returns {Array} Returns the slice of `array`.
10175    */
10176   function baseSlice(array, start, end) {
10177     var index = -1,
10178         length = array.length;
10179
10180     if (start < 0) {
10181       start = -start > length ? 0 : (length + start);
10182     }
10183     end = end > length ? length : end;
10184     if (end < 0) {
10185       end += length;
10186     }
10187     length = start > end ? 0 : ((end - start) >>> 0);
10188     start >>>= 0;
10189
10190     var result = Array(length);
10191     while (++index < length) {
10192       result[index] = array[index + start];
10193     }
10194     return result;
10195   }
10196
10197   /**
10198    * The base implementation of `_.some` without support for iteratee shorthands.
10199    *
10200    * @private
10201    * @param {Array|Object} collection The collection to iterate over.
10202    * @param {Function} predicate The function invoked per iteration.
10203    * @returns {boolean} Returns `true` if any element passes the predicate check,
10204    *  else `false`.
10205    */
10206   function baseSome(collection, predicate) {
10207     var result;
10208
10209     baseEach(collection, function(value, index, collection) {
10210       result = predicate(value, index, collection);
10211       return !result;
10212     });
10213     return !!result;
10214   }
10215
10216   /**
10217    * The base implementation of `_.toString` which doesn't convert nullish
10218    * values to empty strings.
10219    *
10220    * @private
10221    * @param {*} value The value to process.
10222    * @returns {string} Returns the string.
10223    */
10224   function baseToString(value) {
10225     // Exit early for strings to avoid a performance hit in some environments.
10226     if (typeof value == 'string') {
10227       return value;
10228     }
10229     if (isSymbol(value)) {
10230       return symbolToString ? symbolToString.call(value) : '';
10231     }
10232     var result = (value + '');
10233     return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
10234   }
10235
10236   /**
10237    * The base implementation of `_.uniqBy` without support for iteratee shorthands.
10238    *
10239    * @private
10240    * @param {Array} array The array to inspect.
10241    * @param {Function} [iteratee] The iteratee invoked per element.
10242    * @param {Function} [comparator] The comparator invoked per element.
10243    * @returns {Array} Returns the new duplicate free array.
10244    */
10245   function baseUniq(array, iteratee, comparator) {
10246     var index = -1,
10247         includes = arrayIncludes,
10248         length = array.length,
10249         isCommon = true,
10250         result = [],
10251         seen = result;
10252
10253     if (comparator) {
10254       isCommon = false;
10255       includes = arrayIncludesWith;
10256     }
10257     else if (length >= LARGE_ARRAY_SIZE) {
10258       var set = iteratee ? null : createSet(array);
10259       if (set) {
10260         return setToArray(set);
10261       }
10262       isCommon = false;
10263       includes = cacheHas;
10264       seen = new SetCache;
10265     }
10266     else {
10267       seen = iteratee ? [] : result;
10268     }
10269     outer:
10270     while (++index < length) {
10271       var value = array[index],
10272           computed = iteratee ? iteratee(value) : value;
10273
10274       value = (comparator || value !== 0) ? value : 0;
10275       if (isCommon && computed === computed) {
10276         var seenIndex = seen.length;
10277         while (seenIndex--) {
10278           if (seen[seenIndex] === computed) {
10279             continue outer;
10280           }
10281         }
10282         if (iteratee) {
10283           seen.push(computed);
10284         }
10285         result.push(value);
10286       }
10287       else if (!includes(seen, computed, comparator)) {
10288         if (seen !== result) {
10289           seen.push(computed);
10290         }
10291         result.push(value);
10292       }
10293     }
10294     return result;
10295   }
10296
10297   /**
10298    * The base implementation of `wrapperValue` which returns the result of
10299    * performing a sequence of actions on the unwrapped `value`, where each
10300    * successive action is supplied the return value of the previous.
10301    *
10302    * @private
10303    * @param {*} value The unwrapped value.
10304    * @param {Array} actions Actions to perform to resolve the unwrapped value.
10305    * @returns {*} Returns the resolved value.
10306    */
10307   function baseWrapperValue(value, actions) {
10308     var result = value;
10309     if (result instanceof LazyWrapper) {
10310       result = result.value();
10311     }
10312     return arrayReduce(actions, function(result, action) {
10313       return action.func.apply(action.thisArg, arrayPush([result], action.args));
10314     }, result);
10315   }
10316
10317   /**
10318    * Casts `value` to an empty array if it's not an array like object.
10319    *
10320    * @private
10321    * @param {*} value The value to inspect.
10322    * @returns {Array|Object} Returns the cast array-like object.
10323    */
10324   function castArrayLikeObject(value) {
10325     return isArrayLikeObject(value) ? value : [];
10326   }
10327
10328   /**
10329    * Casts `value` to a path array if it's not one.
10330    *
10331    * @private
10332    * @param {*} value The value to inspect.
10333    * @returns {Array} Returns the cast property path array.
10334    */
10335   function castPath(value) {
10336     return isArray(value) ? value : stringToPath(value);
10337   }
10338
10339   /**
10340    * Creates a clone of  `buffer`.
10341    *
10342    * @private
10343    * @param {Buffer} buffer The buffer to clone.
10344    * @param {boolean} [isDeep] Specify a deep clone.
10345    * @returns {Buffer} Returns the cloned buffer.
10346    */
10347   function cloneBuffer(buffer, isDeep) {
10348     if (isDeep) {
10349       return buffer.slice();
10350     }
10351     var result = new buffer.constructor(buffer.length);
10352     buffer.copy(result);
10353     return result;
10354   }
10355
10356   /**
10357    * Creates a clone of `arrayBuffer`.
10358    *
10359    * @private
10360    * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
10361    * @returns {ArrayBuffer} Returns the cloned array buffer.
10362    */
10363   function cloneArrayBuffer(arrayBuffer) {
10364     var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
10365     new Uint8Array(result).set(new Uint8Array(arrayBuffer));
10366     return result;
10367   }
10368
10369   /**
10370    * Creates a clone of `dataView`.
10371    *
10372    * @private
10373    * @param {Object} dataView The data view to clone.
10374    * @param {boolean} [isDeep] Specify a deep clone.
10375    * @returns {Object} Returns the cloned data view.
10376    */
10377   function cloneDataView(dataView, isDeep) {
10378     var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
10379     return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
10380   }
10381
10382   /**
10383    * Creates a clone of `map`.
10384    *
10385    * @private
10386    * @param {Object} map The map to clone.
10387    * @param {Function} cloneFunc The function to clone values.
10388    * @param {boolean} [isDeep] Specify a deep clone.
10389    * @returns {Object} Returns the cloned map.
10390    */
10391   function cloneMap(map, isDeep, cloneFunc) {
10392     var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
10393     return arrayReduce(array, addMapEntry, new map.constructor);
10394   }
10395
10396   /**
10397    * Creates a clone of `regexp`.
10398    *
10399    * @private
10400    * @param {Object} regexp The regexp to clone.
10401    * @returns {Object} Returns the cloned regexp.
10402    */
10403   function cloneRegExp(regexp) {
10404     var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
10405     result.lastIndex = regexp.lastIndex;
10406     return result;
10407   }
10408
10409   /**
10410    * Creates a clone of `set`.
10411    *
10412    * @private
10413    * @param {Object} set The set to clone.
10414    * @param {Function} cloneFunc The function to clone values.
10415    * @param {boolean} [isDeep] Specify a deep clone.
10416    * @returns {Object} Returns the cloned set.
10417    */
10418   function cloneSet(set, isDeep, cloneFunc) {
10419     var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
10420     return arrayReduce(array, addSetEntry, new set.constructor);
10421   }
10422
10423   /**
10424    * Creates a clone of the `symbol` object.
10425    *
10426    * @private
10427    * @param {Object} symbol The symbol object to clone.
10428    * @returns {Object} Returns the cloned symbol object.
10429    */
10430   function cloneSymbol(symbol) {
10431     return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
10432   }
10433
10434   /**
10435    * Creates a clone of `typedArray`.
10436    *
10437    * @private
10438    * @param {Object} typedArray The typed array to clone.
10439    * @param {boolean} [isDeep] Specify a deep clone.
10440    * @returns {Object} Returns the cloned typed array.
10441    */
10442   function cloneTypedArray(typedArray, isDeep) {
10443     var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
10444     return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
10445   }
10446
10447   /**
10448    * Creates an array that is the composition of partially applied arguments,
10449    * placeholders, and provided arguments into a single array of arguments.
10450    *
10451    * @private
10452    * @param {Array} args The provided arguments.
10453    * @param {Array} partials The arguments to prepend to those provided.
10454    * @param {Array} holders The `partials` placeholder indexes.
10455    * @params {boolean} [isCurried] Specify composing for a curried function.
10456    * @returns {Array} Returns the new array of composed arguments.
10457    */
10458   function composeArgs(args, partials, holders, isCurried) {
10459     var argsIndex = -1,
10460         argsLength = args.length,
10461         holdersLength = holders.length,
10462         leftIndex = -1,
10463         leftLength = partials.length,
10464         rangeLength = nativeMax(argsLength - holdersLength, 0),
10465         result = Array(leftLength + rangeLength),
10466         isUncurried = !isCurried;
10467
10468     while (++leftIndex < leftLength) {
10469       result[leftIndex] = partials[leftIndex];
10470     }
10471     while (++argsIndex < holdersLength) {
10472       if (isUncurried || argsIndex < argsLength) {
10473         result[holders[argsIndex]] = args[argsIndex];
10474       }
10475     }
10476     while (rangeLength--) {
10477       result[leftIndex++] = args[argsIndex++];
10478     }
10479     return result;
10480   }
10481
10482   /**
10483    * This function is like `composeArgs` except that the arguments composition
10484    * is tailored for `_.partialRight`.
10485    *
10486    * @private
10487    * @param {Array} args The provided arguments.
10488    * @param {Array} partials The arguments to append to those provided.
10489    * @param {Array} holders The `partials` placeholder indexes.
10490    * @params {boolean} [isCurried] Specify composing for a curried function.
10491    * @returns {Array} Returns the new array of composed arguments.
10492    */
10493   function composeArgsRight(args, partials, holders, isCurried) {
10494     var argsIndex = -1,
10495         argsLength = args.length,
10496         holdersIndex = -1,
10497         holdersLength = holders.length,
10498         rightIndex = -1,
10499         rightLength = partials.length,
10500         rangeLength = nativeMax(argsLength - holdersLength, 0),
10501         result = Array(rangeLength + rightLength),
10502         isUncurried = !isCurried;
10503
10504     while (++argsIndex < rangeLength) {
10505       result[argsIndex] = args[argsIndex];
10506     }
10507     var offset = argsIndex;
10508     while (++rightIndex < rightLength) {
10509       result[offset + rightIndex] = partials[rightIndex];
10510     }
10511     while (++holdersIndex < holdersLength) {
10512       if (isUncurried || argsIndex < argsLength) {
10513         result[offset + holders[holdersIndex]] = args[argsIndex++];
10514       }
10515     }
10516     return result;
10517   }
10518
10519   /**
10520    * Copies the values of `source` to `array`.
10521    *
10522    * @private
10523    * @param {Array} source The array to copy values from.
10524    * @param {Array} [array=[]] The array to copy values to.
10525    * @returns {Array} Returns `array`.
10526    */
10527   function copyArray(source, array) {
10528     var index = -1,
10529         length = source.length;
10530
10531     array || (array = Array(length));
10532     while (++index < length) {
10533       array[index] = source[index];
10534     }
10535     return array;
10536   }
10537
10538   /**
10539    * Copies properties of `source` to `object`.
10540    *
10541    * @private
10542    * @param {Object} source The object to copy properties from.
10543    * @param {Array} props The property identifiers to copy.
10544    * @param {Object} [object={}] The object to copy properties to.
10545    * @param {Function} [customizer] The function to customize copied values.
10546    * @returns {Object} Returns `object`.
10547    */
10548   function copyObject(source, props, object, customizer) {
10549     object || (object = {});
10550
10551     var index = -1,
10552         length = props.length;
10553
10554     while (++index < length) {
10555       var key = props[index];
10556
10557       var newValue = customizer
10558         ? customizer(object[key], source[key], key, object, source)
10559         : source[key];
10560
10561       assignValue(object, key, newValue);
10562     }
10563     return object;
10564   }
10565
10566   /**
10567    * Copies own symbol properties of `source` to `object`.
10568    *
10569    * @private
10570    * @param {Object} source The object to copy symbols from.
10571    * @param {Object} [object={}] The object to copy symbols to.
10572    * @returns {Object} Returns `object`.
10573    */
10574   function copySymbols(source, object) {
10575     return copyObject(source, getSymbols(source), object);
10576   }
10577
10578   /**
10579    * Creates a function like `_.groupBy`.
10580    *
10581    * @private
10582    * @param {Function} setter The function to set accumulator values.
10583    * @param {Function} [initializer] The accumulator object initializer.
10584    * @returns {Function} Returns the new aggregator function.
10585    */
10586   function createAggregator(setter, initializer) {
10587     return function(collection, iteratee) {
10588       var func = isArray(collection) ? arrayAggregator : baseAggregator,
10589           accumulator = initializer ? initializer() : {};
10590
10591       return func(collection, setter, getIteratee(iteratee), accumulator);
10592     };
10593   }
10594
10595   /**
10596    * Creates a function like `_.assign`.
10597    *
10598    * @private
10599    * @param {Function} assigner The function to assign values.
10600    * @returns {Function} Returns the new assigner function.
10601    */
10602   function createAssigner(assigner) {
10603     return rest(function(object, sources) {
10604       var index = -1,
10605           length = sources.length,
10606           customizer = length > 1 ? sources[length - 1] : undefined,
10607           guard = length > 2 ? sources[2] : undefined;
10608
10609       customizer = (assigner.length > 3 && typeof customizer == 'function')
10610         ? (length--, customizer)
10611         : undefined;
10612
10613       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
10614         customizer = length < 3 ? undefined : customizer;
10615         length = 1;
10616       }
10617       object = Object(object);
10618       while (++index < length) {
10619         var source = sources[index];
10620         if (source) {
10621           assigner(object, source, index, customizer);
10622         }
10623       }
10624       return object;
10625     });
10626   }
10627
10628   /**
10629    * Creates a `baseEach` or `baseEachRight` function.
10630    *
10631    * @private
10632    * @param {Function} eachFunc The function to iterate over a collection.
10633    * @param {boolean} [fromRight] Specify iterating from right to left.
10634    * @returns {Function} Returns the new base function.
10635    */
10636   function createBaseEach(eachFunc, fromRight) {
10637     return function(collection, iteratee) {
10638       if (collection == null) {
10639         return collection;
10640       }
10641       if (!isArrayLike(collection)) {
10642         return eachFunc(collection, iteratee);
10643       }
10644       var length = collection.length,
10645           index = fromRight ? length : -1,
10646           iterable = Object(collection);
10647
10648       while ((fromRight ? index-- : ++index < length)) {
10649         if (iteratee(iterable[index], index, iterable) === false) {
10650           break;
10651         }
10652       }
10653       return collection;
10654     };
10655   }
10656
10657   /**
10658    * Creates a base function for methods like `_.forIn` and `_.forOwn`.
10659    *
10660    * @private
10661    * @param {boolean} [fromRight] Specify iterating from right to left.
10662    * @returns {Function} Returns the new base function.
10663    */
10664   function createBaseFor(fromRight) {
10665     return function(object, iteratee, keysFunc) {
10666       var index = -1,
10667           iterable = Object(object),
10668           props = keysFunc(object),
10669           length = props.length;
10670
10671       while (length--) {
10672         var key = props[fromRight ? length : ++index];
10673         if (iteratee(iterable[key], key, iterable) === false) {
10674           break;
10675         }
10676       }
10677       return object;
10678     };
10679   }
10680
10681   /**
10682    * Creates a function that wraps `func` to invoke it with the optional `this`
10683    * binding of `thisArg`.
10684    *
10685    * @private
10686    * @param {Function} func The function to wrap.
10687    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10688    *  for more details.
10689    * @param {*} [thisArg] The `this` binding of `func`.
10690    * @returns {Function} Returns the new wrapped function.
10691    */
10692   function createBaseWrapper(func, bitmask, thisArg) {
10693     var isBind = bitmask & BIND_FLAG,
10694         Ctor = createCtorWrapper(func);
10695
10696     function wrapper() {
10697       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10698       return fn.apply(isBind ? thisArg : this, arguments);
10699     }
10700     return wrapper;
10701   }
10702
10703   /**
10704    * Creates a function that produces an instance of `Ctor` regardless of
10705    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
10706    *
10707    * @private
10708    * @param {Function} Ctor The constructor to wrap.
10709    * @returns {Function} Returns the new wrapped function.
10710    */
10711   function createCtorWrapper(Ctor) {
10712     return function() {
10713       // Use a `switch` statement to work with class constructors. See
10714       // http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
10715       // for more details.
10716       var args = arguments;
10717       switch (args.length) {
10718         case 0: return new Ctor;
10719         case 1: return new Ctor(args[0]);
10720         case 2: return new Ctor(args[0], args[1]);
10721         case 3: return new Ctor(args[0], args[1], args[2]);
10722         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
10723         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
10724         case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
10725         case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
10726       }
10727       var thisBinding = baseCreate(Ctor.prototype),
10728           result = Ctor.apply(thisBinding, args);
10729
10730       // Mimic the constructor's `return` behavior.
10731       // See https://es5.github.io/#x13.2.2 for more details.
10732       return isObject(result) ? result : thisBinding;
10733     };
10734   }
10735
10736   /**
10737    * Creates a function that wraps `func` to enable currying.
10738    *
10739    * @private
10740    * @param {Function} func The function to wrap.
10741    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10742    *  for more details.
10743    * @param {number} arity The arity of `func`.
10744    * @returns {Function} Returns the new wrapped function.
10745    */
10746   function createCurryWrapper(func, bitmask, arity) {
10747     var Ctor = createCtorWrapper(func);
10748
10749     function wrapper() {
10750       var length = arguments.length,
10751           args = Array(length),
10752           index = length,
10753           placeholder = getHolder(wrapper);
10754
10755       while (index--) {
10756         args[index] = arguments[index];
10757       }
10758       var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
10759         ? []
10760         : replaceHolders(args, placeholder);
10761
10762       length -= holders.length;
10763       if (length < arity) {
10764         return createRecurryWrapper(
10765           func, bitmask, createHybridWrapper, wrapper.placeholder, undefined,
10766           args, holders, undefined, undefined, arity - length);
10767       }
10768       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10769       return apply(fn, this, args);
10770     }
10771     return wrapper;
10772   }
10773
10774   /**
10775    * Creates a function that wraps `func` to invoke it with optional `this`
10776    * binding of `thisArg`, partial application, and currying.
10777    *
10778    * @private
10779    * @param {Function|string} func The function or method name to wrap.
10780    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10781    *  for more details.
10782    * @param {*} [thisArg] The `this` binding of `func`.
10783    * @param {Array} [partials] The arguments to prepend to those provided to
10784    *  the new function.
10785    * @param {Array} [holders] The `partials` placeholder indexes.
10786    * @param {Array} [partialsRight] The arguments to append to those provided
10787    *  to the new function.
10788    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10789    * @param {Array} [argPos] The argument positions of the new function.
10790    * @param {number} [ary] The arity cap of `func`.
10791    * @param {number} [arity] The arity of `func`.
10792    * @returns {Function} Returns the new wrapped function.
10793    */
10794   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10795     var isAry = bitmask & ARY_FLAG,
10796         isBind = bitmask & BIND_FLAG,
10797         isBindKey = bitmask & BIND_KEY_FLAG,
10798         isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),
10799         isFlip = bitmask & FLIP_FLAG,
10800         Ctor = isBindKey ? undefined : createCtorWrapper(func);
10801
10802     function wrapper() {
10803       var length = arguments.length,
10804           args = Array(length),
10805           index = length;
10806
10807       while (index--) {
10808         args[index] = arguments[index];
10809       }
10810       if (isCurried) {
10811         var placeholder = getHolder(wrapper),
10812             holdersCount = countHolders(args, placeholder);
10813       }
10814       if (partials) {
10815         args = composeArgs(args, partials, holders, isCurried);
10816       }
10817       if (partialsRight) {
10818         args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
10819       }
10820       length -= holdersCount;
10821       if (isCurried && length < arity) {
10822         var newHolders = replaceHolders(args, placeholder);
10823         return createRecurryWrapper(
10824           func, bitmask, createHybridWrapper, wrapper.placeholder, thisArg,
10825           args, newHolders, argPos, ary, arity - length
10826         );
10827       }
10828       var thisBinding = isBind ? thisArg : this,
10829           fn = isBindKey ? thisBinding[func] : func;
10830
10831       length = args.length;
10832       if (argPos) {
10833         args = reorder(args, argPos);
10834       } else if (isFlip && length > 1) {
10835         args.reverse();
10836       }
10837       if (isAry && ary < length) {
10838         args.length = ary;
10839       }
10840       if (this && this !== root && this instanceof wrapper) {
10841         fn = Ctor || createCtorWrapper(fn);
10842       }
10843       return fn.apply(thisBinding, args);
10844     }
10845     return wrapper;
10846   }
10847
10848   /**
10849    * Creates a function that wraps `func` to invoke it with the `this` binding
10850    * of `thisArg` and `partials` prepended to the arguments it receives.
10851    *
10852    * @private
10853    * @param {Function} func The function to wrap.
10854    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10855    *  for more details.
10856    * @param {*} thisArg The `this` binding of `func`.
10857    * @param {Array} partials The arguments to prepend to those provided to
10858    *  the new function.
10859    * @returns {Function} Returns the new wrapped function.
10860    */
10861   function createPartialWrapper(func, bitmask, thisArg, partials) {
10862     var isBind = bitmask & BIND_FLAG,
10863         Ctor = createCtorWrapper(func);
10864
10865     function wrapper() {
10866       var argsIndex = -1,
10867           argsLength = arguments.length,
10868           leftIndex = -1,
10869           leftLength = partials.length,
10870           args = Array(leftLength + argsLength),
10871           fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10872
10873       while (++leftIndex < leftLength) {
10874         args[leftIndex] = partials[leftIndex];
10875       }
10876       while (argsLength--) {
10877         args[leftIndex++] = arguments[++argsIndex];
10878       }
10879       return apply(fn, isBind ? thisArg : this, args);
10880     }
10881     return wrapper;
10882   }
10883
10884   /**
10885    * Creates a function that wraps `func` to continue currying.
10886    *
10887    * @private
10888    * @param {Function} func The function to wrap.
10889    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10890    *  for more details.
10891    * @param {Function} wrapFunc The function to create the `func` wrapper.
10892    * @param {*} placeholder The placeholder value.
10893    * @param {*} [thisArg] The `this` binding of `func`.
10894    * @param {Array} [partials] The arguments to prepend to those provided to
10895    *  the new function.
10896    * @param {Array} [holders] The `partials` placeholder indexes.
10897    * @param {Array} [argPos] The argument positions of the new function.
10898    * @param {number} [ary] The arity cap of `func`.
10899    * @param {number} [arity] The arity of `func`.
10900    * @returns {Function} Returns the new wrapped function.
10901    */
10902   function createRecurryWrapper(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
10903     var isCurry = bitmask & CURRY_FLAG,
10904         newHolders = isCurry ? holders : undefined,
10905         newHoldersRight = isCurry ? undefined : holders,
10906         newPartials = isCurry ? partials : undefined,
10907         newPartialsRight = isCurry ? undefined : partials;
10908
10909     bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10910     bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10911
10912     if (!(bitmask & CURRY_BOUND_FLAG)) {
10913       bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10914     }
10915     var newData = [
10916       func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
10917       newHoldersRight, argPos, ary, arity
10918     ];
10919
10920     var result = wrapFunc.apply(undefined, newData);
10921     if (isLaziable(func)) {
10922       setData(result, newData);
10923     }
10924     result.placeholder = placeholder;
10925     return result;
10926   }
10927
10928   /**
10929    * Creates a set of `values`.
10930    *
10931    * @private
10932    * @param {Array} values The values to add to the set.
10933    * @returns {Object} Returns the new set.
10934    */
10935   var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
10936     return new Set(values);
10937   };
10938
10939   /**
10940    * Creates a `_.toPairs` or `_.toPairsIn` function.
10941    *
10942    * @private
10943    * @param {Function} keysFunc The function to get the keys of a given object.
10944    * @returns {Function} Returns the new pairs function.
10945    */
10946   function createToPairs(keysFunc) {
10947     return function(object) {
10948       var tag = getTag(object);
10949       if (tag == mapTag) {
10950         return mapToArray(object);
10951       }
10952       if (tag == setTag) {
10953         return setToPairs(object);
10954       }
10955       return baseToPairs(object, keysFunc(object));
10956     };
10957   }
10958
10959   /**
10960    * Creates a function that either curries or invokes `func` with optional
10961    * `this` binding and partially applied arguments.
10962    *
10963    * @private
10964    * @param {Function|string} func The function or method name to wrap.
10965    * @param {number} bitmask The bitmask of wrapper flags.
10966    *  The bitmask may be composed of the following flags:
10967    *     1 - `_.bind`
10968    *     2 - `_.bindKey`
10969    *     4 - `_.curry` or `_.curryRight` of a bound function
10970    *     8 - `_.curry`
10971    *    16 - `_.curryRight`
10972    *    32 - `_.partial`
10973    *    64 - `_.partialRight`
10974    *   128 - `_.rearg`
10975    *   256 - `_.ary`
10976    *   512 - `_.flip`
10977    * @param {*} [thisArg] The `this` binding of `func`.
10978    * @param {Array} [partials] The arguments to be partially applied.
10979    * @param {Array} [holders] The `partials` placeholder indexes.
10980    * @param {Array} [argPos] The argument positions of the new function.
10981    * @param {number} [ary] The arity cap of `func`.
10982    * @param {number} [arity] The arity of `func`.
10983    * @returns {Function} Returns the new wrapped function.
10984    */
10985   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10986     var isBindKey = bitmask & BIND_KEY_FLAG;
10987     if (!isBindKey && typeof func != 'function') {
10988       throw new TypeError(FUNC_ERROR_TEXT);
10989     }
10990     var length = partials ? partials.length : 0;
10991     if (!length) {
10992       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10993       partials = holders = undefined;
10994     }
10995     ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
10996     arity = arity === undefined ? arity : toInteger(arity);
10997     length -= holders ? holders.length : 0;
10998
10999     if (bitmask & PARTIAL_RIGHT_FLAG) {
11000       var partialsRight = partials,
11001           holdersRight = holders;
11002
11003       partials = holders = undefined;
11004     }
11005     var data = isBindKey ? undefined : getData(func);
11006
11007     var newData = [
11008       func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
11009       argPos, ary, arity
11010     ];
11011
11012     if (data) {
11013       mergeData(newData, data);
11014     }
11015     func = newData[0];
11016     bitmask = newData[1];
11017     thisArg = newData[2];
11018     partials = newData[3];
11019     holders = newData[4];
11020     arity = newData[9] = newData[9] == null
11021       ? (isBindKey ? 0 : func.length)
11022       : nativeMax(newData[9] - length, 0);
11023
11024     if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {
11025       bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);
11026     }
11027     if (!bitmask || bitmask == BIND_FLAG) {
11028       var result = createBaseWrapper(func, bitmask, thisArg);
11029     } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {
11030       result = createCurryWrapper(func, bitmask, arity);
11031     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {
11032       result = createPartialWrapper(func, bitmask, thisArg, partials);
11033     } else {
11034       result = createHybridWrapper.apply(undefined, newData);
11035     }
11036     var setter = data ? baseSetData : setData;
11037     return setter(result, newData);
11038   }
11039
11040   /**
11041    * A specialized version of `baseIsEqualDeep` for arrays with support for
11042    * partial deep comparisons.
11043    *
11044    * @private
11045    * @param {Array} array The array to compare.
11046    * @param {Array} other The other array to compare.
11047    * @param {Function} equalFunc The function to determine equivalents of values.
11048    * @param {Function} customizer The function to customize comparisons.
11049    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11050    *  for more details.
11051    * @param {Object} stack Tracks traversed `array` and `other` objects.
11052    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
11053    */
11054   function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
11055     var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
11056         arrLength = array.length,
11057         othLength = other.length;
11058
11059     if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
11060       return false;
11061     }
11062     // Assume cyclic values are equal.
11063     var stacked = stack.get(array);
11064     if (stacked) {
11065       return stacked == other;
11066     }
11067     var index = -1,
11068         result = true,
11069         seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;
11070
11071     stack.set(array, other);
11072
11073     // Ignore non-index properties.
11074     while (++index < arrLength) {
11075       var arrValue = array[index],
11076           othValue = other[index];
11077
11078       if (customizer) {
11079         var compared = isPartial
11080           ? customizer(othValue, arrValue, index, other, array, stack)
11081           : customizer(arrValue, othValue, index, array, other, stack);
11082       }
11083       if (compared !== undefined) {
11084         if (compared) {
11085           continue;
11086         }
11087         result = false;
11088         break;
11089       }
11090       // Recursively compare arrays (susceptible to call stack limits).
11091       if (seen) {
11092         if (!arraySome(other, function(othValue, othIndex) {
11093               if (!seen.has(othIndex) &&
11094                   (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {
11095                 return seen.add(othIndex);
11096               }
11097             })) {
11098           result = false;
11099           break;
11100         }
11101       } else if (!(
11102             arrValue === othValue ||
11103               equalFunc(arrValue, othValue, customizer, bitmask, stack)
11104           )) {
11105         result = false;
11106         break;
11107       }
11108     }
11109     stack['delete'](array);
11110     return result;
11111   }
11112
11113   /**
11114    * A specialized version of `baseIsEqualDeep` for comparing objects of
11115    * the same `toStringTag`.
11116    *
11117    * **Note:** This function only supports comparing values with tags of
11118    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
11119    *
11120    * @private
11121    * @param {Object} object The object to compare.
11122    * @param {Object} other The other object to compare.
11123    * @param {string} tag The `toStringTag` of the objects to compare.
11124    * @param {Function} equalFunc The function to determine equivalents of values.
11125    * @param {Function} customizer The function to customize comparisons.
11126    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11127    *  for more details.
11128    * @param {Object} stack Tracks traversed `object` and `other` objects.
11129    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
11130    */
11131   function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {
11132     switch (tag) {
11133       case dataViewTag:
11134         if ((object.byteLength != other.byteLength) ||
11135             (object.byteOffset != other.byteOffset)) {
11136           return false;
11137         }
11138         object = object.buffer;
11139         other = other.buffer;
11140
11141       case arrayBufferTag:
11142         if ((object.byteLength != other.byteLength) ||
11143             !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
11144           return false;
11145         }
11146         return true;
11147
11148       case boolTag:
11149       case dateTag:
11150         // Coerce dates and booleans to numbers, dates to milliseconds and
11151         // booleans to `1` or `0` treating invalid dates coerced to `NaN` as
11152         // not equal.
11153         return +object == +other;
11154
11155       case errorTag:
11156         return object.name == other.name && object.message == other.message;
11157
11158       case numberTag:
11159         // Treat `NaN` vs. `NaN` as equal.
11160         return (object != +object) ? other != +other : object == +other;
11161
11162       case regexpTag:
11163       case stringTag:
11164         // Coerce regexes to strings and treat strings, primitives and objects,
11165         // as equal. See http://www.ecma-international.org/ecma-262/6.0/#sec-regexp.prototype.tostring
11166         // for more details.
11167         return object == (other + '');
11168
11169       case mapTag:
11170         var convert = mapToArray;
11171
11172       case setTag:
11173         var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
11174         convert || (convert = setToArray);
11175
11176         if (object.size != other.size && !isPartial) {
11177           return false;
11178         }
11179         // Assume cyclic values are equal.
11180         var stacked = stack.get(object);
11181         if (stacked) {
11182           return stacked == other;
11183         }
11184         bitmask |= UNORDERED_COMPARE_FLAG;
11185         stack.set(object, other);
11186
11187         // Recursively compare objects (susceptible to call stack limits).
11188         return equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);
11189
11190       case symbolTag:
11191         if (symbolValueOf) {
11192           return symbolValueOf.call(object) == symbolValueOf.call(other);
11193         }
11194     }
11195     return false;
11196   }
11197
11198   /**
11199    * A specialized version of `baseIsEqualDeep` for objects with support for
11200    * partial deep comparisons.
11201    *
11202    * @private
11203    * @param {Object} object The object to compare.
11204    * @param {Object} other The other object to compare.
11205    * @param {Function} equalFunc The function to determine equivalents of values.
11206    * @param {Function} customizer The function to customize comparisons.
11207    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11208    *  for more details.
11209    * @param {Object} stack Tracks traversed `object` and `other` objects.
11210    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
11211    */
11212   function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {
11213     var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
11214         objProps = keys(object),
11215         objLength = objProps.length,
11216         othProps = keys(other),
11217         othLength = othProps.length;
11218
11219     if (objLength != othLength && !isPartial) {
11220       return false;
11221     }
11222     var index = objLength;
11223     while (index--) {
11224       var key = objProps[index];
11225       if (!(isPartial ? key in other : baseHas(other, key))) {
11226         return false;
11227       }
11228     }
11229     // Assume cyclic values are equal.
11230     var stacked = stack.get(object);
11231     if (stacked) {
11232       return stacked == other;
11233     }
11234     var result = true;
11235     stack.set(object, other);
11236
11237     var skipCtor = isPartial;
11238     while (++index < objLength) {
11239       key = objProps[index];
11240       var objValue = object[key],
11241           othValue = other[key];
11242
11243       if (customizer) {
11244         var compared = isPartial
11245           ? customizer(othValue, objValue, key, other, object, stack)
11246           : customizer(objValue, othValue, key, object, other, stack);
11247       }
11248       // Recursively compare objects (susceptible to call stack limits).
11249       if (!(compared === undefined
11250             ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))
11251             : compared
11252           )) {
11253         result = false;
11254         break;
11255       }
11256       skipCtor || (skipCtor = key == 'constructor');
11257     }
11258     if (result && !skipCtor) {
11259       var objCtor = object.constructor,
11260           othCtor = other.constructor;
11261
11262       // Non `Object` object instances with different constructors are not equal.
11263       if (objCtor != othCtor &&
11264           ('constructor' in object && 'constructor' in other) &&
11265           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
11266             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
11267         result = false;
11268       }
11269     }
11270     stack['delete'](object);
11271     return result;
11272   }
11273
11274   /**
11275    * Creates an array of own enumerable property names and symbols of `object`.
11276    *
11277    * @private
11278    * @param {Object} object The object to query.
11279    * @returns {Array} Returns the array of property names and symbols.
11280    */
11281   function getAllKeys(object) {
11282     return baseGetAllKeys(object, keys, getSymbols);
11283   }
11284
11285   /**
11286    * Creates an array of own and inherited enumerable property names and
11287    * symbols of `object`.
11288    *
11289    * @private
11290    * @param {Object} object The object to query.
11291    * @returns {Array} Returns the array of property names and symbols.
11292    */
11293   function getAllKeysIn(object) {
11294     return baseGetAllKeys(object, keysIn, getSymbolsIn);
11295   }
11296
11297   /**
11298    * Gets metadata for `func`.
11299    *
11300    * @private
11301    * @param {Function} func The function to query.
11302    * @returns {*} Returns the metadata for `func`.
11303    */
11304   var getData = !metaMap ? noop : function(func) {
11305     return metaMap.get(func);
11306   };
11307
11308   /**
11309    * Gets the name of `func`.
11310    *
11311    * @private
11312    * @param {Function} func The function to query.
11313    * @returns {string} Returns the function name.
11314    */
11315   function getFuncName(func) {
11316     var result = (func.name + ''),
11317         array = realNames[result],
11318         length = hasOwnProperty.call(realNames, result) ? array.length : 0;
11319
11320     while (length--) {
11321       var data = array[length],
11322           otherFunc = data.func;
11323       if (otherFunc == null || otherFunc == func) {
11324         return data.name;
11325       }
11326     }
11327     return result;
11328   }
11329
11330   /**
11331    * Gets the argument placeholder value for `func`.
11332    *
11333    * @private
11334    * @param {Function} func The function to inspect.
11335    * @returns {*} Returns the placeholder value.
11336    */
11337   function getHolder(func) {
11338     var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
11339     return object.placeholder;
11340   }
11341
11342   /**
11343    * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
11344    * this function returns the custom method, otherwise it returns `baseIteratee`.
11345    * If arguments are provided, the chosen function is invoked with them and
11346    * its result is returned.
11347    *
11348    * @private
11349    * @param {*} [value] The value to convert to an iteratee.
11350    * @param {number} [arity] The arity of the created iteratee.
11351    * @returns {Function} Returns the chosen function or its result.
11352    */
11353   function getIteratee() {
11354     var result = lodash.iteratee || iteratee;
11355     result = result === iteratee ? baseIteratee : result;
11356     return arguments.length ? result(arguments[0], arguments[1]) : result;
11357   }
11358
11359   /**
11360    * Gets the "length" property value of `object`.
11361    *
11362    * **Note:** This function is used to avoid a
11363    * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects
11364    * Safari on at least iOS 8.1-8.3 ARM64.
11365    *
11366    * @private
11367    * @param {Object} object The object to query.
11368    * @returns {*} Returns the "length" value.
11369    */
11370   var getLength = baseProperty('length');
11371
11372   /**
11373    * Gets the data for `map`.
11374    *
11375    * @private
11376    * @param {Object} map The map to query.
11377    * @param {string} key The reference key.
11378    * @returns {*} Returns the map data.
11379    */
11380   function getMapData(map, key) {
11381     var data = map.__data__;
11382     return isKeyable(key)
11383       ? data[typeof key == 'string' ? 'string' : 'hash']
11384       : data.map;
11385   }
11386
11387   /**
11388    * Gets the property names, values, and compare flags of `object`.
11389    *
11390    * @private
11391    * @param {Object} object The object to query.
11392    * @returns {Array} Returns the match data of `object`.
11393    */
11394   function getMatchData(object) {
11395     var result = toPairs(object),
11396         length = result.length;
11397
11398     while (length--) {
11399       result[length][2] = isStrictComparable(result[length][1]);
11400     }
11401     return result;
11402   }
11403
11404   /**
11405    * Gets the native function at `key` of `object`.
11406    *
11407    * @private
11408    * @param {Object} object The object to query.
11409    * @param {string} key The key of the method to get.
11410    * @returns {*} Returns the function if it's native, else `undefined`.
11411    */
11412   function getNative(object, key) {
11413     var value = object[key];
11414     return isNative(value) ? value : undefined;
11415   }
11416
11417   /**
11418    * Gets the `[[Prototype]]` of `value`.
11419    *
11420    * @private
11421    * @param {*} value The value to query.
11422    * @returns {null|Object} Returns the `[[Prototype]]`.
11423    */
11424   function getPrototype(value) {
11425     return nativeGetPrototype(Object(value));
11426   }
11427
11428   /**
11429    * Creates an array of the own enumerable symbol properties of `object`.
11430    *
11431    * @private
11432    * @param {Object} object The object to query.
11433    * @returns {Array} Returns the array of symbols.
11434    */
11435   function getSymbols(object) {
11436     // Coerce `object` to an object to avoid non-object errors in V8.
11437     // See https://bugs.chromium.org/p/v8/issues/detail?id=3443 for more details.
11438     return getOwnPropertySymbols(Object(object));
11439   }
11440
11441   // Fallback for IE < 11.
11442   if (!getOwnPropertySymbols) {
11443     getSymbols = function() {
11444       return [];
11445     };
11446   }
11447
11448   /**
11449    * Creates an array of the own and inherited enumerable symbol properties
11450    * of `object`.
11451    *
11452    * @private
11453    * @param {Object} object The object to query.
11454    * @returns {Array} Returns the array of symbols.
11455    */
11456   var getSymbolsIn = !getOwnPropertySymbols ? getSymbols : function(object) {
11457     var result = [];
11458     while (object) {
11459       arrayPush(result, getSymbols(object));
11460       object = getPrototype(object);
11461     }
11462     return result;
11463   };
11464
11465   /**
11466    * Gets the `toStringTag` of `value`.
11467    *
11468    * @private
11469    * @param {*} value The value to query.
11470    * @returns {string} Returns the `toStringTag`.
11471    */
11472   function getTag(value) {
11473     return objectToString.call(value);
11474   }
11475
11476   // Fallback for data views, maps, sets, and weak maps in IE 11,
11477   // for data views in Edge, and promises in Node.js.
11478   if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
11479       (Map && getTag(new Map) != mapTag) ||
11480       (Promise && getTag(Promise.resolve()) != promiseTag) ||
11481       (Set && getTag(new Set) != setTag) ||
11482       (WeakMap && getTag(new WeakMap) != weakMapTag)) {
11483     getTag = function(value) {
11484       var result = objectToString.call(value),
11485           Ctor = result == objectTag ? value.constructor : undefined,
11486           ctorString = Ctor ? toSource(Ctor) : undefined;
11487
11488       if (ctorString) {
11489         switch (ctorString) {
11490           case dataViewCtorString: return dataViewTag;
11491           case mapCtorString: return mapTag;
11492           case promiseCtorString: return promiseTag;
11493           case setCtorString: return setTag;
11494           case weakMapCtorString: return weakMapTag;
11495         }
11496       }
11497       return result;
11498     };
11499   }
11500
11501   /**
11502    * Gets the view, applying any `transforms` to the `start` and `end` positions.
11503    *
11504    * @private
11505    * @param {number} start The start of the view.
11506    * @param {number} end The end of the view.
11507    * @param {Array} transforms The transformations to apply to the view.
11508    * @returns {Object} Returns an object containing the `start` and `end`
11509    *  positions of the view.
11510    */
11511   function getView(start, end, transforms) {
11512     var index = -1,
11513         length = transforms.length;
11514
11515     while (++index < length) {
11516       var data = transforms[index],
11517           size = data.size;
11518
11519       switch (data.type) {
11520         case 'drop':      start += size; break;
11521         case 'dropRight': end -= size; break;
11522         case 'take':      end = nativeMin(end, start + size); break;
11523         case 'takeRight': start = nativeMax(start, end - size); break;
11524       }
11525     }
11526     return { 'start': start, 'end': end };
11527   }
11528
11529   /**
11530    * Checks if `path` exists on `object`.
11531    *
11532    * @private
11533    * @param {Object} object The object to query.
11534    * @param {Array|string} path The path to check.
11535    * @param {Function} hasFunc The function to check properties.
11536    * @returns {boolean} Returns `true` if `path` exists, else `false`.
11537    */
11538   function hasPath(object, path, hasFunc) {
11539     path = isKey(path, object) ? [path] : castPath(path);
11540
11541     var result,
11542         index = -1,
11543         length = path.length;
11544
11545     while (++index < length) {
11546       var key = toKey(path[index]);
11547       if (!(result = object != null && hasFunc(object, key))) {
11548         break;
11549       }
11550       object = object[key];
11551     }
11552     if (result) {
11553       return result;
11554     }
11555     var length = object ? object.length : 0;
11556     return !!length && isLength(length) && isIndex(key, length) &&
11557       (isArray(object) || isString(object) || isArguments(object));
11558   }
11559
11560   /**
11561    * Initializes an array clone.
11562    *
11563    * @private
11564    * @param {Array} array The array to clone.
11565    * @returns {Array} Returns the initialized clone.
11566    */
11567   function initCloneArray(array) {
11568     var length = array.length,
11569         result = array.constructor(length);
11570
11571     // Add properties assigned by `RegExp#exec`.
11572     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
11573       result.index = array.index;
11574       result.input = array.input;
11575     }
11576     return result;
11577   }
11578
11579   /**
11580    * Initializes an object clone.
11581    *
11582    * @private
11583    * @param {Object} object The object to clone.
11584    * @returns {Object} Returns the initialized clone.
11585    */
11586   function initCloneObject(object) {
11587     return (typeof object.constructor == 'function' && !isPrototype(object))
11588       ? baseCreate(getPrototype(object))
11589       : {};
11590   }
11591
11592   /**
11593    * Initializes an object clone based on its `toStringTag`.
11594    *
11595    * **Note:** This function only supports cloning values with tags of
11596    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
11597    *
11598    * @private
11599    * @param {Object} object The object to clone.
11600    * @param {string} tag The `toStringTag` of the object to clone.
11601    * @param {Function} cloneFunc The function to clone values.
11602    * @param {boolean} [isDeep] Specify a deep clone.
11603    * @returns {Object} Returns the initialized clone.
11604    */
11605   function initCloneByTag(object, tag, cloneFunc, isDeep) {
11606     var Ctor = object.constructor;
11607     switch (tag) {
11608       case arrayBufferTag:
11609         return cloneArrayBuffer(object);
11610
11611       case boolTag:
11612       case dateTag:
11613         return new Ctor(+object);
11614
11615       case dataViewTag:
11616         return cloneDataView(object, isDeep);
11617
11618       case float32Tag: case float64Tag:
11619       case int8Tag: case int16Tag: case int32Tag:
11620       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
11621         return cloneTypedArray(object, isDeep);
11622
11623       case mapTag:
11624         return cloneMap(object, isDeep, cloneFunc);
11625
11626       case numberTag:
11627       case stringTag:
11628         return new Ctor(object);
11629
11630       case regexpTag:
11631         return cloneRegExp(object);
11632
11633       case setTag:
11634         return cloneSet(object, isDeep, cloneFunc);
11635
11636       case symbolTag:
11637         return cloneSymbol(object);
11638     }
11639   }
11640
11641   /**
11642    * Creates an array of index keys for `object` values of arrays,
11643    * `arguments` objects, and strings, otherwise `null` is returned.
11644    *
11645    * @private
11646    * @param {Object} object The object to query.
11647    * @returns {Array|null} Returns index keys, else `null`.
11648    */
11649   function indexKeys(object) {
11650     var length = object ? object.length : undefined;
11651     if (isLength(length) &&
11652         (isArray(object) || isString(object) || isArguments(object))) {
11653       return baseTimes(length, String);
11654     }
11655     return null;
11656   }
11657
11658   /**
11659    * Checks if `value` is a flattenable `arguments` object or array.
11660    *
11661    * @private
11662    * @param {*} value The value to check.
11663    * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
11664    */
11665   function isFlattenable(value) {
11666     return isArray(value) || isArguments(value);
11667   }
11668
11669   /**
11670    * Checks if `value` is a valid array-like index.
11671    *
11672    * @private
11673    * @param {*} value The value to check.
11674    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
11675    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
11676    */
11677   function isIndex(value, length) {
11678     length = length == null ? MAX_SAFE_INTEGER : length;
11679     return !!length &&
11680       (typeof value == 'number' || reIsUint.test(value)) &&
11681       (value > -1 && value % 1 == 0 && value < length);
11682   }
11683
11684   /**
11685    * Checks if the given arguments are from an iteratee call.
11686    *
11687    * @private
11688    * @param {*} value The potential iteratee value argument.
11689    * @param {*} index The potential iteratee index or key argument.
11690    * @param {*} object The potential iteratee object argument.
11691    * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
11692    *  else `false`.
11693    */
11694   function isIterateeCall(value, index, object) {
11695     if (!isObject(object)) {
11696       return false;
11697     }
11698     var type = typeof index;
11699     if (type == 'number'
11700           ? (isArrayLike(object) && isIndex(index, object.length))
11701           : (type == 'string' && index in object)
11702         ) {
11703       return eq(object[index], value);
11704     }
11705     return false;
11706   }
11707
11708   /**
11709    * Checks if `value` is a property name and not a property path.
11710    *
11711    * @private
11712    * @param {*} value The value to check.
11713    * @param {Object} [object] The object to query keys on.
11714    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
11715    */
11716   function isKey(value, object) {
11717     if (isArray(value)) {
11718       return false;
11719     }
11720     var type = typeof value;
11721     if (type == 'number' || type == 'symbol' || type == 'boolean' ||
11722         value == null || isSymbol(value)) {
11723       return true;
11724     }
11725     return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
11726       (object != null && value in Object(object));
11727   }
11728
11729   /**
11730    * Checks if `value` is suitable for use as unique object key.
11731    *
11732    * @private
11733    * @param {*} value The value to check.
11734    * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
11735    */
11736   function isKeyable(value) {
11737     var type = typeof value;
11738     return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
11739       ? (value !== '__proto__')
11740       : (value === null);
11741   }
11742
11743   /**
11744    * Checks if `func` has a lazy counterpart.
11745    *
11746    * @private
11747    * @param {Function} func The function to check.
11748    * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
11749    *  else `false`.
11750    */
11751   function isLaziable(func) {
11752     var funcName = getFuncName(func),
11753         other = lodash[funcName];
11754
11755     if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
11756       return false;
11757     }
11758     if (func === other) {
11759       return true;
11760     }
11761     var data = getData(other);
11762     return !!data && func === data[0];
11763   }
11764
11765   /**
11766    * Checks if `value` is likely a prototype object.
11767    *
11768    * @private
11769    * @param {*} value The value to check.
11770    * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
11771    */
11772   function isPrototype(value) {
11773     var Ctor = value && value.constructor,
11774         proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
11775
11776     return value === proto;
11777   }
11778
11779   /**
11780    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
11781    *
11782    * @private
11783    * @param {*} value The value to check.
11784    * @returns {boolean} Returns `true` if `value` if suitable for strict
11785    *  equality comparisons, else `false`.
11786    */
11787   function isStrictComparable(value) {
11788     return value === value && !isObject(value);
11789   }
11790
11791   /**
11792    * A specialized version of `matchesProperty` for source values suitable
11793    * for strict equality comparisons, i.e. `===`.
11794    *
11795    * @private
11796    * @param {string} key The key of the property to get.
11797    * @param {*} srcValue The value to match.
11798    * @returns {Function} Returns the new spec function.
11799    */
11800   function matchesStrictComparable(key, srcValue) {
11801     return function(object) {
11802       if (object == null) {
11803         return false;
11804       }
11805       return object[key] === srcValue &&
11806         (srcValue !== undefined || (key in Object(object)));
11807     };
11808   }
11809
11810   /**
11811    * Merges the function metadata of `source` into `data`.
11812    *
11813    * Merging metadata reduces the number of wrappers used to invoke a function.
11814    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
11815    * may be applied regardless of execution order. Methods like `_.ary` and
11816    * `_.rearg` modify function arguments, making the order in which they are
11817    * executed important, preventing the merging of metadata. However, we make
11818    * an exception for a safe combined case where curried functions have `_.ary`
11819    * and or `_.rearg` applied.
11820    *
11821    * @private
11822    * @param {Array} data The destination metadata.
11823    * @param {Array} source The source metadata.
11824    * @returns {Array} Returns `data`.
11825    */
11826   function mergeData(data, source) {
11827     var bitmask = data[1],
11828         srcBitmask = source[1],
11829         newBitmask = bitmask | srcBitmask,
11830         isCommon = newBitmask < (BIND_FLAG | BIND_KEY_FLAG | ARY_FLAG);
11831
11832     var isCombo =
11833       ((srcBitmask == ARY_FLAG) && (bitmask == CURRY_FLAG)) ||
11834       ((srcBitmask == ARY_FLAG) && (bitmask == REARG_FLAG) && (data[7].length <= source[8])) ||
11835       ((srcBitmask == (ARY_FLAG | REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == CURRY_FLAG));
11836
11837     // Exit early if metadata can't be merged.
11838     if (!(isCommon || isCombo)) {
11839       return data;
11840     }
11841     // Use source `thisArg` if available.
11842     if (srcBitmask & BIND_FLAG) {
11843       data[2] = source[2];
11844       // Set when currying a bound function.
11845       newBitmask |= bitmask & BIND_FLAG ? 0 : CURRY_BOUND_FLAG;
11846     }
11847     // Compose partial arguments.
11848     var value = source[3];
11849     if (value) {
11850       var partials = data[3];
11851       data[3] = partials ? composeArgs(partials, value, source[4]) : value;
11852       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
11853     }
11854     // Compose partial right arguments.
11855     value = source[5];
11856     if (value) {
11857       partials = data[5];
11858       data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
11859       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
11860     }
11861     // Use source `argPos` if available.
11862     value = source[7];
11863     if (value) {
11864       data[7] = value;
11865     }
11866     // Use source `ary` if it's smaller.
11867     if (srcBitmask & ARY_FLAG) {
11868       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
11869     }
11870     // Use source `arity` if one is not provided.
11871     if (data[9] == null) {
11872       data[9] = source[9];
11873     }
11874     // Use source `func` and merge bitmasks.
11875     data[0] = source[0];
11876     data[1] = newBitmask;
11877
11878     return data;
11879   }
11880
11881   /**
11882    * Gets the parent value at `path` of `object`.
11883    *
11884    * @private
11885    * @param {Object} object The object to query.
11886    * @param {Array} path The path to get the parent value of.
11887    * @returns {*} Returns the parent value.
11888    */
11889   function parent(object, path) {
11890     return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
11891   }
11892
11893   /**
11894    * Reorder `array` according to the specified indexes where the element at
11895    * the first index is assigned as the first element, the element at
11896    * the second index is assigned as the second element, and so on.
11897    *
11898    * @private
11899    * @param {Array} array The array to reorder.
11900    * @param {Array} indexes The arranged array indexes.
11901    * @returns {Array} Returns `array`.
11902    */
11903   function reorder(array, indexes) {
11904     var arrLength = array.length,
11905         length = nativeMin(indexes.length, arrLength),
11906         oldArray = copyArray(array);
11907
11908     while (length--) {
11909       var index = indexes[length];
11910       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
11911     }
11912     return array;
11913   }
11914
11915   /**
11916    * Sets metadata for `func`.
11917    *
11918    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
11919    * period of time, it will trip its breaker and transition to an identity
11920    * function to avoid garbage collection pauses in V8. See
11921    * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
11922    * for more details.
11923    *
11924    * @private
11925    * @param {Function} func The function to associate metadata with.
11926    * @param {*} data The metadata.
11927    * @returns {Function} Returns `func`.
11928    */
11929   var setData = (function() {
11930     var count = 0,
11931         lastCalled = 0;
11932
11933     return function(key, value) {
11934       var stamp = now(),
11935           remaining = HOT_SPAN - (stamp - lastCalled);
11936
11937       lastCalled = stamp;
11938       if (remaining > 0) {
11939         if (++count >= HOT_COUNT) {
11940           return key;
11941         }
11942       } else {
11943         count = 0;
11944       }
11945       return baseSetData(key, value);
11946     };
11947   }());
11948
11949   /**
11950    * Converts `string` to a property path array.
11951    *
11952    * @private
11953    * @param {string} string The string to convert.
11954    * @returns {Array} Returns the property path array.
11955    */
11956   var stringToPath = memoize(function(string) {
11957     var result = [];
11958     toString(string).replace(rePropName, function(match, number, quote, string) {
11959       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
11960     });
11961     return result;
11962   });
11963
11964   /**
11965    * Converts `value` to a string key if it's not a string or symbol.
11966    *
11967    * @private
11968    * @param {*} value The value to inspect.
11969    * @returns {string|symbol} Returns the key.
11970    */
11971   function toKey(value) {
11972     if (typeof value == 'string' || isSymbol(value)) {
11973       return value;
11974     }
11975     var result = (value + '');
11976     return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
11977   }
11978
11979   /**
11980    * Converts `func` to its source code.
11981    *
11982    * @private
11983    * @param {Function} func The function to process.
11984    * @returns {string} Returns the source code.
11985    */
11986   function toSource(func) {
11987     if (func != null) {
11988       try {
11989         return funcToString.call(func);
11990       } catch (e) {}
11991       try {
11992         return (func + '');
11993       } catch (e) {}
11994     }
11995     return '';
11996   }
11997
11998   /**
11999    * Creates a clone of `wrapper`.
12000    *
12001    * @private
12002    * @param {Object} wrapper The wrapper to clone.
12003    * @returns {Object} Returns the cloned wrapper.
12004    */
12005   function wrapperClone(wrapper) {
12006     if (wrapper instanceof LazyWrapper) {
12007       return wrapper.clone();
12008     }
12009     var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
12010     result.__actions__ = copyArray(wrapper.__actions__);
12011     result.__index__  = wrapper.__index__;
12012     result.__values__ = wrapper.__values__;
12013     return result;
12014   }
12015
12016   /*------------------------------------------------------------------------*/
12017
12018   /**
12019    * Creates an array of elements split into groups the length of `size`.
12020    * If `array` can't be split evenly, the final chunk will be the remaining
12021    * elements.
12022    *
12023    * @static
12024    * @memberOf _
12025    * @since 3.0.0
12026    * @category Array
12027    * @param {Array} array The array to process.
12028    * @param {number} [size=1] The length of each chunk
12029    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
12030    * @returns {Array} Returns the new array of chunks.
12031    * @example
12032    *
12033    * _.chunk(['a', 'b', 'c', 'd'], 2);
12034    * // => [['a', 'b'], ['c', 'd']]
12035    *
12036    * _.chunk(['a', 'b', 'c', 'd'], 3);
12037    * // => [['a', 'b', 'c'], ['d']]
12038    */
12039   function chunk(array, size, guard) {
12040     if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
12041       size = 1;
12042     } else {
12043       size = nativeMax(toInteger(size), 0);
12044     }
12045     var length = array ? array.length : 0;
12046     if (!length || size < 1) {
12047       return [];
12048     }
12049     var index = 0,
12050         resIndex = 0,
12051         result = Array(nativeCeil(length / size));
12052
12053     while (index < length) {
12054       result[resIndex++] = baseSlice(array, index, (index += size));
12055     }
12056     return result;
12057   }
12058
12059   /**
12060    * Creates an array with all falsey values removed. The values `false`, `null`,
12061    * `0`, `""`, `undefined`, and `NaN` are falsey.
12062    *
12063    * @static
12064    * @memberOf _
12065    * @since 0.1.0
12066    * @category Array
12067    * @param {Array} array The array to compact.
12068    * @returns {Array} Returns the new array of filtered values.
12069    * @example
12070    *
12071    * _.compact([0, 1, false, 2, '', 3]);
12072    * // => [1, 2, 3]
12073    */
12074   function compact(array) {
12075     var index = -1,
12076         length = array ? array.length : 0,
12077         resIndex = 0,
12078         result = [];
12079
12080     while (++index < length) {
12081       var value = array[index];
12082       if (value) {
12083         result[resIndex++] = value;
12084       }
12085     }
12086     return result;
12087   }
12088
12089   /**
12090    * Creates an array of unique `array` values not included in the other given
12091    * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12092    * for equality comparisons. The order of result values is determined by the
12093    * order they occur in the first array.
12094    *
12095    * @static
12096    * @memberOf _
12097    * @since 0.1.0
12098    * @category Array
12099    * @param {Array} array The array to inspect.
12100    * @param {...Array} [values] The values to exclude.
12101    * @returns {Array} Returns the new array of filtered values.
12102    * @see _.without, _.xor
12103    * @example
12104    *
12105    * _.difference([3, 2, 1], [4, 2]);
12106    * // => [3, 1]
12107    */
12108   var difference = rest(function(array, values) {
12109     return isArrayLikeObject(array)
12110       ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
12111       : [];
12112   });
12113
12114   /**
12115    * Flattens `array` a single level deep.
12116    *
12117    * @static
12118    * @memberOf _
12119    * @since 0.1.0
12120    * @category Array
12121    * @param {Array} array The array to flatten.
12122    * @returns {Array} Returns the new flattened array.
12123    * @example
12124    *
12125    * _.flatten([1, [2, [3, [4]], 5]]);
12126    * // => [1, 2, [3, [4]], 5]
12127    */
12128   function flatten(array) {
12129     var length = array ? array.length : 0;
12130     return length ? baseFlatten(array, 1) : [];
12131   }
12132
12133   /**
12134    * Gets the first element of `array`.
12135    *
12136    * @static
12137    * @memberOf _
12138    * @since 0.1.0
12139    * @alias first
12140    * @category Array
12141    * @param {Array} array The array to query.
12142    * @returns {*} Returns the first element of `array`.
12143    * @example
12144    *
12145    * _.head([1, 2, 3]);
12146    * // => 1
12147    *
12148    * _.head([]);
12149    * // => undefined
12150    */
12151   function head(array) {
12152     return (array && array.length) ? array[0] : undefined;
12153   }
12154
12155   /**
12156    * Gets the index at which the first occurrence of `value` is found in `array`
12157    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12158    * for equality comparisons. If `fromIndex` is negative, it's used as the
12159    * offset from the end of `array`.
12160    *
12161    * @static
12162    * @memberOf _
12163    * @since 0.1.0
12164    * @category Array
12165    * @param {Array} array The array to search.
12166    * @param {*} value The value to search for.
12167    * @param {number} [fromIndex=0] The index to search from.
12168    * @returns {number} Returns the index of the matched value, else `-1`.
12169    * @example
12170    *
12171    * _.indexOf([1, 2, 1, 2], 2);
12172    * // => 1
12173    *
12174    * // Search from the `fromIndex`.
12175    * _.indexOf([1, 2, 1, 2], 2, 2);
12176    * // => 3
12177    */
12178   function indexOf(array, value, fromIndex) {
12179     var length = array ? array.length : 0;
12180     if (!length) {
12181       return -1;
12182     }
12183     fromIndex = toInteger(fromIndex);
12184     if (fromIndex < 0) {
12185       fromIndex = nativeMax(length + fromIndex, 0);
12186     }
12187     return baseIndexOf(array, value, fromIndex);
12188   }
12189
12190   /**
12191    * Creates an array of unique values that are included in all given arrays
12192    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12193    * for equality comparisons. The order of result values is determined by the
12194    * order they occur in the first array.
12195    *
12196    * @static
12197    * @memberOf _
12198    * @since 0.1.0
12199    * @category Array
12200    * @param {...Array} [arrays] The arrays to inspect.
12201    * @returns {Array} Returns the new array of intersecting values.
12202    * @example
12203    *
12204    * _.intersection([2, 1], [4, 2], [1, 2]);
12205    * // => [2]
12206    */
12207   var intersection = rest(function(arrays) {
12208     var mapped = arrayMap(arrays, castArrayLikeObject);
12209     return (mapped.length && mapped[0] === arrays[0])
12210       ? baseIntersection(mapped)
12211       : [];
12212   });
12213
12214   /**
12215    * Gets the last element of `array`.
12216    *
12217    * @static
12218    * @memberOf _
12219    * @since 0.1.0
12220    * @category Array
12221    * @param {Array} array The array to query.
12222    * @returns {*} Returns the last element of `array`.
12223    * @example
12224    *
12225    * _.last([1, 2, 3]);
12226    * // => 3
12227    */
12228   function last(array) {
12229     var length = array ? array.length : 0;
12230     return length ? array[length - 1] : undefined;
12231   }
12232
12233   /**
12234    * Reverses `array` so that the first element becomes the last, the second
12235    * element becomes the second to last, and so on.
12236    *
12237    * **Note:** This method mutates `array` and is based on
12238    * [`Array#reverse`](https://mdn.io/Array/reverse).
12239    *
12240    * @static
12241    * @memberOf _
12242    * @since 4.0.0
12243    * @category Array
12244    * @param {Array} array The array to modify.
12245    * @returns {Array} Returns `array`.
12246    * @example
12247    *
12248    * var array = [1, 2, 3];
12249    *
12250    * _.reverse(array);
12251    * // => [3, 2, 1]
12252    *
12253    * console.log(array);
12254    * // => [3, 2, 1]
12255    */
12256   function reverse(array) {
12257     return array ? nativeReverse.call(array) : array;
12258   }
12259
12260   /**
12261    * Creates an array of unique values, in order, from all given arrays using
12262    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12263    * for equality comparisons.
12264    *
12265    * @static
12266    * @memberOf _
12267    * @since 0.1.0
12268    * @category Array
12269    * @param {...Array} [arrays] The arrays to inspect.
12270    * @returns {Array} Returns the new array of combined values.
12271    * @example
12272    *
12273    * _.union([2, 1], [4, 2], [1, 2]);
12274    * // => [2, 1, 4]
12275    */
12276   var union = rest(function(arrays) {
12277     return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
12278   });
12279
12280   /**
12281    * Creates a duplicate-free version of an array, using
12282    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12283    * for equality comparisons, in which only the first occurrence of each
12284    * element is kept.
12285    *
12286    * @static
12287    * @memberOf _
12288    * @since 0.1.0
12289    * @category Array
12290    * @param {Array} array The array to inspect.
12291    * @returns {Array} Returns the new duplicate free array.
12292    * @example
12293    *
12294    * _.uniq([2, 1, 2]);
12295    * // => [2, 1]
12296    */
12297   function uniq(array) {
12298     return (array && array.length)
12299       ? baseUniq(array)
12300       : [];
12301   }
12302
12303   /**
12304    * Creates an array excluding all given values using
12305    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12306    * for equality comparisons.
12307    *
12308    * @static
12309    * @memberOf _
12310    * @since 0.1.0
12311    * @category Array
12312    * @param {Array} array The array to inspect.
12313    * @param {...*} [values] The values to exclude.
12314    * @returns {Array} Returns the new array of filtered values.
12315    * @see _.difference, _.xor
12316    * @example
12317    *
12318    * _.without([1, 2, 1, 3], 1, 2);
12319    * // => [3]
12320    */
12321   var without = rest(function(array, values) {
12322     return isArrayLikeObject(array)
12323       ? baseDifference(array, values)
12324       : [];
12325   });
12326
12327   /*------------------------------------------------------------------------*/
12328
12329   /**
12330    * Creates a `lodash` wrapper instance that wraps `value` with explicit method
12331    * chain sequences enabled. The result of such sequences must be unwrapped
12332    * with `_#value`.
12333    *
12334    * @static
12335    * @memberOf _
12336    * @since 1.3.0
12337    * @category Seq
12338    * @param {*} value The value to wrap.
12339    * @returns {Object} Returns the new `lodash` wrapper instance.
12340    * @example
12341    *
12342    * var users = [
12343    *   { 'user': 'barney',  'age': 36 },
12344    *   { 'user': 'fred',    'age': 40 },
12345    *   { 'user': 'pebbles', 'age': 1 }
12346    * ];
12347    *
12348    * var youngest = _
12349    *   .chain(users)
12350    *   .sortBy('age')
12351    *   .map(function(o) {
12352    *     return o.user + ' is ' + o.age;
12353    *   })
12354    *   .head()
12355    *   .value();
12356    * // => 'pebbles is 1'
12357    */
12358   function chain(value) {
12359     var result = lodash(value);
12360     result.__chain__ = true;
12361     return result;
12362   }
12363
12364   /**
12365    * This method invokes `interceptor` and returns `value`. The interceptor
12366    * is invoked with one argument; (value). The purpose of this method is to
12367    * "tap into" a method chain sequence in order to modify intermediate results.
12368    *
12369    * @static
12370    * @memberOf _
12371    * @since 0.1.0
12372    * @category Seq
12373    * @param {*} value The value to provide to `interceptor`.
12374    * @param {Function} interceptor The function to invoke.
12375    * @returns {*} Returns `value`.
12376    * @example
12377    *
12378    * _([1, 2, 3])
12379    *  .tap(function(array) {
12380    *    // Mutate input array.
12381    *    array.pop();
12382    *  })
12383    *  .reverse()
12384    *  .value();
12385    * // => [2, 1]
12386    */
12387   function tap(value, interceptor) {
12388     interceptor(value);
12389     return value;
12390   }
12391
12392   /**
12393    * This method is like `_.tap` except that it returns the result of `interceptor`.
12394    * The purpose of this method is to "pass thru" values replacing intermediate
12395    * results in a method chain sequence.
12396    *
12397    * @static
12398    * @memberOf _
12399    * @since 3.0.0
12400    * @category Seq
12401    * @param {*} value The value to provide to `interceptor`.
12402    * @param {Function} interceptor The function to invoke.
12403    * @returns {*} Returns the result of `interceptor`.
12404    * @example
12405    *
12406    * _('  abc  ')
12407    *  .chain()
12408    *  .trim()
12409    *  .thru(function(value) {
12410    *    return [value];
12411    *  })
12412    *  .value();
12413    * // => ['abc']
12414    */
12415   function thru(value, interceptor) {
12416     return interceptor(value);
12417   }
12418
12419   /**
12420    * This method is the wrapper version of `_.at`.
12421    *
12422    * @name at
12423    * @memberOf _
12424    * @since 1.0.0
12425    * @category Seq
12426    * @param {...(string|string[])} [paths] The property paths of elements to pick.
12427    * @returns {Object} Returns the new `lodash` wrapper instance.
12428    * @example
12429    *
12430    * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
12431    *
12432    * _(object).at(['a[0].b.c', 'a[1]']).value();
12433    * // => [3, 4]
12434    *
12435    * _(['a', 'b', 'c']).at(0, 2).value();
12436    * // => ['a', 'c']
12437    */
12438   var wrapperAt = rest(function(paths) {
12439     paths = baseFlatten(paths, 1);
12440     var length = paths.length,
12441         start = length ? paths[0] : 0,
12442         value = this.__wrapped__,
12443         interceptor = function(object) { return baseAt(object, paths); };
12444
12445     if (length > 1 || this.__actions__.length ||
12446         !(value instanceof LazyWrapper) || !isIndex(start)) {
12447       return this.thru(interceptor);
12448     }
12449     value = value.slice(start, +start + (length ? 1 : 0));
12450     value.__actions__.push({
12451       'func': thru,
12452       'args': [interceptor],
12453       'thisArg': undefined
12454     });
12455     return new LodashWrapper(value, this.__chain__).thru(function(array) {
12456       if (length && !array.length) {
12457         array.push(undefined);
12458       }
12459       return array;
12460     });
12461   });
12462
12463   /**
12464    * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
12465    *
12466    * @name chain
12467    * @memberOf _
12468    * @since 0.1.0
12469    * @category Seq
12470    * @returns {Object} Returns the new `lodash` wrapper instance.
12471    * @example
12472    *
12473    * var users = [
12474    *   { 'user': 'barney', 'age': 36 },
12475    *   { 'user': 'fred',   'age': 40 }
12476    * ];
12477    *
12478    * // A sequence without explicit chaining.
12479    * _(users).head();
12480    * // => { 'user': 'barney', 'age': 36 }
12481    *
12482    * // A sequence with explicit chaining.
12483    * _(users)
12484    *   .chain()
12485    *   .head()
12486    *   .pick('user')
12487    *   .value();
12488    * // => { 'user': 'barney' }
12489    */
12490   function wrapperChain() {
12491     return chain(this);
12492   }
12493
12494   /**
12495    * Executes the chain sequence and returns the wrapped result.
12496    *
12497    * @name commit
12498    * @memberOf _
12499    * @since 3.2.0
12500    * @category Seq
12501    * @returns {Object} Returns the new `lodash` wrapper instance.
12502    * @example
12503    *
12504    * var array = [1, 2];
12505    * var wrapped = _(array).push(3);
12506    *
12507    * console.log(array);
12508    * // => [1, 2]
12509    *
12510    * wrapped = wrapped.commit();
12511    * console.log(array);
12512    * // => [1, 2, 3]
12513    *
12514    * wrapped.last();
12515    * // => 3
12516    *
12517    * console.log(array);
12518    * // => [1, 2, 3]
12519    */
12520   function wrapperCommit() {
12521     return new LodashWrapper(this.value(), this.__chain__);
12522   }
12523
12524   /**
12525    * Gets the next value on a wrapped object following the
12526    * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
12527    *
12528    * @name next
12529    * @memberOf _
12530    * @since 4.0.0
12531    * @category Seq
12532    * @returns {Object} Returns the next iterator value.
12533    * @example
12534    *
12535    * var wrapped = _([1, 2]);
12536    *
12537    * wrapped.next();
12538    * // => { 'done': false, 'value': 1 }
12539    *
12540    * wrapped.next();
12541    * // => { 'done': false, 'value': 2 }
12542    *
12543    * wrapped.next();
12544    * // => { 'done': true, 'value': undefined }
12545    */
12546   function wrapperNext() {
12547     if (this.__values__ === undefined) {
12548       this.__values__ = toArray(this.value());
12549     }
12550     var done = this.__index__ >= this.__values__.length,
12551         value = done ? undefined : this.__values__[this.__index__++];
12552
12553     return { 'done': done, 'value': value };
12554   }
12555
12556   /**
12557    * Enables the wrapper to be iterable.
12558    *
12559    * @name Symbol.iterator
12560    * @memberOf _
12561    * @since 4.0.0
12562    * @category Seq
12563    * @returns {Object} Returns the wrapper object.
12564    * @example
12565    *
12566    * var wrapped = _([1, 2]);
12567    *
12568    * wrapped[Symbol.iterator]() === wrapped;
12569    * // => true
12570    *
12571    * Array.from(wrapped);
12572    * // => [1, 2]
12573    */
12574   function wrapperToIterator() {
12575     return this;
12576   }
12577
12578   /**
12579    * Creates a clone of the chain sequence planting `value` as the wrapped value.
12580    *
12581    * @name plant
12582    * @memberOf _
12583    * @since 3.2.0
12584    * @category Seq
12585    * @param {*} value The value to plant.
12586    * @returns {Object} Returns the new `lodash` wrapper instance.
12587    * @example
12588    *
12589    * function square(n) {
12590    *   return n * n;
12591    * }
12592    *
12593    * var wrapped = _([1, 2]).map(square);
12594    * var other = wrapped.plant([3, 4]);
12595    *
12596    * other.value();
12597    * // => [9, 16]
12598    *
12599    * wrapped.value();
12600    * // => [1, 4]
12601    */
12602   function wrapperPlant(value) {
12603     var result,
12604         parent = this;
12605
12606     while (parent instanceof baseLodash) {
12607       var clone = wrapperClone(parent);
12608       clone.__index__ = 0;
12609       clone.__values__ = undefined;
12610       if (result) {
12611         previous.__wrapped__ = clone;
12612       } else {
12613         result = clone;
12614       }
12615       var previous = clone;
12616       parent = parent.__wrapped__;
12617     }
12618     previous.__wrapped__ = value;
12619     return result;
12620   }
12621
12622   /**
12623    * This method is the wrapper version of `_.reverse`.
12624    *
12625    * **Note:** This method mutates the wrapped array.
12626    *
12627    * @name reverse
12628    * @memberOf _
12629    * @since 0.1.0
12630    * @category Seq
12631    * @returns {Object} Returns the new `lodash` wrapper instance.
12632    * @example
12633    *
12634    * var array = [1, 2, 3];
12635    *
12636    * _(array).reverse().value()
12637    * // => [3, 2, 1]
12638    *
12639    * console.log(array);
12640    * // => [3, 2, 1]
12641    */
12642   function wrapperReverse() {
12643     var value = this.__wrapped__;
12644     if (value instanceof LazyWrapper) {
12645       var wrapped = value;
12646       if (this.__actions__.length) {
12647         wrapped = new LazyWrapper(this);
12648       }
12649       wrapped = wrapped.reverse();
12650       wrapped.__actions__.push({
12651         'func': thru,
12652         'args': [reverse],
12653         'thisArg': undefined
12654       });
12655       return new LodashWrapper(wrapped, this.__chain__);
12656     }
12657     return this.thru(reverse);
12658   }
12659
12660   /**
12661    * Executes the chain sequence to resolve the unwrapped value.
12662    *
12663    * @name value
12664    * @memberOf _
12665    * @since 0.1.0
12666    * @alias toJSON, valueOf
12667    * @category Seq
12668    * @returns {*} Returns the resolved unwrapped value.
12669    * @example
12670    *
12671    * _([1, 2, 3]).value();
12672    * // => [1, 2, 3]
12673    */
12674   function wrapperValue() {
12675     return baseWrapperValue(this.__wrapped__, this.__actions__);
12676   }
12677
12678   /*------------------------------------------------------------------------*/
12679
12680   /**
12681    * Checks if `predicate` returns truthy for **all** elements of `collection`.
12682    * Iteration is stopped once `predicate` returns falsey. The predicate is
12683    * invoked with three arguments: (value, index|key, collection).
12684    *
12685    * @static
12686    * @memberOf _
12687    * @since 0.1.0
12688    * @category Collection
12689    * @param {Array|Object} collection The collection to iterate over.
12690    * @param {Array|Function|Object|string} [predicate=_.identity]
12691    *  The function invoked per iteration.
12692    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
12693    * @returns {boolean} Returns `true` if all elements pass the predicate check,
12694    *  else `false`.
12695    * @example
12696    *
12697    * _.every([true, 1, null, 'yes'], Boolean);
12698    * // => false
12699    *
12700    * var users = [
12701    *   { 'user': 'barney', 'age': 36, 'active': false },
12702    *   { 'user': 'fred',   'age': 40, 'active': false }
12703    * ];
12704    *
12705    * // The `_.matches` iteratee shorthand.
12706    * _.every(users, { 'user': 'barney', 'active': false });
12707    * // => false
12708    *
12709    * // The `_.matchesProperty` iteratee shorthand.
12710    * _.every(users, ['active', false]);
12711    * // => true
12712    *
12713    * // The `_.property` iteratee shorthand.
12714    * _.every(users, 'active');
12715    * // => false
12716    */
12717   function every(collection, predicate, guard) {
12718     var func = isArray(collection) ? arrayEvery : baseEvery;
12719     if (guard && isIterateeCall(collection, predicate, guard)) {
12720       predicate = undefined;
12721     }
12722     return func(collection, getIteratee(predicate, 3));
12723   }
12724
12725   /**
12726    * Iterates over elements of `collection`, returning an array of all elements
12727    * `predicate` returns truthy for. The predicate is invoked with three
12728    * arguments: (value, index|key, collection).
12729    *
12730    * @static
12731    * @memberOf _
12732    * @since 0.1.0
12733    * @category Collection
12734    * @param {Array|Object} collection The collection to iterate over.
12735    * @param {Array|Function|Object|string} [predicate=_.identity]
12736    *  The function invoked per iteration.
12737    * @returns {Array} Returns the new filtered array.
12738    * @see _.reject
12739    * @example
12740    *
12741    * var users = [
12742    *   { 'user': 'barney', 'age': 36, 'active': true },
12743    *   { 'user': 'fred',   'age': 40, 'active': false }
12744    * ];
12745    *
12746    * _.filter(users, function(o) { return !o.active; });
12747    * // => objects for ['fred']
12748    *
12749    * // The `_.matches` iteratee shorthand.
12750    * _.filter(users, { 'age': 36, 'active': true });
12751    * // => objects for ['barney']
12752    *
12753    * // The `_.matchesProperty` iteratee shorthand.
12754    * _.filter(users, ['active', false]);
12755    * // => objects for ['fred']
12756    *
12757    * // The `_.property` iteratee shorthand.
12758    * _.filter(users, 'active');
12759    * // => objects for ['barney']
12760    */
12761   function filter(collection, predicate) {
12762     var func = isArray(collection) ? arrayFilter : baseFilter;
12763     return func(collection, getIteratee(predicate, 3));
12764   }
12765
12766   /**
12767    * Iterates over elements of `collection`, returning the first element
12768    * `predicate` returns truthy for. The predicate is invoked with three
12769    * arguments: (value, index|key, collection).
12770    *
12771    * @static
12772    * @memberOf _
12773    * @since 0.1.0
12774    * @category Collection
12775    * @param {Array|Object} collection The collection to search.
12776    * @param {Array|Function|Object|string} [predicate=_.identity]
12777    *  The function invoked per iteration.
12778    * @returns {*} Returns the matched element, else `undefined`.
12779    * @example
12780    *
12781    * var users = [
12782    *   { 'user': 'barney',  'age': 36, 'active': true },
12783    *   { 'user': 'fred',    'age': 40, 'active': false },
12784    *   { 'user': 'pebbles', 'age': 1,  'active': true }
12785    * ];
12786    *
12787    * _.find(users, function(o) { return o.age < 40; });
12788    * // => object for 'barney'
12789    *
12790    * // The `_.matches` iteratee shorthand.
12791    * _.find(users, { 'age': 1, 'active': true });
12792    * // => object for 'pebbles'
12793    *
12794    * // The `_.matchesProperty` iteratee shorthand.
12795    * _.find(users, ['active', false]);
12796    * // => object for 'fred'
12797    *
12798    * // The `_.property` iteratee shorthand.
12799    * _.find(users, 'active');
12800    * // => object for 'barney'
12801    */
12802   function find(collection, predicate) {
12803     predicate = getIteratee(predicate, 3);
12804     if (isArray(collection)) {
12805       var index = baseFindIndex(collection, predicate);
12806       return index > -1 ? collection[index] : undefined;
12807     }
12808     return baseFind(collection, predicate, baseEach);
12809   }
12810
12811   /**
12812    * Iterates over elements of `collection` and invokes `iteratee` for each element.
12813    * The iteratee is invoked with three arguments: (value, index|key, collection).
12814    * Iteratee functions may exit iteration early by explicitly returning `false`.
12815    *
12816    * **Note:** As with other "Collections" methods, objects with a "length"
12817    * property are iterated like arrays. To avoid this behavior use `_.forIn`
12818    * or `_.forOwn` for object iteration.
12819    *
12820    * @static
12821    * @memberOf _
12822    * @since 0.1.0
12823    * @alias each
12824    * @category Collection
12825    * @param {Array|Object} collection The collection to iterate over.
12826    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12827    * @returns {Array|Object} Returns `collection`.
12828    * @see _.forEachRight
12829    * @example
12830    *
12831    * _([1, 2]).forEach(function(value) {
12832    *   console.log(value);
12833    * });
12834    * // => Logs `1` then `2`.
12835    *
12836    * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
12837    *   console.log(key);
12838    * });
12839    * // => Logs 'a' then 'b' (iteration order is not guaranteed).
12840    */
12841   function forEach(collection, iteratee) {
12842     var func = isArray(collection) ? arrayEach : baseEach;
12843     return func(collection, getIteratee(iteratee, 3));
12844   }
12845
12846   /**
12847    * Creates an object composed of keys generated from the results of running
12848    * each element of `collection` thru `iteratee`. The order of grouped values
12849    * is determined by the order they occur in `collection`. The corresponding
12850    * value of each key is an array of elements responsible for generating the
12851    * key. The iteratee is invoked with one argument: (value).
12852    *
12853    * @static
12854    * @memberOf _
12855    * @since 0.1.0
12856    * @category Collection
12857    * @param {Array|Object} collection The collection to iterate over.
12858    * @param {Array|Function|Object|string} [iteratee=_.identity]
12859    *  The iteratee to transform keys.
12860    * @returns {Object} Returns the composed aggregate object.
12861    * @example
12862    *
12863    * _.groupBy([6.1, 4.2, 6.3], Math.floor);
12864    * // => { '4': [4.2], '6': [6.1, 6.3] }
12865    *
12866    * // The `_.property` iteratee shorthand.
12867    * _.groupBy(['one', 'two', 'three'], 'length');
12868    * // => { '3': ['one', 'two'], '5': ['three'] }
12869    */
12870   var groupBy = createAggregator(function(result, value, key) {
12871     if (hasOwnProperty.call(result, key)) {
12872       result[key].push(value);
12873     } else {
12874       result[key] = [value];
12875     }
12876   });
12877
12878   /**
12879    * Checks if `value` is in `collection`. If `collection` is a string, it's
12880    * checked for a substring of `value`, otherwise
12881    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12882    * is used for equality comparisons. If `fromIndex` is negative, it's used as
12883    * the offset from the end of `collection`.
12884    *
12885    * @static
12886    * @memberOf _
12887    * @since 0.1.0
12888    * @category Collection
12889    * @param {Array|Object|string} collection The collection to search.
12890    * @param {*} value The value to search for.
12891    * @param {number} [fromIndex=0] The index to search from.
12892    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
12893    * @returns {boolean} Returns `true` if `value` is found, else `false`.
12894    * @example
12895    *
12896    * _.includes([1, 2, 3], 1);
12897    * // => true
12898    *
12899    * _.includes([1, 2, 3], 1, 2);
12900    * // => false
12901    *
12902    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
12903    * // => true
12904    *
12905    * _.includes('pebbles', 'eb');
12906    * // => true
12907    */
12908   function includes(collection, value, fromIndex, guard) {
12909     collection = isArrayLike(collection) ? collection : values(collection);
12910     fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;
12911
12912     var length = collection.length;
12913     if (fromIndex < 0) {
12914       fromIndex = nativeMax(length + fromIndex, 0);
12915     }
12916     return isString(collection)
12917       ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
12918       : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
12919   }
12920
12921   /**
12922    * Creates an array of values by running each element in `collection` thru
12923    * `iteratee`. The iteratee is invoked with three arguments:
12924    * (value, index|key, collection).
12925    *
12926    * Many lodash methods are guarded to work as iteratees for methods like
12927    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
12928    *
12929    * The guarded methods are:
12930    * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
12931    * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
12932    * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
12933    * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
12934    *
12935    * @static
12936    * @memberOf _
12937    * @since 0.1.0
12938    * @category Collection
12939    * @param {Array|Object} collection The collection to iterate over.
12940    * @param {Array|Function|Object|string} [iteratee=_.identity]
12941    *  The function invoked per iteration.
12942    * @returns {Array} Returns the new mapped array.
12943    * @example
12944    *
12945    * function square(n) {
12946    *   return n * n;
12947    * }
12948    *
12949    * _.map([4, 8], square);
12950    * // => [16, 64]
12951    *
12952    * _.map({ 'a': 4, 'b': 8 }, square);
12953    * // => [16, 64] (iteration order is not guaranteed)
12954    *
12955    * var users = [
12956    *   { 'user': 'barney' },
12957    *   { 'user': 'fred' }
12958    * ];
12959    *
12960    * // The `_.property` iteratee shorthand.
12961    * _.map(users, 'user');
12962    * // => ['barney', 'fred']
12963    */
12964   function map(collection, iteratee) {
12965     var func = isArray(collection) ? arrayMap : baseMap;
12966     return func(collection, getIteratee(iteratee, 3));
12967   }
12968
12969   /**
12970    * Reduces `collection` to a value which is the accumulated result of running
12971    * each element in `collection` thru `iteratee`, where each successive
12972    * invocation is supplied the return value of the previous. If `accumulator`
12973    * is not given, the first element of `collection` is used as the initial
12974    * value. The iteratee is invoked with four arguments:
12975    * (accumulator, value, index|key, collection).
12976    *
12977    * Many lodash methods are guarded to work as iteratees for methods like
12978    * `_.reduce`, `_.reduceRight`, and `_.transform`.
12979    *
12980    * The guarded methods are:
12981    * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
12982    * and `sortBy`
12983    *
12984    * @static
12985    * @memberOf _
12986    * @since 0.1.0
12987    * @category Collection
12988    * @param {Array|Object} collection The collection to iterate over.
12989    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12990    * @param {*} [accumulator] The initial value.
12991    * @returns {*} Returns the accumulated value.
12992    * @see _.reduceRight
12993    * @example
12994    *
12995    * _.reduce([1, 2], function(sum, n) {
12996    *   return sum + n;
12997    * }, 0);
12998    * // => 3
12999    *
13000    * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
13001    *   (result[value] || (result[value] = [])).push(key);
13002    *   return result;
13003    * }, {});
13004    * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
13005    */
13006   function reduce(collection, iteratee, accumulator) {
13007     var func = isArray(collection) ? arrayReduce : baseReduce,
13008         initAccum = arguments.length < 3;
13009
13010     return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
13011   }
13012
13013   /**
13014    * The opposite of `_.filter`; this method returns the elements of `collection`
13015    * that `predicate` does **not** return truthy for.
13016    *
13017    * @static
13018    * @memberOf _
13019    * @since 0.1.0
13020    * @category Collection
13021    * @param {Array|Object} collection The collection to iterate over.
13022    * @param {Array|Function|Object|string} [predicate=_.identity]
13023    *  The function invoked per iteration.
13024    * @returns {Array} Returns the new filtered array.
13025    * @see _.filter
13026    * @example
13027    *
13028    * var users = [
13029    *   { 'user': 'barney', 'age': 36, 'active': false },
13030    *   { 'user': 'fred',   'age': 40, 'active': true }
13031    * ];
13032    *
13033    * _.reject(users, function(o) { return !o.active; });
13034    * // => objects for ['fred']
13035    *
13036    * // The `_.matches` iteratee shorthand.
13037    * _.reject(users, { 'age': 40, 'active': true });
13038    * // => objects for ['barney']
13039    *
13040    * // The `_.matchesProperty` iteratee shorthand.
13041    * _.reject(users, ['active', false]);
13042    * // => objects for ['fred']
13043    *
13044    * // The `_.property` iteratee shorthand.
13045    * _.reject(users, 'active');
13046    * // => objects for ['barney']
13047    */
13048   function reject(collection, predicate) {
13049     var func = isArray(collection) ? arrayFilter : baseFilter;
13050     predicate = getIteratee(predicate, 3);
13051     return func(collection, function(value, index, collection) {
13052       return !predicate(value, index, collection);
13053     });
13054   }
13055
13056   /**
13057    * Checks if `predicate` returns truthy for **any** element of `collection`.
13058    * Iteration is stopped once `predicate` returns truthy. The predicate is
13059    * invoked with three arguments: (value, index|key, collection).
13060    *
13061    * @static
13062    * @memberOf _
13063    * @since 0.1.0
13064    * @category Collection
13065    * @param {Array|Object} collection The collection to iterate over.
13066    * @param {Array|Function|Object|string} [predicate=_.identity]
13067    *  The function invoked per iteration.
13068    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
13069    * @returns {boolean} Returns `true` if any element passes the predicate check,
13070    *  else `false`.
13071    * @example
13072    *
13073    * _.some([null, 0, 'yes', false], Boolean);
13074    * // => true
13075    *
13076    * var users = [
13077    *   { 'user': 'barney', 'active': true },
13078    *   { 'user': 'fred',   'active': false }
13079    * ];
13080    *
13081    * // The `_.matches` iteratee shorthand.
13082    * _.some(users, { 'user': 'barney', 'active': false });
13083    * // => false
13084    *
13085    * // The `_.matchesProperty` iteratee shorthand.
13086    * _.some(users, ['active', false]);
13087    * // => true
13088    *
13089    * // The `_.property` iteratee shorthand.
13090    * _.some(users, 'active');
13091    * // => true
13092    */
13093   function some(collection, predicate, guard) {
13094     var func = isArray(collection) ? arraySome : baseSome;
13095     if (guard && isIterateeCall(collection, predicate, guard)) {
13096       predicate = undefined;
13097     }
13098     return func(collection, getIteratee(predicate, 3));
13099   }
13100
13101   /*------------------------------------------------------------------------*/
13102
13103   /**
13104    * Gets the timestamp of the number of milliseconds that have elapsed since
13105    * the Unix epoch (1 January 1970 00:00:00 UTC).
13106    *
13107    * @static
13108    * @memberOf _
13109    * @since 2.4.0
13110    * @type {Function}
13111    * @category Date
13112    * @returns {number} Returns the timestamp.
13113    * @example
13114    *
13115    * _.defer(function(stamp) {
13116    *   console.log(_.now() - stamp);
13117    * }, _.now());
13118    * // => Logs the number of milliseconds it took for the deferred function to be invoked.
13119    */
13120   var now = Date.now;
13121
13122   /*------------------------------------------------------------------------*/
13123
13124   /**
13125    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13126    * and `partials` prepended to the arguments it receives.
13127    *
13128    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
13129    * may be used as a placeholder for partially applied arguments.
13130    *
13131    * **Note:** Unlike native `Function#bind` this method doesn't set the "length"
13132    * property of bound functions.
13133    *
13134    * @static
13135    * @memberOf _
13136    * @since 0.1.0
13137    * @category Function
13138    * @param {Function} func The function to bind.
13139    * @param {*} thisArg The `this` binding of `func`.
13140    * @param {...*} [partials] The arguments to be partially applied.
13141    * @returns {Function} Returns the new bound function.
13142    * @example
13143    *
13144    * var greet = function(greeting, punctuation) {
13145    *   return greeting + ' ' + this.user + punctuation;
13146    * };
13147    *
13148    * var object = { 'user': 'fred' };
13149    *
13150    * var bound = _.bind(greet, object, 'hi');
13151    * bound('!');
13152    * // => 'hi fred!'
13153    *
13154    * // Bound with placeholders.
13155    * var bound = _.bind(greet, object, _, '!');
13156    * bound('hi');
13157    * // => 'hi fred!'
13158    */
13159   var bind = rest(function(func, thisArg, partials) {
13160     var bitmask = BIND_FLAG;
13161     if (partials.length) {
13162       var holders = replaceHolders(partials, getHolder(bind));
13163       bitmask |= PARTIAL_FLAG;
13164     }
13165     return createWrapper(func, bitmask, thisArg, partials, holders);
13166   });
13167
13168   /**
13169    * Creates a debounced function that delays invoking `func` until after `wait`
13170    * milliseconds have elapsed since the last time the debounced function was
13171    * invoked. The debounced function comes with a `cancel` method to cancel
13172    * delayed `func` invocations and a `flush` method to immediately invoke them.
13173    * Provide an options object to indicate whether `func` should be invoked on
13174    * the leading and/or trailing edge of the `wait` timeout. The `func` is invoked
13175    * with the last arguments provided to the debounced function. Subsequent calls
13176    * to the debounced function return the result of the last `func` invocation.
13177    *
13178    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
13179    * on the trailing edge of the timeout only if the debounced function is
13180    * invoked more than once during the `wait` timeout.
13181    *
13182    * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
13183    * for details over the differences between `_.debounce` and `_.throttle`.
13184    *
13185    * @static
13186    * @memberOf _
13187    * @since 0.1.0
13188    * @category Function
13189    * @param {Function} func The function to debounce.
13190    * @param {number} [wait=0] The number of milliseconds to delay.
13191    * @param {Object} [options={}] The options object.
13192    * @param {boolean} [options.leading=false]
13193    *  Specify invoking on the leading edge of the timeout.
13194    * @param {number} [options.maxWait]
13195    *  The maximum time `func` is allowed to be delayed before it's invoked.
13196    * @param {boolean} [options.trailing=true]
13197    *  Specify invoking on the trailing edge of the timeout.
13198    * @returns {Function} Returns the new debounced function.
13199    * @example
13200    *
13201    * // Avoid costly calculations while the window size is in flux.
13202    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
13203    *
13204    * // Invoke `sendMail` when clicked, debouncing subsequent calls.
13205    * jQuery(element).on('click', _.debounce(sendMail, 300, {
13206    *   'leading': true,
13207    *   'trailing': false
13208    * }));
13209    *
13210    * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
13211    * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
13212    * var source = new EventSource('/stream');
13213    * jQuery(source).on('message', debounced);
13214    *
13215    * // Cancel the trailing debounced invocation.
13216    * jQuery(window).on('popstate', debounced.cancel);
13217    */
13218   function debounce(func, wait, options) {
13219     var lastArgs,
13220         lastThis,
13221         maxWait,
13222         result,
13223         timerId,
13224         lastCallTime = 0,
13225         lastInvokeTime = 0,
13226         leading = false,
13227         maxing = false,
13228         trailing = true;
13229
13230     if (typeof func != 'function') {
13231       throw new TypeError(FUNC_ERROR_TEXT);
13232     }
13233     wait = toNumber(wait) || 0;
13234     if (isObject(options)) {
13235       leading = !!options.leading;
13236       maxing = 'maxWait' in options;
13237       maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
13238       trailing = 'trailing' in options ? !!options.trailing : trailing;
13239     }
13240
13241     function invokeFunc(time) {
13242       var args = lastArgs,
13243           thisArg = lastThis;
13244
13245       lastArgs = lastThis = undefined;
13246       lastInvokeTime = time;
13247       result = func.apply(thisArg, args);
13248       return result;
13249     }
13250
13251     function leadingEdge(time) {
13252       // Reset any `maxWait` timer.
13253       lastInvokeTime = time;
13254       // Start the timer for the trailing edge.
13255       timerId = setTimeout(timerExpired, wait);
13256       // Invoke the leading edge.
13257       return leading ? invokeFunc(time) : result;
13258     }
13259
13260     function remainingWait(time) {
13261       var timeSinceLastCall = time - lastCallTime,
13262           timeSinceLastInvoke = time - lastInvokeTime,
13263           result = wait - timeSinceLastCall;
13264
13265       return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
13266     }
13267
13268     function shouldInvoke(time) {
13269       var timeSinceLastCall = time - lastCallTime,
13270           timeSinceLastInvoke = time - lastInvokeTime;
13271
13272       // Either this is the first call, activity has stopped and we're at the
13273       // trailing edge, the system time has gone backwards and we're treating
13274       // it as the trailing edge, or we've hit the `maxWait` limit.
13275       return (!lastCallTime || (timeSinceLastCall >= wait) ||
13276         (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
13277     }
13278
13279     function timerExpired() {
13280       var time = now();
13281       if (shouldInvoke(time)) {
13282         return trailingEdge(time);
13283       }
13284       // Restart the timer.
13285       timerId = setTimeout(timerExpired, remainingWait(time));
13286     }
13287
13288     function trailingEdge(time) {
13289       clearTimeout(timerId);
13290       timerId = undefined;
13291
13292       // Only invoke if we have `lastArgs` which means `func` has been
13293       // debounced at least once.
13294       if (trailing && lastArgs) {
13295         return invokeFunc(time);
13296       }
13297       lastArgs = lastThis = undefined;
13298       return result;
13299     }
13300
13301     function cancel() {
13302       if (timerId !== undefined) {
13303         clearTimeout(timerId);
13304       }
13305       lastCallTime = lastInvokeTime = 0;
13306       lastArgs = lastThis = timerId = undefined;
13307     }
13308
13309     function flush() {
13310       return timerId === undefined ? result : trailingEdge(now());
13311     }
13312
13313     function debounced() {
13314       var time = now(),
13315           isInvoking = shouldInvoke(time);
13316
13317       lastArgs = arguments;
13318       lastThis = this;
13319       lastCallTime = time;
13320
13321       if (isInvoking) {
13322         if (timerId === undefined) {
13323           return leadingEdge(lastCallTime);
13324         }
13325         if (maxing) {
13326           // Handle invocations in a tight loop.
13327           clearTimeout(timerId);
13328           timerId = setTimeout(timerExpired, wait);
13329           return invokeFunc(lastCallTime);
13330         }
13331       }
13332       if (timerId === undefined) {
13333         timerId = setTimeout(timerExpired, wait);
13334       }
13335       return result;
13336     }
13337     debounced.cancel = cancel;
13338     debounced.flush = flush;
13339     return debounced;
13340   }
13341
13342   /**
13343    * Creates a function that memoizes the result of `func`. If `resolver` is
13344    * provided, it determines the cache key for storing the result based on the
13345    * arguments provided to the memoized function. By default, the first argument
13346    * provided to the memoized function is used as the map cache key. The `func`
13347    * is invoked with the `this` binding of the memoized function.
13348    *
13349    * **Note:** The cache is exposed as the `cache` property on the memoized
13350    * function. Its creation may be customized by replacing the `_.memoize.Cache`
13351    * constructor with one whose instances implement the
13352    * [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)
13353    * method interface of `delete`, `get`, `has`, and `set`.
13354    *
13355    * @static
13356    * @memberOf _
13357    * @since 0.1.0
13358    * @category Function
13359    * @param {Function} func The function to have its output memoized.
13360    * @param {Function} [resolver] The function to resolve the cache key.
13361    * @returns {Function} Returns the new memoized function.
13362    * @example
13363    *
13364    * var object = { 'a': 1, 'b': 2 };
13365    * var other = { 'c': 3, 'd': 4 };
13366    *
13367    * var values = _.memoize(_.values);
13368    * values(object);
13369    * // => [1, 2]
13370    *
13371    * values(other);
13372    * // => [3, 4]
13373    *
13374    * object.a = 2;
13375    * values(object);
13376    * // => [1, 2]
13377    *
13378    * // Modify the result cache.
13379    * values.cache.set(object, ['a', 'b']);
13380    * values(object);
13381    * // => ['a', 'b']
13382    *
13383    * // Replace `_.memoize.Cache`.
13384    * _.memoize.Cache = WeakMap;
13385    */
13386   function memoize(func, resolver) {
13387     if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
13388       throw new TypeError(FUNC_ERROR_TEXT);
13389     }
13390     var memoized = function() {
13391       var args = arguments,
13392           key = resolver ? resolver.apply(this, args) : args[0],
13393           cache = memoized.cache;
13394
13395       if (cache.has(key)) {
13396         return cache.get(key);
13397       }
13398       var result = func.apply(this, args);
13399       memoized.cache = cache.set(key, result);
13400       return result;
13401     };
13402     memoized.cache = new (memoize.Cache || MapCache);
13403     return memoized;
13404   }
13405
13406   // Assign cache to `_.memoize`.
13407   memoize.Cache = MapCache;
13408
13409   /**
13410    * Creates a function that invokes `func` with the `this` binding of the
13411    * created function and arguments from `start` and beyond provided as
13412    * an array.
13413    *
13414    * **Note:** This method is based on the
13415    * [rest parameter](https://mdn.io/rest_parameters).
13416    *
13417    * @static
13418    * @memberOf _
13419    * @since 4.0.0
13420    * @category Function
13421    * @param {Function} func The function to apply a rest parameter to.
13422    * @param {number} [start=func.length-1] The start position of the rest parameter.
13423    * @returns {Function} Returns the new function.
13424    * @example
13425    *
13426    * var say = _.rest(function(what, names) {
13427    *   return what + ' ' + _.initial(names).join(', ') +
13428    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
13429    * });
13430    *
13431    * say('hello', 'fred', 'barney', 'pebbles');
13432    * // => 'hello fred, barney, & pebbles'
13433    */
13434   function rest(func, start) {
13435     if (typeof func != 'function') {
13436       throw new TypeError(FUNC_ERROR_TEXT);
13437     }
13438     start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0);
13439     return function() {
13440       var args = arguments,
13441           index = -1,
13442           length = nativeMax(args.length - start, 0),
13443           array = Array(length);
13444
13445       while (++index < length) {
13446         array[index] = args[start + index];
13447       }
13448       switch (start) {
13449         case 0: return func.call(this, array);
13450         case 1: return func.call(this, args[0], array);
13451         case 2: return func.call(this, args[0], args[1], array);
13452       }
13453       var otherArgs = Array(start + 1);
13454       index = -1;
13455       while (++index < start) {
13456         otherArgs[index] = args[index];
13457       }
13458       otherArgs[start] = array;
13459       return apply(func, this, otherArgs);
13460     };
13461   }
13462
13463   /**
13464    * Creates a throttled function that only invokes `func` at most once per
13465    * every `wait` milliseconds. The throttled function comes with a `cancel`
13466    * method to cancel delayed `func` invocations and a `flush` method to
13467    * immediately invoke them. Provide an options object to indicate whether
13468    * `func` should be invoked on the leading and/or trailing edge of the `wait`
13469    * timeout. The `func` is invoked with the last arguments provided to the
13470    * throttled function. Subsequent calls to the throttled function return the
13471    * result of the last `func` invocation.
13472    *
13473    * **Note:** If `leading` and `trailing` options are `true`, `func` is
13474    * invoked on the trailing edge of the timeout only if the throttled function
13475    * is invoked more than once during the `wait` timeout.
13476    *
13477    * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
13478    * for details over the differences between `_.throttle` and `_.debounce`.
13479    *
13480    * @static
13481    * @memberOf _
13482    * @since 0.1.0
13483    * @category Function
13484    * @param {Function} func The function to throttle.
13485    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
13486    * @param {Object} [options={}] The options object.
13487    * @param {boolean} [options.leading=true]
13488    *  Specify invoking on the leading edge of the timeout.
13489    * @param {boolean} [options.trailing=true]
13490    *  Specify invoking on the trailing edge of the timeout.
13491    * @returns {Function} Returns the new throttled function.
13492    * @example
13493    *
13494    * // Avoid excessively updating the position while scrolling.
13495    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
13496    *
13497    * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
13498    * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
13499    * jQuery(element).on('click', throttled);
13500    *
13501    * // Cancel the trailing throttled invocation.
13502    * jQuery(window).on('popstate', throttled.cancel);
13503    */
13504   function throttle(func, wait, options) {
13505     var leading = true,
13506         trailing = true;
13507
13508     if (typeof func != 'function') {
13509       throw new TypeError(FUNC_ERROR_TEXT);
13510     }
13511     if (isObject(options)) {
13512       leading = 'leading' in options ? !!options.leading : leading;
13513       trailing = 'trailing' in options ? !!options.trailing : trailing;
13514     }
13515     return debounce(func, wait, {
13516       'leading': leading,
13517       'maxWait': wait,
13518       'trailing': trailing
13519     });
13520   }
13521
13522   /*------------------------------------------------------------------------*/
13523
13524   /**
13525    * Creates a shallow clone of `value`.
13526    *
13527    * **Note:** This method is loosely based on the
13528    * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
13529    * and supports cloning arrays, array buffers, booleans, date objects, maps,
13530    * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
13531    * arrays. The own enumerable properties of `arguments` objects are cloned
13532    * as plain objects. An empty object is returned for uncloneable values such
13533    * as error objects, functions, DOM nodes, and WeakMaps.
13534    *
13535    * @static
13536    * @memberOf _
13537    * @since 0.1.0
13538    * @category Lang
13539    * @param {*} value The value to clone.
13540    * @returns {*} Returns the cloned value.
13541    * @see _.cloneDeep
13542    * @example
13543    *
13544    * var objects = [{ 'a': 1 }, { 'b': 2 }];
13545    *
13546    * var shallow = _.clone(objects);
13547    * console.log(shallow[0] === objects[0]);
13548    * // => true
13549    */
13550   function clone(value) {
13551     return baseClone(value, false, true);
13552   }
13553
13554   /**
13555    * This method is like `_.clone` except that it recursively clones `value`.
13556    *
13557    * @static
13558    * @memberOf _
13559    * @since 1.0.0
13560    * @category Lang
13561    * @param {*} value The value to recursively clone.
13562    * @returns {*} Returns the deep cloned value.
13563    * @see _.clone
13564    * @example
13565    *
13566    * var objects = [{ 'a': 1 }, { 'b': 2 }];
13567    *
13568    * var deep = _.cloneDeep(objects);
13569    * console.log(deep[0] === objects[0]);
13570    * // => false
13571    */
13572   function cloneDeep(value) {
13573     return baseClone(value, true, true);
13574   }
13575
13576   /**
13577    * Performs a
13578    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
13579    * comparison between two values to determine if they are equivalent.
13580    *
13581    * @static
13582    * @memberOf _
13583    * @since 4.0.0
13584    * @category Lang
13585    * @param {*} value The value to compare.
13586    * @param {*} other The other value to compare.
13587    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
13588    * @example
13589    *
13590    * var object = { 'user': 'fred' };
13591    * var other = { 'user': 'fred' };
13592    *
13593    * _.eq(object, object);
13594    * // => true
13595    *
13596    * _.eq(object, other);
13597    * // => false
13598    *
13599    * _.eq('a', 'a');
13600    * // => true
13601    *
13602    * _.eq('a', Object('a'));
13603    * // => false
13604    *
13605    * _.eq(NaN, NaN);
13606    * // => true
13607    */
13608   function eq(value, other) {
13609     return value === other || (value !== value && other !== other);
13610   }
13611
13612   /**
13613    * Checks if `value` is likely an `arguments` object.
13614    *
13615    * @static
13616    * @memberOf _
13617    * @since 0.1.0
13618    * @category Lang
13619    * @param {*} value The value to check.
13620    * @returns {boolean} Returns `true` if `value` is correctly classified,
13621    *  else `false`.
13622    * @example
13623    *
13624    * _.isArguments(function() { return arguments; }());
13625    * // => true
13626    *
13627    * _.isArguments([1, 2, 3]);
13628    * // => false
13629    */
13630   function isArguments(value) {
13631     // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.
13632     return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
13633       (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
13634   }
13635
13636   /**
13637    * Checks if `value` is classified as an `Array` object.
13638    *
13639    * @static
13640    * @memberOf _
13641    * @since 0.1.0
13642    * @type {Function}
13643    * @category Lang
13644    * @param {*} value The value to check.
13645    * @returns {boolean} Returns `true` if `value` is correctly classified,
13646    *  else `false`.
13647    * @example
13648    *
13649    * _.isArray([1, 2, 3]);
13650    * // => true
13651    *
13652    * _.isArray(document.body.children);
13653    * // => false
13654    *
13655    * _.isArray('abc');
13656    * // => false
13657    *
13658    * _.isArray(_.noop);
13659    * // => false
13660    */
13661   var isArray = Array.isArray;
13662
13663   /**
13664    * Checks if `value` is array-like. A value is considered array-like if it's
13665    * not a function and has a `value.length` that's an integer greater than or
13666    * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
13667    *
13668    * @static
13669    * @memberOf _
13670    * @since 4.0.0
13671    * @category Lang
13672    * @param {*} value The value to check.
13673    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
13674    * @example
13675    *
13676    * _.isArrayLike([1, 2, 3]);
13677    * // => true
13678    *
13679    * _.isArrayLike(document.body.children);
13680    * // => true
13681    *
13682    * _.isArrayLike('abc');
13683    * // => true
13684    *
13685    * _.isArrayLike(_.noop);
13686    * // => false
13687    */
13688   function isArrayLike(value) {
13689     return value != null && isLength(getLength(value)) && !isFunction(value);
13690   }
13691
13692   /**
13693    * This method is like `_.isArrayLike` except that it also checks if `value`
13694    * is an object.
13695    *
13696    * @static
13697    * @memberOf _
13698    * @since 4.0.0
13699    * @category Lang
13700    * @param {*} value The value to check.
13701    * @returns {boolean} Returns `true` if `value` is an array-like object,
13702    *  else `false`.
13703    * @example
13704    *
13705    * _.isArrayLikeObject([1, 2, 3]);
13706    * // => true
13707    *
13708    * _.isArrayLikeObject(document.body.children);
13709    * // => true
13710    *
13711    * _.isArrayLikeObject('abc');
13712    * // => false
13713    *
13714    * _.isArrayLikeObject(_.noop);
13715    * // => false
13716    */
13717   function isArrayLikeObject(value) {
13718     return isObjectLike(value) && isArrayLike(value);
13719   }
13720
13721   /**
13722    * Checks if `value` is a buffer.
13723    *
13724    * @static
13725    * @memberOf _
13726    * @since 4.3.0
13727    * @category Lang
13728    * @param {*} value The value to check.
13729    * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
13730    * @example
13731    *
13732    * _.isBuffer(new Buffer(2));
13733    * // => true
13734    *
13735    * _.isBuffer(new Uint8Array(2));
13736    * // => false
13737    */
13738   var isBuffer = !Buffer ? constant(false) : function(value) {
13739     return value instanceof Buffer;
13740   };
13741
13742   /**
13743    * Checks if `value` is an empty object, collection, map, or set.
13744    *
13745    * Objects are considered empty if they have no own enumerable string keyed
13746    * properties.
13747    *
13748    * Array-like values such as `arguments` objects, arrays, buffers, strings, or
13749    * jQuery-like collections are considered empty if they have a `length` of `0`.
13750    * Similarly, maps and sets are considered empty if they have a `size` of `0`.
13751    *
13752    * @static
13753    * @memberOf _
13754    * @since 0.1.0
13755    * @category Lang
13756    * @param {*} value The value to check.
13757    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
13758    * @example
13759    *
13760    * _.isEmpty(null);
13761    * // => true
13762    *
13763    * _.isEmpty(true);
13764    * // => true
13765    *
13766    * _.isEmpty(1);
13767    * // => true
13768    *
13769    * _.isEmpty([1, 2, 3]);
13770    * // => false
13771    *
13772    * _.isEmpty({ 'a': 1 });
13773    * // => false
13774    */
13775   function isEmpty(value) {
13776     if (isArrayLike(value) &&
13777         (isArray(value) || isString(value) || isFunction(value.splice) ||
13778           isArguments(value) || isBuffer(value))) {
13779       return !value.length;
13780     }
13781     if (isObjectLike(value)) {
13782       var tag = getTag(value);
13783       if (tag == mapTag || tag == setTag) {
13784         return !value.size;
13785       }
13786     }
13787     for (var key in value) {
13788       if (hasOwnProperty.call(value, key)) {
13789         return false;
13790       }
13791     }
13792     return !(nonEnumShadows && keys(value).length);
13793   }
13794
13795   /**
13796    * Performs a deep comparison between two values to determine if they are
13797    * equivalent.
13798    *
13799    * **Note:** This method supports comparing arrays, array buffers, booleans,
13800    * date objects, error objects, maps, numbers, `Object` objects, regexes,
13801    * sets, strings, symbols, and typed arrays. `Object` objects are compared
13802    * by their own, not inherited, enumerable properties. Functions and DOM
13803    * nodes are **not** supported.
13804    *
13805    * @static
13806    * @memberOf _
13807    * @since 0.1.0
13808    * @category Lang
13809    * @param {*} value The value to compare.
13810    * @param {*} other The other value to compare.
13811    * @returns {boolean} Returns `true` if the values are equivalent,
13812    *  else `false`.
13813    * @example
13814    *
13815    * var object = { 'user': 'fred' };
13816    * var other = { 'user': 'fred' };
13817    *
13818    * _.isEqual(object, other);
13819    * // => true
13820    *
13821    * object === other;
13822    * // => false
13823    */
13824   function isEqual(value, other) {
13825     return baseIsEqual(value, other);
13826   }
13827
13828   /**
13829    * Checks if `value` is classified as a `Function` object.
13830    *
13831    * @static
13832    * @memberOf _
13833    * @since 0.1.0
13834    * @category Lang
13835    * @param {*} value The value to check.
13836    * @returns {boolean} Returns `true` if `value` is correctly classified,
13837    *  else `false`.
13838    * @example
13839    *
13840    * _.isFunction(_);
13841    * // => true
13842    *
13843    * _.isFunction(/abc/);
13844    * // => false
13845    */
13846   function isFunction(value) {
13847     // The use of `Object#toString` avoids issues with the `typeof` operator
13848     // in Safari 8 which returns 'object' for typed array and weak map constructors,
13849     // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
13850     var tag = isObject(value) ? objectToString.call(value) : '';
13851     return tag == funcTag || tag == genTag;
13852   }
13853
13854   /**
13855    * Checks if `value` is a valid array-like length.
13856    *
13857    * **Note:** This function is loosely based on
13858    * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
13859    *
13860    * @static
13861    * @memberOf _
13862    * @since 4.0.0
13863    * @category Lang
13864    * @param {*} value The value to check.
13865    * @returns {boolean} Returns `true` if `value` is a valid length,
13866    *  else `false`.
13867    * @example
13868    *
13869    * _.isLength(3);
13870    * // => true
13871    *
13872    * _.isLength(Number.MIN_VALUE);
13873    * // => false
13874    *
13875    * _.isLength(Infinity);
13876    * // => false
13877    *
13878    * _.isLength('3');
13879    * // => false
13880    */
13881   function isLength(value) {
13882     return typeof value == 'number' &&
13883       value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
13884   }
13885
13886   /**
13887    * Checks if `value` is the
13888    * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)
13889    * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
13890    *
13891    * @static
13892    * @memberOf _
13893    * @since 0.1.0
13894    * @category Lang
13895    * @param {*} value The value to check.
13896    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
13897    * @example
13898    *
13899    * _.isObject({});
13900    * // => true
13901    *
13902    * _.isObject([1, 2, 3]);
13903    * // => true
13904    *
13905    * _.isObject(_.noop);
13906    * // => true
13907    *
13908    * _.isObject(null);
13909    * // => false
13910    */
13911   function isObject(value) {
13912     var type = typeof value;
13913     return !!value && (type == 'object' || type == 'function');
13914   }
13915
13916   /**
13917    * Checks if `value` is object-like. A value is object-like if it's not `null`
13918    * and has a `typeof` result of "object".
13919    *
13920    * @static
13921    * @memberOf _
13922    * @since 4.0.0
13923    * @category Lang
13924    * @param {*} value The value to check.
13925    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
13926    * @example
13927    *
13928    * _.isObjectLike({});
13929    * // => true
13930    *
13931    * _.isObjectLike([1, 2, 3]);
13932    * // => true
13933    *
13934    * _.isObjectLike(_.noop);
13935    * // => false
13936    *
13937    * _.isObjectLike(null);
13938    * // => false
13939    */
13940   function isObjectLike(value) {
13941     return !!value && typeof value == 'object';
13942   }
13943
13944   /**
13945    * Checks if `value` is a native function.
13946    *
13947    * @static
13948    * @memberOf _
13949    * @since 3.0.0
13950    * @category Lang
13951    * @param {*} value The value to check.
13952    * @returns {boolean} Returns `true` if `value` is a native function,
13953    *  else `false`.
13954    * @example
13955    *
13956    * _.isNative(Array.prototype.push);
13957    * // => true
13958    *
13959    * _.isNative(_);
13960    * // => false
13961    */
13962   function isNative(value) {
13963     if (!isObject(value)) {
13964       return false;
13965     }
13966     var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
13967     return pattern.test(toSource(value));
13968   }
13969
13970   /**
13971    * Checks if `value` is a plain object, that is, an object created by the
13972    * `Object` constructor or one with a `[[Prototype]]` of `null`.
13973    *
13974    * @static
13975    * @memberOf _
13976    * @since 0.8.0
13977    * @category Lang
13978    * @param {*} value The value to check.
13979    * @returns {boolean} Returns `true` if `value` is a plain object,
13980    *  else `false`.
13981    * @example
13982    *
13983    * function Foo() {
13984    *   this.a = 1;
13985    * }
13986    *
13987    * _.isPlainObject(new Foo);
13988    * // => false
13989    *
13990    * _.isPlainObject([1, 2, 3]);
13991    * // => false
13992    *
13993    * _.isPlainObject({ 'x': 0, 'y': 0 });
13994    * // => true
13995    *
13996    * _.isPlainObject(Object.create(null));
13997    * // => true
13998    */
13999   function isPlainObject(value) {
14000     if (!isObjectLike(value) ||
14001         objectToString.call(value) != objectTag || isHostObject(value)) {
14002       return false;
14003     }
14004     var proto = getPrototype(value);
14005     if (proto === null) {
14006       return true;
14007     }
14008     var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
14009     return (typeof Ctor == 'function' &&
14010       Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
14011   }
14012
14013   /**
14014    * Checks if `value` is classified as a `String` primitive or object.
14015    *
14016    * @static
14017    * @since 0.1.0
14018    * @memberOf _
14019    * @category Lang
14020    * @param {*} value The value to check.
14021    * @returns {boolean} Returns `true` if `value` is correctly classified,
14022    *  else `false`.
14023    * @example
14024    *
14025    * _.isString('abc');
14026    * // => true
14027    *
14028    * _.isString(1);
14029    * // => false
14030    */
14031   function isString(value) {
14032     return typeof value == 'string' ||
14033       (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);
14034   }
14035
14036   /**
14037    * Checks if `value` is classified as a `Symbol` primitive or object.
14038    *
14039    * @static
14040    * @memberOf _
14041    * @since 4.0.0
14042    * @category Lang
14043    * @param {*} value The value to check.
14044    * @returns {boolean} Returns `true` if `value` is correctly classified,
14045    *  else `false`.
14046    * @example
14047    *
14048    * _.isSymbol(Symbol.iterator);
14049    * // => true
14050    *
14051    * _.isSymbol('abc');
14052    * // => false
14053    */
14054   function isSymbol(value) {
14055     return typeof value == 'symbol' ||
14056       (isObjectLike(value) && objectToString.call(value) == symbolTag);
14057   }
14058
14059   /**
14060    * Checks if `value` is classified as a typed array.
14061    *
14062    * @static
14063    * @memberOf _
14064    * @since 3.0.0
14065    * @category Lang
14066    * @param {*} value The value to check.
14067    * @returns {boolean} Returns `true` if `value` is correctly classified,
14068    *  else `false`.
14069    * @example
14070    *
14071    * _.isTypedArray(new Uint8Array);
14072    * // => true
14073    *
14074    * _.isTypedArray([]);
14075    * // => false
14076    */
14077   function isTypedArray(value) {
14078     return isObjectLike(value) &&
14079       isLength(value.length) && !!typedArrayTags[objectToString.call(value)];
14080   }
14081
14082   /**
14083    * Converts `value` to an array.
14084    *
14085    * @static
14086    * @since 0.1.0
14087    * @memberOf _
14088    * @category Lang
14089    * @param {*} value The value to convert.
14090    * @returns {Array} Returns the converted array.
14091    * @example
14092    *
14093    * _.toArray({ 'a': 1, 'b': 2 });
14094    * // => [1, 2]
14095    *
14096    * _.toArray('abc');
14097    * // => ['a', 'b', 'c']
14098    *
14099    * _.toArray(1);
14100    * // => []
14101    *
14102    * _.toArray(null);
14103    * // => []
14104    */
14105   function toArray(value) {
14106     if (!value) {
14107       return [];
14108     }
14109     if (isArrayLike(value)) {
14110       return isString(value) ? stringToArray(value) : copyArray(value);
14111     }
14112     if (iteratorSymbol && value[iteratorSymbol]) {
14113       return iteratorToArray(value[iteratorSymbol]());
14114     }
14115     var tag = getTag(value),
14116         func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
14117
14118     return func(value);
14119   }
14120
14121   /**
14122    * Converts `value` to a finite number.
14123    *
14124    * @static
14125    * @memberOf _
14126    * @since 4.12.0
14127    * @category Lang
14128    * @param {*} value The value to convert.
14129    * @returns {number} Returns the converted number.
14130    * @example
14131    *
14132    * _.toFinite(3.2);
14133    * // => 3.2
14134    *
14135    * _.toFinite(Number.MIN_VALUE);
14136    * // => 5e-324
14137    *
14138    * _.toFinite(Infinity);
14139    * // => 1.7976931348623157e+308
14140    *
14141    * _.toFinite('3.2');
14142    * // => 3.2
14143    */
14144   function toFinite(value) {
14145     if (!value) {
14146       return value === 0 ? value : 0;
14147     }
14148     value = toNumber(value);
14149     if (value === INFINITY || value === -INFINITY) {
14150       var sign = (value < 0 ? -1 : 1);
14151       return sign * MAX_INTEGER;
14152     }
14153     return value === value ? value : 0;
14154   }
14155
14156   /**
14157    * Converts `value` to an integer.
14158    *
14159    * **Note:** This function is loosely based on
14160    * [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
14161    *
14162    * @static
14163    * @memberOf _
14164    * @since 4.0.0
14165    * @category Lang
14166    * @param {*} value The value to convert.
14167    * @returns {number} Returns the converted integer.
14168    * @example
14169    *
14170    * _.toInteger(3.2);
14171    * // => 3
14172    *
14173    * _.toInteger(Number.MIN_VALUE);
14174    * // => 0
14175    *
14176    * _.toInteger(Infinity);
14177    * // => 1.7976931348623157e+308
14178    *
14179    * _.toInteger('3.2');
14180    * // => 3
14181    */
14182   function toInteger(value) {
14183     var result = toFinite(value),
14184         remainder = result % 1;
14185
14186     return result === result ? (remainder ? result - remainder : result) : 0;
14187   }
14188
14189   /**
14190    * Converts `value` to a number.
14191    *
14192    * @static
14193    * @memberOf _
14194    * @since 4.0.0
14195    * @category Lang
14196    * @param {*} value The value to process.
14197    * @returns {number} Returns the number.
14198    * @example
14199    *
14200    * _.toNumber(3.2);
14201    * // => 3.2
14202    *
14203    * _.toNumber(Number.MIN_VALUE);
14204    * // => 5e-324
14205    *
14206    * _.toNumber(Infinity);
14207    * // => Infinity
14208    *
14209    * _.toNumber('3.2');
14210    * // => 3.2
14211    */
14212   function toNumber(value) {
14213     if (typeof value == 'number') {
14214       return value;
14215     }
14216     if (isSymbol(value)) {
14217       return NAN;
14218     }
14219     if (isObject(value)) {
14220       var other = isFunction(value.valueOf) ? value.valueOf() : value;
14221       value = isObject(other) ? (other + '') : other;
14222     }
14223     if (typeof value != 'string') {
14224       return value === 0 ? value : +value;
14225     }
14226     value = value.replace(reTrim, '');
14227     var isBinary = reIsBinary.test(value);
14228     return (isBinary || reIsOctal.test(value))
14229       ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
14230       : (reIsBadHex.test(value) ? NAN : +value);
14231   }
14232
14233   /**
14234    * Converts `value` to a plain object flattening inherited enumerable string
14235    * keyed properties of `value` to own properties of the plain object.
14236    *
14237    * @static
14238    * @memberOf _
14239    * @since 3.0.0
14240    * @category Lang
14241    * @param {*} value The value to convert.
14242    * @returns {Object} Returns the converted plain object.
14243    * @example
14244    *
14245    * function Foo() {
14246    *   this.b = 2;
14247    * }
14248    *
14249    * Foo.prototype.c = 3;
14250    *
14251    * _.assign({ 'a': 1 }, new Foo);
14252    * // => { 'a': 1, 'b': 2 }
14253    *
14254    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
14255    * // => { 'a': 1, 'b': 2, 'c': 3 }
14256    */
14257   function toPlainObject(value) {
14258     return copyObject(value, keysIn(value));
14259   }
14260
14261   /**
14262    * Converts `value` to a string. An empty string is returned for `null`
14263    * and `undefined` values. The sign of `-0` is preserved.
14264    *
14265    * @static
14266    * @memberOf _
14267    * @since 4.0.0
14268    * @category Lang
14269    * @param {*} value The value to process.
14270    * @returns {string} Returns the string.
14271    * @example
14272    *
14273    * _.toString(null);
14274    * // => ''
14275    *
14276    * _.toString(-0);
14277    * // => '-0'
14278    *
14279    * _.toString([1, 2, 3]);
14280    * // => '1,2,3'
14281    */
14282   function toString(value) {
14283     return value == null ? '' : baseToString(value);
14284   }
14285
14286   /*------------------------------------------------------------------------*/
14287
14288   /**
14289    * Assigns own enumerable string keyed properties of source objects to the
14290    * destination object. Source objects are applied from left to right.
14291    * Subsequent sources overwrite property assignments of previous sources.
14292    *
14293    * **Note:** This method mutates `object` and is loosely based on
14294    * [`Object.assign`](https://mdn.io/Object/assign).
14295    *
14296    * @static
14297    * @memberOf _
14298    * @since 0.10.0
14299    * @category Object
14300    * @param {Object} object The destination object.
14301    * @param {...Object} [sources] The source objects.
14302    * @returns {Object} Returns `object`.
14303    * @see _.assignIn
14304    * @example
14305    *
14306    * function Foo() {
14307    *   this.c = 3;
14308    * }
14309    *
14310    * function Bar() {
14311    *   this.e = 5;
14312    * }
14313    *
14314    * Foo.prototype.d = 4;
14315    * Bar.prototype.f = 6;
14316    *
14317    * _.assign({ 'a': 1 }, new Foo, new Bar);
14318    * // => { 'a': 1, 'c': 3, 'e': 5 }
14319    */
14320   var assign = createAssigner(function(object, source) {
14321     if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
14322       copyObject(source, keys(source), object);
14323       return;
14324     }
14325     for (var key in source) {
14326       if (hasOwnProperty.call(source, key)) {
14327         assignValue(object, key, source[key]);
14328       }
14329     }
14330   });
14331
14332   /**
14333    * This method is like `_.assign` except that it iterates over own and
14334    * inherited source properties.
14335    *
14336    * **Note:** This method mutates `object`.
14337    *
14338    * @static
14339    * @memberOf _
14340    * @since 4.0.0
14341    * @alias extend
14342    * @category Object
14343    * @param {Object} object The destination object.
14344    * @param {...Object} [sources] The source objects.
14345    * @returns {Object} Returns `object`.
14346    * @see _.assign
14347    * @example
14348    *
14349    * function Foo() {
14350    *   this.b = 2;
14351    * }
14352    *
14353    * function Bar() {
14354    *   this.d = 4;
14355    * }
14356    *
14357    * Foo.prototype.c = 3;
14358    * Bar.prototype.e = 5;
14359    *
14360    * _.assignIn({ 'a': 1 }, new Foo, new Bar);
14361    * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }
14362    */
14363   var assignIn = createAssigner(function(object, source) {
14364     if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
14365       copyObject(source, keysIn(source), object);
14366       return;
14367     }
14368     for (var key in source) {
14369       assignValue(object, key, source[key]);
14370     }
14371   });
14372
14373   /**
14374    * Iterates over own enumerable string keyed properties of an object and
14375    * invokes `iteratee` for each property. The iteratee is invoked with three
14376    * arguments: (value, key, object). Iteratee functions may exit iteration
14377    * early by explicitly returning `false`.
14378    *
14379    * @static
14380    * @memberOf _
14381    * @since 0.3.0
14382    * @category Object
14383    * @param {Object} object The object to iterate over.
14384    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
14385    * @returns {Object} Returns `object`.
14386    * @see _.forOwnRight
14387    * @example
14388    *
14389    * function Foo() {
14390    *   this.a = 1;
14391    *   this.b = 2;
14392    * }
14393    *
14394    * Foo.prototype.c = 3;
14395    *
14396    * _.forOwn(new Foo, function(value, key) {
14397    *   console.log(key);
14398    * });
14399    * // => Logs 'a' then 'b' (iteration order is not guaranteed).
14400    */
14401   function forOwn(object, iteratee) {
14402     return object && baseForOwn(object, getIteratee(iteratee, 3));
14403   }
14404
14405   /**
14406    * Gets the value at `path` of `object`. If the resolved value is
14407    * `undefined`, the `defaultValue` is used in its place.
14408    *
14409    * @static
14410    * @memberOf _
14411    * @since 3.7.0
14412    * @category Object
14413    * @param {Object} object The object to query.
14414    * @param {Array|string} path The path of the property to get.
14415    * @param {*} [defaultValue] The value returned for `undefined` resolved values.
14416    * @returns {*} Returns the resolved value.
14417    * @example
14418    *
14419    * var object = { 'a': [{ 'b': { 'c': 3 } }] };
14420    *
14421    * _.get(object, 'a[0].b.c');
14422    * // => 3
14423    *
14424    * _.get(object, ['a', '0', 'b', 'c']);
14425    * // => 3
14426    *
14427    * _.get(object, 'a.b.c', 'default');
14428    * // => 'default'
14429    */
14430   function get(object, path, defaultValue) {
14431     var result = object == null ? undefined : baseGet(object, path);
14432     return result === undefined ? defaultValue : result;
14433   }
14434
14435   /**
14436    * Checks if `path` is a direct or inherited property of `object`.
14437    *
14438    * @static
14439    * @memberOf _
14440    * @since 4.0.0
14441    * @category Object
14442    * @param {Object} object The object to query.
14443    * @param {Array|string} path The path to check.
14444    * @returns {boolean} Returns `true` if `path` exists, else `false`.
14445    * @example
14446    *
14447    * var object = _.create({ 'a': _.create({ 'b': 2 }) });
14448    *
14449    * _.hasIn(object, 'a');
14450    * // => true
14451    *
14452    * _.hasIn(object, 'a.b');
14453    * // => true
14454    *
14455    * _.hasIn(object, ['a', 'b']);
14456    * // => true
14457    *
14458    * _.hasIn(object, 'b');
14459    * // => false
14460    */
14461   function hasIn(object, path) {
14462     return object != null && hasPath(object, path, baseHasIn);
14463   }
14464
14465   /**
14466    * Creates an array of the own enumerable property names of `object`.
14467    *
14468    * **Note:** Non-object values are coerced to objects. See the
14469    * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
14470    * for more details.
14471    *
14472    * @static
14473    * @since 0.1.0
14474    * @memberOf _
14475    * @category Object
14476    * @param {Object} object The object to query.
14477    * @returns {Array} Returns the array of property names.
14478    * @example
14479    *
14480    * function Foo() {
14481    *   this.a = 1;
14482    *   this.b = 2;
14483    * }
14484    *
14485    * Foo.prototype.c = 3;
14486    *
14487    * _.keys(new Foo);
14488    * // => ['a', 'b'] (iteration order is not guaranteed)
14489    *
14490    * _.keys('hi');
14491    * // => ['0', '1']
14492    */
14493   function keys(object) {
14494     var isProto = isPrototype(object);
14495     if (!(isProto || isArrayLike(object))) {
14496       return baseKeys(object);
14497     }
14498     var indexes = indexKeys(object),
14499         skipIndexes = !!indexes,
14500         result = indexes || [],
14501         length = result.length;
14502
14503     for (var key in object) {
14504       if (baseHas(object, key) &&
14505           !(skipIndexes && (key == 'length' || isIndex(key, length))) &&
14506           !(isProto && key == 'constructor')) {
14507         result.push(key);
14508       }
14509     }
14510     return result;
14511   }
14512
14513   /**
14514    * Creates an array of the own and inherited enumerable property names of `object`.
14515    *
14516    * **Note:** Non-object values are coerced to objects.
14517    *
14518    * @static
14519    * @memberOf _
14520    * @since 3.0.0
14521    * @category Object
14522    * @param {Object} object The object to query.
14523    * @returns {Array} Returns the array of property names.
14524    * @example
14525    *
14526    * function Foo() {
14527    *   this.a = 1;
14528    *   this.b = 2;
14529    * }
14530    *
14531    * Foo.prototype.c = 3;
14532    *
14533    * _.keysIn(new Foo);
14534    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
14535    */
14536   function keysIn(object) {
14537     var index = -1,
14538         isProto = isPrototype(object),
14539         props = baseKeysIn(object),
14540         propsLength = props.length,
14541         indexes = indexKeys(object),
14542         skipIndexes = !!indexes,
14543         result = indexes || [],
14544         length = result.length;
14545
14546     while (++index < propsLength) {
14547       var key = props[index];
14548       if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
14549           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
14550         result.push(key);
14551       }
14552     }
14553     return result;
14554   }
14555
14556   /**
14557    * This method is like `_.assign` except that it recursively merges own and
14558    * inherited enumerable string keyed properties of source objects into the
14559    * destination object. Source properties that resolve to `undefined` are
14560    * skipped if a destination value exists. Array and plain object properties
14561    * are merged recursively. Other objects and value types are overridden by
14562    * assignment. Source objects are applied from left to right. Subsequent
14563    * sources overwrite property assignments of previous sources.
14564    *
14565    * **Note:** This method mutates `object`.
14566    *
14567    * @static
14568    * @memberOf _
14569    * @since 0.5.0
14570    * @category Object
14571    * @param {Object} object The destination object.
14572    * @param {...Object} [sources] The source objects.
14573    * @returns {Object} Returns `object`.
14574    * @example
14575    *
14576    * var users = {
14577    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
14578    * };
14579    *
14580    * var ages = {
14581    *   'data': [{ 'age': 36 }, { 'age': 40 }]
14582    * };
14583    *
14584    * _.merge(users, ages);
14585    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
14586    */
14587   var merge = createAssigner(function(object, source, srcIndex) {
14588     baseMerge(object, source, srcIndex);
14589   });
14590
14591   /**
14592    * The opposite of `_.pick`; this method creates an object composed of the
14593    * own and inherited enumerable string keyed properties of `object` that are
14594    * not omitted.
14595    *
14596    * @static
14597    * @since 0.1.0
14598    * @memberOf _
14599    * @category Object
14600    * @param {Object} object The source object.
14601    * @param {...(string|string[])} [props] The property identifiers to omit.
14602    * @returns {Object} Returns the new object.
14603    * @example
14604    *
14605    * var object = { 'a': 1, 'b': '2', 'c': 3 };
14606    *
14607    * _.omit(object, ['a', 'c']);
14608    * // => { 'b': '2' }
14609    */
14610   var omit = rest(function(object, props) {
14611     if (object == null) {
14612       return {};
14613     }
14614     props = arrayMap(baseFlatten(props, 1), toKey);
14615     return basePick(object, baseDifference(getAllKeysIn(object), props));
14616   });
14617
14618   /**
14619    * Creates an object composed of the picked `object` properties.
14620    *
14621    * @static
14622    * @since 0.1.0
14623    * @memberOf _
14624    * @category Object
14625    * @param {Object} object The source object.
14626    * @param {...(string|string[])} [props] The property identifiers to pick.
14627    * @returns {Object} Returns the new object.
14628    * @example
14629    *
14630    * var object = { 'a': 1, 'b': '2', 'c': 3 };
14631    *
14632    * _.pick(object, ['a', 'c']);
14633    * // => { 'a': 1, 'c': 3 }
14634    */
14635   var pick = rest(function(object, props) {
14636     return object == null ? {} : basePick(object, arrayMap(baseFlatten(props, 1), toKey));
14637   });
14638
14639   /**
14640    * Creates an array of own enumerable string keyed-value pairs for `object`
14641    * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
14642    * entries are returned.
14643    *
14644    * @static
14645    * @memberOf _
14646    * @since 4.0.0
14647    * @alias entries
14648    * @category Object
14649    * @param {Object} object The object to query.
14650    * @returns {Array} Returns the key-value pairs.
14651    * @example
14652    *
14653    * function Foo() {
14654    *   this.a = 1;
14655    *   this.b = 2;
14656    * }
14657    *
14658    * Foo.prototype.c = 3;
14659    *
14660    * _.toPairs(new Foo);
14661    * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
14662    */
14663   var toPairs = createToPairs(keys);
14664
14665   /**
14666    * Creates an array of the own enumerable string keyed property values of `object`.
14667    *
14668    * **Note:** Non-object values are coerced to objects.
14669    *
14670    * @static
14671    * @since 0.1.0
14672    * @memberOf _
14673    * @category Object
14674    * @param {Object} object The object to query.
14675    * @returns {Array} Returns the array of property values.
14676    * @example
14677    *
14678    * function Foo() {
14679    *   this.a = 1;
14680    *   this.b = 2;
14681    * }
14682    *
14683    * Foo.prototype.c = 3;
14684    *
14685    * _.values(new Foo);
14686    * // => [1, 2] (iteration order is not guaranteed)
14687    *
14688    * _.values('hi');
14689    * // => ['h', 'i']
14690    */
14691   function values(object) {
14692     return object ? baseValues(object, keys(object)) : [];
14693   }
14694
14695   /*------------------------------------------------------------------------*/
14696
14697   /**
14698    * Creates a function that returns `value`.
14699    *
14700    * @static
14701    * @memberOf _
14702    * @since 2.4.0
14703    * @category Util
14704    * @param {*} value The value to return from the new function.
14705    * @returns {Function} Returns the new constant function.
14706    * @example
14707    *
14708    * var object = { 'user': 'fred' };
14709    * var getter = _.constant(object);
14710    *
14711    * getter() === object;
14712    * // => true
14713    */
14714   function constant(value) {
14715     return function() {
14716       return value;
14717     };
14718   }
14719
14720   /**
14721    * This method returns the first argument given to it.
14722    *
14723    * @static
14724    * @since 0.1.0
14725    * @memberOf _
14726    * @category Util
14727    * @param {*} value Any value.
14728    * @returns {*} Returns `value`.
14729    * @example
14730    *
14731    * var object = { 'user': 'fred' };
14732    *
14733    * _.identity(object) === object;
14734    * // => true
14735    */
14736   function identity(value) {
14737     return value;
14738   }
14739
14740   /**
14741    * Creates a function that invokes `func` with the arguments of the created
14742    * function. If `func` is a property name, the created function returns the
14743    * property value for a given element. If `func` is an array or object, the
14744    * created function returns `true` for elements that contain the equivalent
14745    * source properties, otherwise it returns `false`.
14746    *
14747    * @static
14748    * @since 4.0.0
14749    * @memberOf _
14750    * @category Util
14751    * @param {*} [func=_.identity] The value to convert to a callback.
14752    * @returns {Function} Returns the callback.
14753    * @example
14754    *
14755    * var users = [
14756    *   { 'user': 'barney', 'age': 36, 'active': true },
14757    *   { 'user': 'fred',   'age': 40, 'active': false }
14758    * ];
14759    *
14760    * // The `_.matches` iteratee shorthand.
14761    * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));
14762    * // => [{ 'user': 'barney', 'age': 36, 'active': true }]
14763    *
14764    * // The `_.matchesProperty` iteratee shorthand.
14765    * _.filter(users, _.iteratee(['user', 'fred']));
14766    * // => [{ 'user': 'fred', 'age': 40 }]
14767    *
14768    * // The `_.property` iteratee shorthand.
14769    * _.map(users, _.iteratee('user'));
14770    * // => ['barney', 'fred']
14771    *
14772    * // Create custom iteratee shorthands.
14773    * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {
14774    *   return !_.isRegExp(func) ? iteratee(func) : function(string) {
14775    *     return func.test(string);
14776    *   };
14777    * });
14778    *
14779    * _.filter(['abc', 'def'], /ef/);
14780    * // => ['def']
14781    */
14782   function iteratee(func) {
14783     return baseIteratee(typeof func == 'function' ? func : baseClone(func, true));
14784   }
14785
14786   /**
14787    * Adds all own enumerable string keyed function properties of a source
14788    * object to the destination object. If `object` is a function, then methods
14789    * are added to its prototype as well.
14790    *
14791    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
14792    * avoid conflicts caused by modifying the original.
14793    *
14794    * @static
14795    * @since 0.1.0
14796    * @memberOf _
14797    * @category Util
14798    * @param {Function|Object} [object=lodash] The destination object.
14799    * @param {Object} source The object of functions to add.
14800    * @param {Object} [options={}] The options object.
14801    * @param {boolean} [options.chain=true] Specify whether mixins are chainable.
14802    * @returns {Function|Object} Returns `object`.
14803    * @example
14804    *
14805    * function vowels(string) {
14806    *   return _.filter(string, function(v) {
14807    *     return /[aeiou]/i.test(v);
14808    *   });
14809    * }
14810    *
14811    * _.mixin({ 'vowels': vowels });
14812    * _.vowels('fred');
14813    * // => ['e']
14814    *
14815    * _('fred').vowels().value();
14816    * // => ['e']
14817    *
14818    * _.mixin({ 'vowels': vowels }, { 'chain': false });
14819    * _('fred').vowels();
14820    * // => ['e']
14821    */
14822   function mixin(object, source, options) {
14823     var props = keys(source),
14824         methodNames = baseFunctions(source, props);
14825
14826     if (options == null &&
14827         !(isObject(source) && (methodNames.length || !props.length))) {
14828       options = source;
14829       source = object;
14830       object = this;
14831       methodNames = baseFunctions(source, keys(source));
14832     }
14833     var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
14834         isFunc = isFunction(object);
14835
14836     arrayEach(methodNames, function(methodName) {
14837       var func = source[methodName];
14838       object[methodName] = func;
14839       if (isFunc) {
14840         object.prototype[methodName] = function() {
14841           var chainAll = this.__chain__;
14842           if (chain || chainAll) {
14843             var result = object(this.__wrapped__),
14844                 actions = result.__actions__ = copyArray(this.__actions__);
14845
14846             actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
14847             result.__chain__ = chainAll;
14848             return result;
14849           }
14850           return func.apply(object, arrayPush([this.value()], arguments));
14851         };
14852       }
14853     });
14854
14855     return object;
14856   }
14857
14858   /**
14859    * A no-operation function that returns `undefined` regardless of the
14860    * arguments it receives.
14861    *
14862    * @static
14863    * @memberOf _
14864    * @since 2.3.0
14865    * @category Util
14866    * @example
14867    *
14868    * var object = { 'user': 'fred' };
14869    *
14870    * _.noop(object) === undefined;
14871    * // => true
14872    */
14873   function noop() {
14874     // No operation performed.
14875   }
14876
14877   /**
14878    * Creates a function that returns the value at `path` of a given object.
14879    *
14880    * @static
14881    * @memberOf _
14882    * @since 2.4.0
14883    * @category Util
14884    * @param {Array|string} path The path of the property to get.
14885    * @returns {Function} Returns the new accessor function.
14886    * @example
14887    *
14888    * var objects = [
14889    *   { 'a': { 'b': 2 } },
14890    *   { 'a': { 'b': 1 } }
14891    * ];
14892    *
14893    * _.map(objects, _.property('a.b'));
14894    * // => [2, 1]
14895    *
14896    * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
14897    * // => [1, 2]
14898    */
14899   function property(path) {
14900     return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
14901   }
14902
14903   /*------------------------------------------------------------------------*/
14904
14905   // Add methods that return wrapped values in chain sequences.
14906   lodash.assign = assign;
14907   lodash.assignIn = assignIn;
14908   lodash.bind = bind;
14909   lodash.chain = chain;
14910   lodash.chunk = chunk;
14911   lodash.compact = compact;
14912   lodash.constant = constant;
14913   lodash.debounce = debounce;
14914   lodash.difference = difference;
14915   lodash.filter = filter;
14916   lodash.flatten = flatten;
14917   lodash.groupBy = groupBy;
14918   lodash.intersection = intersection;
14919   lodash.iteratee = iteratee;
14920   lodash.keys = keys;
14921   lodash.keysIn = keysIn;
14922   lodash.map = map;
14923   lodash.memoize = memoize;
14924   lodash.merge = merge;
14925   lodash.mixin = mixin;
14926   lodash.omit = omit;
14927   lodash.pick = pick;
14928   lodash.property = property;
14929   lodash.reject = reject;
14930   lodash.rest = rest;
14931   lodash.reverse = reverse;
14932   lodash.tap = tap;
14933   lodash.throttle = throttle;
14934   lodash.thru = thru;
14935   lodash.toArray = toArray;
14936   lodash.toPairs = toPairs;
14937   lodash.toPlainObject = toPlainObject;
14938   lodash.union = union;
14939   lodash.uniq = uniq;
14940   lodash.values = values;
14941   lodash.without = without;
14942
14943   // Add aliases.
14944   lodash.entries = toPairs;
14945   lodash.extend = assignIn;
14946
14947   // Add methods to `lodash.prototype`.
14948   mixin(lodash, lodash);
14949
14950   /*------------------------------------------------------------------------*/
14951
14952   // Add methods that return unwrapped values in chain sequences.
14953   lodash.clone = clone;
14954   lodash.cloneDeep = cloneDeep;
14955   lodash.eq = eq;
14956   lodash.every = every;
14957   lodash.find = find;
14958   lodash.forEach = forEach;
14959   lodash.forOwn = forOwn;
14960   lodash.get = get;
14961   lodash.hasIn = hasIn;
14962   lodash.head = head;
14963   lodash.identity = identity;
14964   lodash.includes = includes;
14965   lodash.indexOf = indexOf;
14966   lodash.isArguments = isArguments;
14967   lodash.isArray = isArray;
14968   lodash.isArrayLike = isArrayLike;
14969   lodash.isArrayLikeObject = isArrayLikeObject;
14970   lodash.isBuffer = isBuffer;
14971   lodash.isEmpty = isEmpty;
14972   lodash.isEqual = isEqual;
14973   lodash.isFunction = isFunction;
14974   lodash.isLength = isLength;
14975   lodash.isNative = isNative;
14976   lodash.isObject = isObject;
14977   lodash.isObjectLike = isObjectLike;
14978   lodash.isPlainObject = isPlainObject;
14979   lodash.isString = isString;
14980   lodash.isSymbol = isSymbol;
14981   lodash.isTypedArray = isTypedArray;
14982   lodash.last = last;
14983   lodash.noop = noop;
14984   lodash.now = now;
14985   lodash.reduce = reduce;
14986   lodash.some = some;
14987   lodash.toFinite = toFinite;
14988   lodash.toInteger = toInteger;
14989   lodash.toNumber = toNumber;
14990   lodash.toString = toString;
14991
14992   // Add aliases.
14993   lodash.each = forEach;
14994   lodash.first = head;
14995
14996   mixin(lodash, (function() {
14997     var source = {};
14998     baseForOwn(lodash, function(func, methodName) {
14999       if (!hasOwnProperty.call(lodash.prototype, methodName)) {
15000         source[methodName] = func;
15001       }
15002     });
15003     return source;
15004   }()), { 'chain': false });
15005
15006   /*------------------------------------------------------------------------*/
15007
15008   /**
15009    * The semantic version number.
15010    *
15011    * @static
15012    * @memberOf _
15013    * @type {string}
15014    */
15015   lodash.VERSION = VERSION;
15016
15017   // Assign default placeholders.
15018   bind.placeholder = lodash;
15019
15020   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
15021   arrayEach(['drop', 'take'], function(methodName, index) {
15022     LazyWrapper.prototype[methodName] = function(n) {
15023       var filtered = this.__filtered__;
15024       if (filtered && !index) {
15025         return new LazyWrapper(this);
15026       }
15027       n = n === undefined ? 1 : nativeMax(toInteger(n), 0);
15028
15029       var result = this.clone();
15030       if (filtered) {
15031         result.__takeCount__ = nativeMin(n, result.__takeCount__);
15032       } else {
15033         result.__views__.push({
15034           'size': nativeMin(n, MAX_ARRAY_LENGTH),
15035           'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
15036         });
15037       }
15038       return result;
15039     };
15040
15041     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
15042       return this.reverse()[methodName](n).reverse();
15043     };
15044   });
15045
15046   // Add `LazyWrapper` methods that accept an `iteratee` value.
15047   arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
15048     var type = index + 1,
15049         isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
15050
15051     LazyWrapper.prototype[methodName] = function(iteratee) {
15052       var result = this.clone();
15053       result.__iteratees__.push({
15054         'iteratee': getIteratee(iteratee, 3),
15055         'type': type
15056       });
15057       result.__filtered__ = result.__filtered__ || isFilter;
15058       return result;
15059     };
15060   });
15061
15062   // Add `LazyWrapper` methods for `_.head` and `_.last`.
15063   arrayEach(['head', 'last'], function(methodName, index) {
15064     var takeName = 'take' + (index ? 'Right' : '');
15065
15066     LazyWrapper.prototype[methodName] = function() {
15067       return this[takeName](1).value()[0];
15068     };
15069   });
15070
15071   // Add `LazyWrapper` methods for `_.initial` and `_.tail`.
15072   arrayEach(['initial', 'tail'], function(methodName, index) {
15073     var dropName = 'drop' + (index ? '' : 'Right');
15074
15075     LazyWrapper.prototype[methodName] = function() {
15076       return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
15077     };
15078   });
15079
15080   LazyWrapper.prototype.compact = function() {
15081     return this.filter(identity);
15082   };
15083
15084   LazyWrapper.prototype.find = function(predicate) {
15085     return this.filter(predicate).head();
15086   };
15087
15088   LazyWrapper.prototype.findLast = function(predicate) {
15089     return this.reverse().find(predicate);
15090   };
15091
15092   LazyWrapper.prototype.invokeMap = rest(function(path, args) {
15093     if (typeof path == 'function') {
15094       return new LazyWrapper(this);
15095     }
15096     return this.map(function(value) {
15097       return baseInvoke(value, path, args);
15098     });
15099   });
15100
15101   LazyWrapper.prototype.reject = function(predicate) {
15102     predicate = getIteratee(predicate, 3);
15103     return this.filter(function(value) {
15104       return !predicate(value);
15105     });
15106   };
15107
15108   LazyWrapper.prototype.slice = function(start, end) {
15109     start = toInteger(start);
15110
15111     var result = this;
15112     if (result.__filtered__ && (start > 0 || end < 0)) {
15113       return new LazyWrapper(result);
15114     }
15115     if (start < 0) {
15116       result = result.takeRight(-start);
15117     } else if (start) {
15118       result = result.drop(start);
15119     }
15120     if (end !== undefined) {
15121       end = toInteger(end);
15122       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
15123     }
15124     return result;
15125   };
15126
15127   LazyWrapper.prototype.takeRightWhile = function(predicate) {
15128     return this.reverse().takeWhile(predicate).reverse();
15129   };
15130
15131   LazyWrapper.prototype.toArray = function() {
15132     return this.take(MAX_ARRAY_LENGTH);
15133   };
15134
15135   // Add `LazyWrapper` methods to `lodash.prototype`.
15136   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
15137     var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
15138         isTaker = /^(?:head|last)$/.test(methodName),
15139         lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
15140         retUnwrapped = isTaker || /^find/.test(methodName);
15141
15142     if (!lodashFunc) {
15143       return;
15144     }
15145     lodash.prototype[methodName] = function() {
15146       var value = this.__wrapped__,
15147           args = isTaker ? [1] : arguments,
15148           isLazy = value instanceof LazyWrapper,
15149           iteratee = args[0],
15150           useLazy = isLazy || isArray(value);
15151
15152       var interceptor = function(value) {
15153         var result = lodashFunc.apply(lodash, arrayPush([value], args));
15154         return (isTaker && chainAll) ? result[0] : result;
15155       };
15156
15157       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
15158         // Avoid lazy use if the iteratee has a "length" value other than `1`.
15159         isLazy = useLazy = false;
15160       }
15161       var chainAll = this.__chain__,
15162           isHybrid = !!this.__actions__.length,
15163           isUnwrapped = retUnwrapped && !chainAll,
15164           onlyLazy = isLazy && !isHybrid;
15165
15166       if (!retUnwrapped && useLazy) {
15167         value = onlyLazy ? value : new LazyWrapper(this);
15168         var result = func.apply(value, args);
15169         result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
15170         return new LodashWrapper(result, chainAll);
15171       }
15172       if (isUnwrapped && onlyLazy) {
15173         return func.apply(this, args);
15174       }
15175       result = this.thru(interceptor);
15176       return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
15177     };
15178   });
15179
15180   // Add `Array` methods to `lodash.prototype`.
15181   arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
15182     var func = arrayProto[methodName],
15183         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
15184         retUnwrapped = /^(?:pop|shift)$/.test(methodName);
15185
15186     lodash.prototype[methodName] = function() {
15187       var args = arguments;
15188       if (retUnwrapped && !this.__chain__) {
15189         var value = this.value();
15190         return func.apply(isArray(value) ? value : [], args);
15191       }
15192       return this[chainName](function(value) {
15193         return func.apply(isArray(value) ? value : [], args);
15194       });
15195     };
15196   });
15197
15198   // Map minified method names to their real names.
15199   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
15200     var lodashFunc = lodash[methodName];
15201     if (lodashFunc) {
15202       var key = (lodashFunc.name + ''),
15203           names = realNames[key] || (realNames[key] = []);
15204
15205       names.push({ 'name': methodName, 'func': lodashFunc });
15206     }
15207   });
15208
15209   realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{
15210     'name': 'wrapper',
15211     'func': undefined
15212   }];
15213
15214   // Add methods to `LazyWrapper`.
15215   LazyWrapper.prototype.clone = lazyClone;
15216   LazyWrapper.prototype.reverse = lazyReverse;
15217   LazyWrapper.prototype.value = lazyValue;
15218
15219   // Add chain sequence methods to the `lodash` wrapper.
15220   lodash.prototype.chain = wrapperChain;
15221   lodash.prototype.commit = wrapperCommit;
15222   lodash.prototype.next = wrapperNext;
15223   lodash.prototype.plant = wrapperPlant;
15224   lodash.prototype.reverse = wrapperReverse;
15225   lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
15226
15227   if (iteratorSymbol) {
15228     lodash.prototype[iteratorSymbol] = wrapperToIterator;
15229   }
15230
15231   /*--------------------------------------------------------------------------*/
15232
15233   // Expose Lodash on the free variable `window` or `self` when available so it's
15234   // globally accessible, even when bundled with Browserify, Webpack, etc. This
15235   // also prevents errors in cases where Lodash is loaded by a script tag in the
15236   // presence of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch
15237   // for more details. Use `_.noConflict` to remove Lodash from the global object.
15238   (freeWindow || freeSelf || {})._ = lodash;
15239
15240   if (freeExports && freeModule) {
15241     // Export for Node.js.
15242     if (moduleExports) {
15243       (freeModule.exports = lodash)._ = lodash;
15244     }
15245     // Export for CommonJS support.
15246     freeExports._ = lodash;
15247   }
15248   else {
15249     // Export to the global object.
15250     root._ = lodash;
15251   }
15252 }.call(this));
15253 (function(e){if("function"==typeof bootstrap)bootstrap("osmauth",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeOsmAuth=e}else"undefined"!=typeof window?window.osmAuth=e():global.osmAuth=e()})(function(){var define,ses,bootstrap,module,exports;
15254 return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
15255 'use strict';
15256
15257 var ohauth = require('ohauth'),
15258     xtend = require('xtend'),
15259     store = require('store');
15260
15261 // # osm-auth
15262 //
15263 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
15264 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
15265 // does not support custom headers, which this uses everywhere.
15266 module.exports = function(o) {
15267
15268     var oauth = {};
15269
15270     // authenticated users will also have a request token secret, but it's
15271     // not used in transactions with the server
15272     oauth.authenticated = function() {
15273         return !!(token('oauth_token') && token('oauth_token_secret'));
15274     };
15275
15276     oauth.logout = function() {
15277         token('oauth_token', '');
15278         token('oauth_token_secret', '');
15279         token('oauth_request_token_secret', '');
15280         return oauth;
15281     };
15282
15283     // TODO: detect lack of click event
15284     oauth.authenticate = function(callback) {
15285         if (oauth.authenticated()) return callback();
15286
15287         oauth.logout();
15288
15289         // ## Getting a request token
15290         var params = timenonce(getAuth(o)),
15291             url = o.url + '/oauth/request_token',
15292             timer;
15293
15294         params.oauth_signature = ohauth.signature(
15295             o.oauth_secret, '',
15296             ohauth.baseString('POST', url, params));
15297
15298         if (!o.singlepage) {
15299             // Create a 600x550 popup window in the center of the screen
15300             var w = 600, h = 550,
15301                 settings = [
15302                     ['width', w], ['height', h],
15303                     ['left', screen.width / 2 - w / 2],
15304                     ['top', screen.height / 2 - h / 2]].map(function(x) {
15305                         return x.join('=');
15306                     }).join(','),
15307                 popup = window.open('about:blank', 'oauth_window', settings);
15308
15309
15310             timer = setInterval(function() {
15311                 if (popup.closed) {
15312                     o.done();
15313                     clearInterval(timer);
15314                     callback('not authenticated', null);
15315                 }
15316             }, 100);
15317         }
15318
15319         // Request a request token. When this is complete, the popup
15320         // window is redirected to OSM's authorization page.
15321         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
15322         o.loading();
15323
15324         function reqTokenDone(err, xhr) {
15325             o.done();
15326             if (err) {
15327                 if (timer) clearInterval(timer);
15328                 return callback(err);
15329             }
15330             var resp = ohauth.stringQs(xhr.response);
15331             token('oauth_request_token_secret', resp.oauth_token_secret);
15332             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
15333                 oauth_token: resp.oauth_token,
15334                 oauth_callback: location.href.replace('index.html', '')
15335                     .replace(/#.*/, '') + o.landing
15336             });
15337
15338             if (o.singlepage) {
15339                 location.href = authorize_url;
15340             } else {
15341                 popup.location = authorize_url;
15342             }
15343         }
15344
15345         // Called by a function in a landing page, in the popup window. The
15346         // window closes itself.
15347         window.authComplete = function(token) {
15348             if (timer) clearInterval(timer);
15349             var oauth_token = ohauth.stringQs(token.split('?')[1]);
15350             get_access_token(oauth_token.oauth_token);
15351             delete window.authComplete;
15352         };
15353
15354         // ## Getting an request token
15355         //
15356         // At this point we have an `oauth_token`, brought in from a function
15357         // call on a landing page popup.
15358         function get_access_token(oauth_token) {
15359             var url = o.url + '/oauth/access_token',
15360                 params = timenonce(getAuth(o)),
15361                 request_token_secret = token('oauth_request_token_secret');
15362             params.oauth_token = oauth_token;
15363             params.oauth_signature = ohauth.signature(
15364                 o.oauth_secret,
15365                 request_token_secret,
15366                 ohauth.baseString('POST', url, params));
15367
15368             // ## Getting an access token
15369             //
15370             // The final token required for authentication. At this point
15371             // we have a `request token secret`
15372             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
15373             o.loading();
15374         }
15375
15376         function accessTokenDone(err, xhr) {
15377             o.done();
15378             if (timer) clearInterval(timer);
15379             if (err) return callback(err);
15380             var access_token = ohauth.stringQs(xhr.response);
15381             token('oauth_token', access_token.oauth_token);
15382             token('oauth_token_secret', access_token.oauth_token_secret);
15383             callback(null, oauth);
15384         }
15385     };
15386
15387     oauth.bootstrapToken = function(oauth_token, callback) {
15388         // ## Getting an request token
15389         // At this point we have an `oauth_token`, brought in from a function
15390         // call on a landing page popup.
15391         function get_access_token(oauth_token) {
15392             var url = o.url + '/oauth/access_token',
15393                 params = timenonce(getAuth(o)),
15394                 request_token_secret = token('oauth_request_token_secret');
15395             params.oauth_token = oauth_token;
15396             params.oauth_signature = ohauth.signature(
15397                 o.oauth_secret,
15398                 request_token_secret,
15399                 ohauth.baseString('POST', url, params));
15400
15401             // ## Getting an access token
15402             // The final token required for authentication. At this point
15403             // we have a `request token secret`
15404             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
15405             o.loading();
15406         }
15407
15408         function accessTokenDone(err, xhr) {
15409             o.done();
15410             if (err) return callback(err);
15411             var access_token = ohauth.stringQs(xhr.response);
15412             token('oauth_token', access_token.oauth_token);
15413             token('oauth_token_secret', access_token.oauth_token_secret);
15414             callback(null, oauth);
15415         }
15416
15417         get_access_token(oauth_token);
15418     };
15419
15420     // # xhr
15421     //
15422     // A single XMLHttpRequest wrapper that does authenticated calls if the
15423     // user has logged in.
15424     oauth.xhr = function(options, callback) {
15425         if (!oauth.authenticated()) {
15426             if (o.auto) return oauth.authenticate(run);
15427             else return callback('not authenticated', null);
15428         } else return run();
15429
15430         function run() {
15431             var params = timenonce(getAuth(o)),
15432                 url = o.url + options.path,
15433                 oauth_token_secret = token('oauth_token_secret');
15434
15435             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
15436             if ((!options.options || !options.options.header ||
15437                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
15438                 options.content) {
15439                 params = xtend(params, ohauth.stringQs(options.content));
15440             }
15441
15442             params.oauth_token = token('oauth_token');
15443             params.oauth_signature = ohauth.signature(
15444                 o.oauth_secret,
15445                 oauth_token_secret,
15446                 ohauth.baseString(options.method, url, params));
15447
15448             ohauth.xhr(options.method,
15449                 url, params, options.content, options.options, done);
15450         }
15451
15452         function done(err, xhr) {
15453             if (err) return callback(err);
15454             else if (xhr.responseXML) return callback(err, xhr.responseXML);
15455             else return callback(err, xhr.response);
15456         }
15457     };
15458
15459     // pre-authorize this object, if we can just get a token and token_secret
15460     // from the start
15461     oauth.preauth = function(c) {
15462         if (!c) return;
15463         if (c.oauth_token) token('oauth_token', c.oauth_token);
15464         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
15465         return oauth;
15466     };
15467
15468     oauth.options = function(_) {
15469         if (!arguments.length) return o;
15470
15471         o = _;
15472
15473         o.url = o.url || 'http://www.openstreetmap.org';
15474         o.landing = o.landing || 'land.html';
15475
15476         o.singlepage = o.singlepage || false;
15477
15478         // Optional loading and loading-done functions for nice UI feedback.
15479         // by default, no-ops
15480         o.loading = o.loading || function() {};
15481         o.done = o.done || function() {};
15482
15483         return oauth.preauth(o);
15484     };
15485
15486     // 'stamp' an authentication object from `getAuth()`
15487     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
15488     // and timestamp
15489     function timenonce(o) {
15490         o.oauth_timestamp = ohauth.timestamp();
15491         o.oauth_nonce = ohauth.nonce();
15492         return o;
15493     }
15494
15495     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
15496     // can be used with multiple APIs and the keys in `localStorage`
15497     // will not clash
15498     var token;
15499
15500     if (store.enabled) {
15501         token = function (x, y) {
15502             if (arguments.length === 1) return store.get(o.url + x);
15503             else if (arguments.length === 2) return store.set(o.url + x, y);
15504         };
15505     } else {
15506         var storage = {};
15507         token = function (x, y) {
15508             if (arguments.length === 1) return storage[o.url + x];
15509             else if (arguments.length === 2) return storage[o.url + x] = y;
15510         };
15511     }
15512
15513     // Get an authentication object. If you just add and remove properties
15514     // from a single object, you'll need to use `delete` to make sure that
15515     // it doesn't contain undesired properties for authentication
15516     function getAuth(o) {
15517         return {
15518             oauth_consumer_key: o.oauth_consumer_key,
15519             oauth_signature_method: "HMAC-SHA1"
15520         };
15521     }
15522
15523     // potentially pre-authorize
15524     oauth.options(o);
15525
15526     return oauth;
15527 };
15528
15529 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
15530 (function(global){;(function(win){
15531         var store = {},
15532                 doc = win.document,
15533                 localStorageName = 'localStorage',
15534                 storage
15535
15536         store.disabled = false
15537         store.set = function(key, value) {}
15538         store.get = function(key) {}
15539         store.remove = function(key) {}
15540         store.clear = function() {}
15541         store.transact = function(key, defaultVal, transactionFn) {
15542                 var val = store.get(key)
15543                 if (transactionFn == null) {
15544                         transactionFn = defaultVal
15545                         defaultVal = null
15546                 }
15547                 if (typeof val == 'undefined') { val = defaultVal || {} }
15548                 transactionFn(val)
15549                 store.set(key, val)
15550         }
15551         store.getAll = function() {}
15552         store.forEach = function() {}
15553
15554         store.serialize = function(value) {
15555                 return JSON.stringify(value)
15556         }
15557         store.deserialize = function(value) {
15558                 if (typeof value != 'string') { return undefined }
15559                 try { return JSON.parse(value) }
15560                 catch(e) { return value || undefined }
15561         }
15562
15563         // Functions to encapsulate questionable FireFox 3.6.13 behavior
15564         // when about.config::dom.storage.enabled === false
15565         // See https://github.com/marcuswestin/store.js/issues#issue/13
15566         function isLocalStorageNameSupported() {
15567                 try { return (localStorageName in win && win[localStorageName]) }
15568                 catch(err) { return false }
15569         }
15570
15571         if (isLocalStorageNameSupported()) {
15572                 storage = win[localStorageName]
15573                 store.set = function(key, val) {
15574                         if (val === undefined) { return store.remove(key) }
15575                         storage.setItem(key, store.serialize(val))
15576                         return val
15577                 }
15578                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
15579                 store.remove = function(key) { storage.removeItem(key) }
15580                 store.clear = function() { storage.clear() }
15581                 store.getAll = function() {
15582                         var ret = {}
15583                         store.forEach(function(key, val) {
15584                                 ret[key] = val
15585                         })
15586                         return ret
15587                 }
15588                 store.forEach = function(callback) {
15589                         for (var i=0; i<storage.length; i++) {
15590                                 var key = storage.key(i)
15591                                 callback(key, store.get(key))
15592                         }
15593                 }
15594         } else if (doc.documentElement.addBehavior) {
15595                 var storageOwner,
15596                         storageContainer
15597                 // Since #userData storage applies only to specific paths, we need to
15598                 // somehow link our data to a specific path.  We choose /favicon.ico
15599                 // as a pretty safe option, since all browsers already make a request to
15600                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
15601                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
15602                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
15603                 // since the iframe access rules appear to allow direct access and
15604                 // manipulation of the document element, even for a 404 page.  This
15605                 // document can be used instead of the current document (which would
15606                 // have been limited to the current path) to perform #userData storage.
15607                 try {
15608                         storageContainer = new ActiveXObject('htmlfile')
15609                         storageContainer.open()
15610                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
15611                         storageContainer.close()
15612                         storageOwner = storageContainer.w.frames[0].document
15613                         storage = storageOwner.createElement('div')
15614                 } catch(e) {
15615                         // somehow ActiveXObject instantiation failed (perhaps some special
15616                         // security settings or otherwse), fall back to per-path storage
15617                         storage = doc.createElement('div')
15618                         storageOwner = doc.body
15619                 }
15620                 function withIEStorage(storeFunction) {
15621                         return function() {
15622                                 var args = Array.prototype.slice.call(arguments, 0)
15623                                 args.unshift(storage)
15624                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
15625                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
15626                                 storageOwner.appendChild(storage)
15627                                 storage.addBehavior('#default#userData')
15628                                 storage.load(localStorageName)
15629                                 var result = storeFunction.apply(store, args)
15630                                 storageOwner.removeChild(storage)
15631                                 return result
15632                         }
15633                 }
15634
15635                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
15636                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
15637                 function ieKeyFix(key) {
15638                         return key.replace(forbiddenCharsRegex, '___')
15639                 }
15640                 store.set = withIEStorage(function(storage, key, val) {
15641                         key = ieKeyFix(key)
15642                         if (val === undefined) { return store.remove(key) }
15643                         storage.setAttribute(key, store.serialize(val))
15644                         storage.save(localStorageName)
15645                         return val
15646                 })
15647                 store.get = withIEStorage(function(storage, key) {
15648                         key = ieKeyFix(key)
15649                         return store.deserialize(storage.getAttribute(key))
15650                 })
15651                 store.remove = withIEStorage(function(storage, key) {
15652                         key = ieKeyFix(key)
15653                         storage.removeAttribute(key)
15654                         storage.save(localStorageName)
15655                 })
15656                 store.clear = withIEStorage(function(storage) {
15657                         var attributes = storage.XMLDocument.documentElement.attributes
15658                         storage.load(localStorageName)
15659                         for (var i=0, attr; attr=attributes[i]; i++) {
15660                                 storage.removeAttribute(attr.name)
15661                         }
15662                         storage.save(localStorageName)
15663                 })
15664                 store.getAll = function(storage) {
15665                         var ret = {}
15666                         store.forEach(function(key, val) {
15667                                 ret[key] = val
15668                         })
15669                         return ret
15670                 }
15671                 store.forEach = withIEStorage(function(storage, callback) {
15672                         var attributes = storage.XMLDocument.documentElement.attributes
15673                         for (var i=0, attr; attr=attributes[i]; ++i) {
15674                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
15675                         }
15676                 })
15677         }
15678
15679         try {
15680                 var testKey = '__storejs__'
15681                 store.set(testKey, testKey)
15682                 if (store.get(testKey) != testKey) { store.disabled = true }
15683                 store.remove(testKey)
15684         } catch(e) {
15685                 store.disabled = true
15686         }
15687         store.enabled = !store.disabled
15688
15689         if (typeof module != 'undefined' && module.exports) { module.exports = store }
15690         else if (typeof define === 'function' && define.amd) { define(store) }
15691         else { win.store = store }
15692
15693 })(this.window || global);
15694
15695 })(window)
15696 },{}],5:[function(require,module,exports){
15697 module.exports = hasKeys
15698
15699 function hasKeys(source) {
15700     return source !== null &&
15701         (typeof source === "object" ||
15702         typeof source === "function")
15703 }
15704
15705 },{}],4:[function(require,module,exports){
15706 var Keys = require("object-keys")
15707 var hasKeys = require("./has-keys")
15708
15709 module.exports = extend
15710
15711 function extend() {
15712     var target = {}
15713
15714     for (var i = 0; i < arguments.length; i++) {
15715         var source = arguments[i]
15716
15717         if (!hasKeys(source)) {
15718             continue
15719         }
15720
15721         var keys = Keys(source)
15722
15723         for (var j = 0; j < keys.length; j++) {
15724             var name = keys[j]
15725             target[name] = source[name]
15726         }
15727     }
15728
15729     return target
15730 }
15731
15732 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
15733 (function(global){/**
15734  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
15735  *
15736  * @class Hashes
15737  * @author Tomas Aparicio <tomas@rijndael-project.com>
15738  * @license New BSD (see LICENSE file)
15739  * @version 1.0.4
15740  *
15741  * Algorithms specification:
15742  *
15743  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
15744  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
15745  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15746  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15747  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15748  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
15749  *
15750  */
15751 (function(){
15752   var Hashes;
15753
15754   // private helper methods
15755   function utf8Encode(str) {
15756     var  x, y, output = '', i = -1, l;
15757
15758     if (str && str.length) {
15759       l = str.length;
15760       while ((i+=1) < l) {
15761         /* Decode utf-16 surrogate pairs */
15762         x = str.charCodeAt(i);
15763         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
15764         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
15765             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
15766             i += 1;
15767         }
15768         /* Encode output as utf-8 */
15769         if (x <= 0x7F) {
15770             output += String.fromCharCode(x);
15771         } else if (x <= 0x7FF) {
15772             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
15773                         0x80 | ( x & 0x3F));
15774         } else if (x <= 0xFFFF) {
15775             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
15776                         0x80 | ((x >>> 6 ) & 0x3F),
15777                         0x80 | ( x & 0x3F));
15778         } else if (x <= 0x1FFFFF) {
15779             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
15780                         0x80 | ((x >>> 12) & 0x3F),
15781                         0x80 | ((x >>> 6 ) & 0x3F),
15782                         0x80 | ( x & 0x3F));
15783         }
15784       }
15785     }
15786     return output;
15787   }
15788
15789   function utf8Decode(str) {
15790     var i, ac, c1, c2, c3, arr = [], l;
15791     i = ac = c1 = c2 = c3 = 0;
15792
15793     if (str && str.length) {
15794       l = str.length;
15795       str += '';
15796
15797       while (i < l) {
15798           c1 = str.charCodeAt(i);
15799           ac += 1;
15800           if (c1 < 128) {
15801               arr[ac] = String.fromCharCode(c1);
15802               i+=1;
15803           } else if (c1 > 191 && c1 < 224) {
15804               c2 = str.charCodeAt(i + 1);
15805               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
15806               i += 2;
15807           } else {
15808               c2 = str.charCodeAt(i + 1);
15809               c3 = str.charCodeAt(i + 2);
15810               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
15811               i += 3;
15812           }
15813       }
15814     }
15815     return arr.join('');
15816   }
15817
15818   /**
15819    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
15820    * to work around bugs in some JS interpreters.
15821    */
15822   function safe_add(x, y) {
15823     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
15824         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
15825     return (msw << 16) | (lsw & 0xFFFF);
15826   }
15827
15828   /**
15829    * Bitwise rotate a 32-bit number to the left.
15830    */
15831   function bit_rol(num, cnt) {
15832     return (num << cnt) | (num >>> (32 - cnt));
15833   }
15834
15835   /**
15836    * Convert a raw string to a hex string
15837    */
15838   function rstr2hex(input, hexcase) {
15839     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
15840         output = '', x, i = 0, l = input.length;
15841     for (; i < l; i+=1) {
15842       x = input.charCodeAt(i);
15843       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
15844     }
15845     return output;
15846   }
15847
15848   /**
15849    * Encode a string as utf-16
15850    */
15851   function str2rstr_utf16le(input) {
15852     var i, l = input.length, output = '';
15853     for (i = 0; i < l; i+=1) {
15854       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
15855     }
15856     return output;
15857   }
15858
15859   function str2rstr_utf16be(input) {
15860     var i, l = input.length, output = '';
15861     for (i = 0; i < l; i+=1) {
15862       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
15863     }
15864     return output;
15865   }
15866
15867   /**
15868    * Convert an array of big-endian words to a string
15869    */
15870   function binb2rstr(input) {
15871     var i, l = input.length * 32, output = '';
15872     for (i = 0; i < l; i += 8) {
15873         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
15874     }
15875     return output;
15876   }
15877
15878   /**
15879    * Convert an array of little-endian words to a string
15880    */
15881   function binl2rstr(input) {
15882     var i, l = input.length * 32, output = '';
15883     for (i = 0;i < l; i += 8) {
15884       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15885     }
15886     return output;
15887   }
15888
15889   /**
15890    * Convert a raw string to an array of little-endian words
15891    * Characters >255 have their high-byte silently ignored.
15892    */
15893   function rstr2binl(input) {
15894     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
15895     for (i = 0; i < lo; i+=1) {
15896       output[i] = 0;
15897     }
15898     for (i = 0; i < l; i += 8) {
15899       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
15900     }
15901     return output;
15902   }
15903
15904   /**
15905    * Convert a raw string to an array of big-endian words
15906    * Characters >255 have their high-byte silently ignored.
15907    */
15908    function rstr2binb(input) {
15909       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
15910       for (i = 0; i < lo; i+=1) {
15911             output[i] = 0;
15912         }
15913       for (i = 0; i < l; i += 8) {
15914             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
15915         }
15916       return output;
15917    }
15918
15919   /**
15920    * Convert a raw string to an arbitrary string encoding
15921    */
15922   function rstr2any(input, encoding) {
15923     var divisor = encoding.length,
15924         remainders = Array(),
15925         i, q, x, ld, quotient, dividend, output, full_length;
15926
15927     /* Convert to an array of 16-bit big-endian values, forming the dividend */
15928     dividend = Array(Math.ceil(input.length / 2));
15929     ld = dividend.length;
15930     for (i = 0; i < ld; i+=1) {
15931       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
15932     }
15933
15934     /**
15935      * Repeatedly perform a long division. The binary array forms the dividend,
15936      * the length of the encoding is the divisor. Once computed, the quotient
15937      * forms the dividend for the next step. We stop when the dividend is zerHashes.
15938      * All remainders are stored for later use.
15939      */
15940     while(dividend.length > 0) {
15941       quotient = Array();
15942       x = 0;
15943       for (i = 0; i < dividend.length; i+=1) {
15944         x = (x << 16) + dividend[i];
15945         q = Math.floor(x / divisor);
15946         x -= q * divisor;
15947         if (quotient.length > 0 || q > 0) {
15948           quotient[quotient.length] = q;
15949         }
15950       }
15951       remainders[remainders.length] = x;
15952       dividend = quotient;
15953     }
15954
15955     /* Convert the remainders to the output string */
15956     output = '';
15957     for (i = remainders.length - 1; i >= 0; i--) {
15958       output += encoding.charAt(remainders[i]);
15959     }
15960
15961     /* Append leading zero equivalents */
15962     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
15963     for (i = output.length; i < full_length; i+=1) {
15964       output = encoding[0] + output;
15965     }
15966     return output;
15967   }
15968
15969   /**
15970    * Convert a raw string to a base-64 string
15971    */
15972   function rstr2b64(input, b64pad) {
15973     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
15974         output = '',
15975         len = input.length, i, j, triplet;
15976     b64pad= b64pad || '=';
15977     for (i = 0; i < len; i += 3) {
15978       triplet = (input.charCodeAt(i) << 16)
15979             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
15980             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
15981       for (j = 0; j < 4; j+=1) {
15982         if (i * 8 + j * 6 > input.length * 8) {
15983           output += b64pad;
15984         } else {
15985           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
15986         }
15987        }
15988     }
15989     return output;
15990   }
15991
15992   Hashes = {
15993   /**
15994    * @property {String} version
15995    * @readonly
15996    */
15997   VERSION : '1.0.3',
15998   /**
15999    * @member Hashes
16000    * @class Base64
16001    * @constructor
16002    */
16003   Base64 : function () {
16004     // private properties
16005     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
16006         pad = '=', // default pad according with the RFC standard
16007         url = false, // URL encoding support @todo
16008         utf8 = true; // by default enable UTF-8 support encoding
16009
16010     // public method for encoding
16011     this.encode = function (input) {
16012       var i, j, triplet,
16013           output = '',
16014           len = input.length;
16015
16016       pad = pad || '=';
16017       input = (utf8) ? utf8Encode(input) : input;
16018
16019       for (i = 0; i < len; i += 3) {
16020         triplet = (input.charCodeAt(i) << 16)
16021               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
16022               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
16023         for (j = 0; j < 4; j+=1) {
16024           if (i * 8 + j * 6 > len * 8) {
16025               output += pad;
16026           } else {
16027               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
16028           }
16029         }
16030       }
16031       return output;
16032     };
16033
16034     // public method for decoding
16035     this.decode = function (input) {
16036       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
16037       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
16038         dec = '',
16039         arr = [];
16040       if (!input) { return input; }
16041
16042       i = ac = 0;
16043       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
16044       //input += '';
16045
16046       do { // unpack four hexets into three octets using index points in b64
16047         h1 = tab.indexOf(input.charAt(i+=1));
16048         h2 = tab.indexOf(input.charAt(i+=1));
16049         h3 = tab.indexOf(input.charAt(i+=1));
16050         h4 = tab.indexOf(input.charAt(i+=1));
16051
16052         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
16053
16054         o1 = bits >> 16 & 0xff;
16055         o2 = bits >> 8 & 0xff;
16056         o3 = bits & 0xff;
16057         ac += 1;
16058
16059         if (h3 === 64) {
16060           arr[ac] = String.fromCharCode(o1);
16061         } else if (h4 === 64) {
16062           arr[ac] = String.fromCharCode(o1, o2);
16063         } else {
16064           arr[ac] = String.fromCharCode(o1, o2, o3);
16065         }
16066       } while (i < input.length);
16067
16068       dec = arr.join('');
16069       dec = (utf8) ? utf8Decode(dec) : dec;
16070
16071       return dec;
16072     };
16073
16074     // set custom pad string
16075     this.setPad = function (str) {
16076         pad = str || pad;
16077         return this;
16078     };
16079     // set custom tab string characters
16080     this.setTab = function (str) {
16081         tab = str || tab;
16082         return this;
16083     };
16084     this.setUTF8 = function (bool) {
16085         if (typeof bool === 'boolean') {
16086           utf8 = bool;
16087         }
16088         return this;
16089     };
16090   },
16091
16092   /**
16093    * CRC-32 calculation
16094    * @member Hashes
16095    * @method CRC32
16096    * @static
16097    * @param {String} str Input String
16098    * @return {String}
16099    */
16100   CRC32 : function (str) {
16101     var crc = 0, x = 0, y = 0, table, i, iTop;
16102     str = utf8Encode(str);
16103
16104     table = [
16105         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
16106         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
16107         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
16108         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
16109         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
16110         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
16111         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
16112         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
16113         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
16114         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
16115         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
16116         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
16117         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
16118         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
16119         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
16120         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
16121         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
16122         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
16123         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
16124         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
16125         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
16126         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
16127         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
16128         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
16129         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
16130         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
16131     ].join('');
16132
16133     crc = crc ^ (-1);
16134     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
16135         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
16136         x = '0x' + table.substr( y * 9, 8 );
16137         crc = ( crc >>> 8 ) ^ x;
16138     }
16139     // always return a positive number (that's what >>> 0 does)
16140     return (crc ^ (-1)) >>> 0;
16141   },
16142   /**
16143    * @member Hashes
16144    * @class MD5
16145    * @constructor
16146    * @param {Object} [config]
16147    *
16148    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
16149    * Digest Algorithm, as defined in RFC 1321.
16150    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
16151    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16152    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
16153    */
16154   MD5 : function (options) {
16155     /**
16156      * Private config properties. You may need to tweak these to be compatible with
16157      * the server-side, but the defaults work in most cases.
16158      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
16159      */
16160     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
16161         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
16162         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
16163
16164     // privileged (public) methods
16165     this.hex = function (s) {
16166       return rstr2hex(rstr(s, utf8), hexcase);
16167     };
16168     this.b64 = function (s) {
16169       return rstr2b64(rstr(s), b64pad);
16170     };
16171     this.any = function(s, e) {
16172       return rstr2any(rstr(s, utf8), e);
16173     };
16174     this.hex_hmac = function (k, d) {
16175       return rstr2hex(rstr_hmac(k, d), hexcase);
16176     };
16177     this.b64_hmac = function (k, d) {
16178       return rstr2b64(rstr_hmac(k,d), b64pad);
16179     };
16180     this.any_hmac = function (k, d, e) {
16181       return rstr2any(rstr_hmac(k, d), e);
16182     };
16183     /**
16184      * Perform a simple self-test to see if the VM is working
16185      * @return {String} Hexadecimal hash sample
16186      */
16187     this.vm_test = function () {
16188       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16189     };
16190     /**
16191      * Enable/disable uppercase hexadecimal returned string
16192      * @param {Boolean}
16193      * @return {Object} this
16194      */
16195     this.setUpperCase = function (a) {
16196       if (typeof a === 'boolean' ) {
16197         hexcase = a;
16198       }
16199       return this;
16200     };
16201     /**
16202      * Defines a base64 pad string
16203      * @param {String} Pad
16204      * @return {Object} this
16205      */
16206     this.setPad = function (a) {
16207       b64pad = a || b64pad;
16208       return this;
16209     };
16210     /**
16211      * Defines a base64 pad string
16212      * @param {Boolean}
16213      * @return {Object} [this]
16214      */
16215     this.setUTF8 = function (a) {
16216       if (typeof a === 'boolean') {
16217         utf8 = a;
16218       }
16219       return this;
16220     };
16221
16222     // private methods
16223
16224     /**
16225      * Calculate the MD5 of a raw string
16226      */
16227     function rstr(s) {
16228       s = (utf8) ? utf8Encode(s): s;
16229       return binl2rstr(binl(rstr2binl(s), s.length * 8));
16230     }
16231
16232     /**
16233      * Calculate the HMAC-MD5, of a key and some data (raw strings)
16234      */
16235     function rstr_hmac(key, data) {
16236       var bkey, ipad, opad, hash, i;
16237
16238       key = (utf8) ? utf8Encode(key) : key;
16239       data = (utf8) ? utf8Encode(data) : data;
16240       bkey = rstr2binl(key);
16241       if (bkey.length > 16) {
16242         bkey = binl(bkey, key.length * 8);
16243       }
16244
16245       ipad = Array(16), opad = Array(16);
16246       for (i = 0; i < 16; i+=1) {
16247           ipad[i] = bkey[i] ^ 0x36363636;
16248           opad[i] = bkey[i] ^ 0x5C5C5C5C;
16249       }
16250       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
16251       return binl2rstr(binl(opad.concat(hash), 512 + 128));
16252     }
16253
16254     /**
16255      * Calculate the MD5 of an array of little-endian words, and a bit length.
16256      */
16257     function binl(x, len) {
16258       var i, olda, oldb, oldc, oldd,
16259           a =  1732584193,
16260           b = -271733879,
16261           c = -1732584194,
16262           d =  271733878;
16263
16264       /* append padding */
16265       x[len >> 5] |= 0x80 << ((len) % 32);
16266       x[(((len + 64) >>> 9) << 4) + 14] = len;
16267
16268       for (i = 0; i < x.length; i += 16) {
16269         olda = a;
16270         oldb = b;
16271         oldc = c;
16272         oldd = d;
16273
16274         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
16275         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
16276         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
16277         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
16278         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
16279         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
16280         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
16281         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
16282         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
16283         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
16284         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
16285         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
16286         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
16287         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
16288         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
16289         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
16290
16291         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
16292         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
16293         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
16294         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
16295         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
16296         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
16297         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
16298         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
16299         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
16300         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
16301         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
16302         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
16303         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
16304         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
16305         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
16306         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
16307
16308         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
16309         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
16310         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
16311         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
16312         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
16313         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
16314         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
16315         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
16316         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
16317         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
16318         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
16319         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
16320         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
16321         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
16322         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
16323         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
16324
16325         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
16326         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
16327         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
16328         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
16329         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
16330         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
16331         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
16332         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
16333         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
16334         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
16335         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
16336         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
16337         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
16338         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
16339         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
16340         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
16341
16342         a = safe_add(a, olda);
16343         b = safe_add(b, oldb);
16344         c = safe_add(c, oldc);
16345         d = safe_add(d, oldd);
16346       }
16347       return Array(a, b, c, d);
16348     }
16349
16350     /**
16351      * These functions implement the four basic operations the algorithm uses.
16352      */
16353     function md5_cmn(q, a, b, x, s, t) {
16354       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
16355     }
16356     function md5_ff(a, b, c, d, x, s, t) {
16357       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
16358     }
16359     function md5_gg(a, b, c, d, x, s, t) {
16360       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
16361     }
16362     function md5_hh(a, b, c, d, x, s, t) {
16363       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
16364     }
16365     function md5_ii(a, b, c, d, x, s, t) {
16366       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
16367     }
16368   },
16369   /**
16370    * @member Hashes
16371    * @class Hashes.SHA1
16372    * @param {Object} [config]
16373    * @constructor
16374    *
16375    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
16376    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
16377    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16378    * See http://pajhome.org.uk/crypt/md5 for details.
16379    */
16380   SHA1 : function (options) {
16381    /**
16382      * Private config properties. You may need to tweak these to be compatible with
16383      * the server-side, but the defaults work in most cases.
16384      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
16385      */
16386     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
16387         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
16388         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
16389
16390     // public methods
16391     this.hex = function (s) {
16392         return rstr2hex(rstr(s, utf8), hexcase);
16393     };
16394     this.b64 = function (s) {
16395         return rstr2b64(rstr(s, utf8), b64pad);
16396     };
16397     this.any = function (s, e) {
16398         return rstr2any(rstr(s, utf8), e);
16399     };
16400     this.hex_hmac = function (k, d) {
16401         return rstr2hex(rstr_hmac(k, d));
16402     };
16403     this.b64_hmac = function (k, d) {
16404         return rstr2b64(rstr_hmac(k, d), b64pad);
16405     };
16406     this.any_hmac = function (k, d, e) {
16407         return rstr2any(rstr_hmac(k, d), e);
16408     };
16409     /**
16410      * Perform a simple self-test to see if the VM is working
16411      * @return {String} Hexadecimal hash sample
16412      * @public
16413      */
16414     this.vm_test = function () {
16415       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16416     };
16417     /**
16418      * @description Enable/disable uppercase hexadecimal returned string
16419      * @param {boolean}
16420      * @return {Object} this
16421      * @public
16422      */
16423     this.setUpperCase = function (a) {
16424         if (typeof a === 'boolean') {
16425         hexcase = a;
16426       }
16427         return this;
16428     };
16429     /**
16430      * @description Defines a base64 pad string
16431      * @param {string} Pad
16432      * @return {Object} this
16433      * @public
16434      */
16435     this.setPad = function (a) {
16436       b64pad = a || b64pad;
16437         return this;
16438     };
16439     /**
16440      * @description Defines a base64 pad string
16441      * @param {boolean}
16442      * @return {Object} this
16443      * @public
16444      */
16445     this.setUTF8 = function (a) {
16446         if (typeof a === 'boolean') {
16447         utf8 = a;
16448       }
16449         return this;
16450     };
16451
16452     // private methods
16453
16454     /**
16455          * Calculate the SHA-512 of a raw string
16456          */
16457         function rstr(s) {
16458       s = (utf8) ? utf8Encode(s) : s;
16459       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16460         }
16461
16462     /**
16463      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
16464      */
16465     function rstr_hmac(key, data) {
16466         var bkey, ipad, opad, i, hash;
16467         key = (utf8) ? utf8Encode(key) : key;
16468         data = (utf8) ? utf8Encode(data) : data;
16469         bkey = rstr2binb(key);
16470
16471         if (bkey.length > 16) {
16472         bkey = binb(bkey, key.length * 8);
16473       }
16474         ipad = Array(16), opad = Array(16);
16475         for (i = 0; i < 16; i+=1) {
16476                 ipad[i] = bkey[i] ^ 0x36363636;
16477                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
16478         }
16479         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
16480         return binb2rstr(binb(opad.concat(hash), 512 + 160));
16481     }
16482
16483     /**
16484      * Calculate the SHA-1 of an array of big-endian words, and a bit length
16485      */
16486     function binb(x, len) {
16487       var i, j, t, olda, oldb, oldc, oldd, olde,
16488           w = Array(80),
16489           a =  1732584193,
16490           b = -271733879,
16491           c = -1732584194,
16492           d =  271733878,
16493           e = -1009589776;
16494
16495       /* append padding */
16496       x[len >> 5] |= 0x80 << (24 - len % 32);
16497       x[((len + 64 >> 9) << 4) + 15] = len;
16498
16499       for (i = 0; i < x.length; i += 16) {
16500         olda = a,
16501         oldb = b;
16502         oldc = c;
16503         oldd = d;
16504         olde = e;
16505
16506         for (j = 0; j < 80; j+=1)       {
16507           if (j < 16) {
16508             w[j] = x[i + j];
16509           } else {
16510             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
16511           }
16512           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
16513                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
16514           e = d;
16515           d = c;
16516           c = bit_rol(b, 30);
16517           b = a;
16518           a = t;
16519         }
16520
16521         a = safe_add(a, olda);
16522         b = safe_add(b, oldb);
16523         c = safe_add(c, oldc);
16524         d = safe_add(d, oldd);
16525         e = safe_add(e, olde);
16526       }
16527       return Array(a, b, c, d, e);
16528     }
16529
16530     /**
16531      * Perform the appropriate triplet combination function for the current
16532      * iteration
16533      */
16534     function sha1_ft(t, b, c, d) {
16535       if (t < 20) { return (b & c) | ((~b) & d); }
16536       if (t < 40) { return b ^ c ^ d; }
16537       if (t < 60) { return (b & c) | (b & d) | (c & d); }
16538       return b ^ c ^ d;
16539     }
16540
16541     /**
16542      * Determine the appropriate additive constant for the current iteration
16543      */
16544     function sha1_kt(t) {
16545       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
16546                  (t < 60) ? -1894007588 : -899497514;
16547     }
16548   },
16549   /**
16550    * @class Hashes.SHA256
16551    * @param {config}
16552    *
16553    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
16554    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
16555    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16556    * See http://pajhome.org.uk/crypt/md5 for details.
16557    * Also http://anmar.eu.org/projects/jssha2/
16558    */
16559   SHA256 : function (options) {
16560     /**
16561      * Private properties configuration variables. You may need to tweak these to be compatible with
16562      * the server-side, but the defaults work in most cases.
16563      * @see this.setUpperCase() method
16564      * @see this.setPad() method
16565      */
16566     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
16567               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
16568               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
16569               sha256_K;
16570
16571     /* privileged (public) methods */
16572     this.hex = function (s) {
16573       return rstr2hex(rstr(s, utf8));
16574     };
16575     this.b64 = function (s) {
16576       return rstr2b64(rstr(s, utf8), b64pad);
16577     };
16578     this.any = function (s, e) {
16579       return rstr2any(rstr(s, utf8), e);
16580     };
16581     this.hex_hmac = function (k, d) {
16582       return rstr2hex(rstr_hmac(k, d));
16583     };
16584     this.b64_hmac = function (k, d) {
16585       return rstr2b64(rstr_hmac(k, d), b64pad);
16586     };
16587     this.any_hmac = function (k, d, e) {
16588       return rstr2any(rstr_hmac(k, d), e);
16589     };
16590     /**
16591      * Perform a simple self-test to see if the VM is working
16592      * @return {String} Hexadecimal hash sample
16593      * @public
16594      */
16595     this.vm_test = function () {
16596       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16597     };
16598     /**
16599      * Enable/disable uppercase hexadecimal returned string
16600      * @param {boolean}
16601      * @return {Object} this
16602      * @public
16603      */
16604     this.setUpperCase = function (a) {
16605       if (typeof a === 'boolean') {
16606         hexcase = a;
16607       }
16608       return this;
16609     };
16610     /**
16611      * @description Defines a base64 pad string
16612      * @param {string} Pad
16613      * @return {Object} this
16614      * @public
16615      */
16616     this.setPad = function (a) {
16617       b64pad = a || b64pad;
16618       return this;
16619     };
16620     /**
16621      * Defines a base64 pad string
16622      * @param {boolean}
16623      * @return {Object} this
16624      * @public
16625      */
16626     this.setUTF8 = function (a) {
16627       if (typeof a === 'boolean') {
16628         utf8 = a;
16629       }
16630       return this;
16631     };
16632
16633     // private methods
16634
16635     /**
16636      * Calculate the SHA-512 of a raw string
16637      */
16638     function rstr(s, utf8) {
16639       s = (utf8) ? utf8Encode(s) : s;
16640       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16641     }
16642
16643     /**
16644      * Calculate the HMAC-sha256 of a key and some data (raw strings)
16645      */
16646     function rstr_hmac(key, data) {
16647       key = (utf8) ? utf8Encode(key) : key;
16648       data = (utf8) ? utf8Encode(data) : data;
16649       var hash, i = 0,
16650           bkey = rstr2binb(key),
16651           ipad = Array(16),
16652           opad = Array(16);
16653
16654       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
16655
16656       for (; i < 16; i+=1) {
16657         ipad[i] = bkey[i] ^ 0x36363636;
16658         opad[i] = bkey[i] ^ 0x5C5C5C5C;
16659       }
16660
16661       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
16662       return binb2rstr(binb(opad.concat(hash), 512 + 256));
16663     }
16664
16665     /*
16666      * Main sha256 function, with its support functions
16667      */
16668     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
16669     function sha256_R (X, n) {return ( X >>> n );}
16670     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
16671     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
16672     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
16673     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
16674     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
16675     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
16676     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
16677     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
16678     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
16679     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
16680
16681     sha256_K = [
16682       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
16683       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
16684       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
16685       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
16686       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
16687       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
16688       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
16689       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
16690       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
16691       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
16692       -1866530822, -1538233109, -1090935817, -965641998
16693     ];
16694
16695     function binb(m, l) {
16696       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
16697                  1359893119, -1694144372, 528734635, 1541459225];
16698       var W = new Array(64);
16699       var a, b, c, d, e, f, g, h;
16700       var i, j, T1, T2;
16701
16702       /* append padding */
16703       m[l >> 5] |= 0x80 << (24 - l % 32);
16704       m[((l + 64 >> 9) << 4) + 15] = l;
16705
16706       for (i = 0; i < m.length; i += 16)
16707       {
16708       a = HASH[0];
16709       b = HASH[1];
16710       c = HASH[2];
16711       d = HASH[3];
16712       e = HASH[4];
16713       f = HASH[5];
16714       g = HASH[6];
16715       h = HASH[7];
16716
16717       for (j = 0; j < 64; j+=1)
16718       {
16719         if (j < 16) {
16720           W[j] = m[j + i];
16721         } else {
16722           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
16723                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
16724         }
16725
16726         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
16727                                   sha256_K[j]), W[j]);
16728         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
16729         h = g;
16730         g = f;
16731         f = e;
16732         e = safe_add(d, T1);
16733         d = c;
16734         c = b;
16735         b = a;
16736         a = safe_add(T1, T2);
16737       }
16738
16739       HASH[0] = safe_add(a, HASH[0]);
16740       HASH[1] = safe_add(b, HASH[1]);
16741       HASH[2] = safe_add(c, HASH[2]);
16742       HASH[3] = safe_add(d, HASH[3]);
16743       HASH[4] = safe_add(e, HASH[4]);
16744       HASH[5] = safe_add(f, HASH[5]);
16745       HASH[6] = safe_add(g, HASH[6]);
16746       HASH[7] = safe_add(h, HASH[7]);
16747       }
16748       return HASH;
16749     }
16750
16751   },
16752
16753   /**
16754    * @class Hashes.SHA512
16755    * @param {config}
16756    *
16757    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
16758    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
16759    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16760    * See http://pajhome.org.uk/crypt/md5 for details.
16761    */
16762   SHA512 : function (options) {
16763     /**
16764      * Private properties configuration variables. You may need to tweak these to be compatible with
16765      * the server-side, but the defaults work in most cases.
16766      * @see this.setUpperCase() method
16767      * @see this.setPad() method
16768      */
16769     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
16770         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
16771         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
16772         sha512_k;
16773
16774     /* privileged (public) methods */
16775     this.hex = function (s) {
16776       return rstr2hex(rstr(s));
16777     };
16778     this.b64 = function (s) {
16779       return rstr2b64(rstr(s), b64pad);
16780     };
16781     this.any = function (s, e) {
16782       return rstr2any(rstr(s), e);
16783     };
16784     this.hex_hmac = function (k, d) {
16785       return rstr2hex(rstr_hmac(k, d));
16786     };
16787     this.b64_hmac = function (k, d) {
16788       return rstr2b64(rstr_hmac(k, d), b64pad);
16789     };
16790     this.any_hmac = function (k, d, e) {
16791       return rstr2any(rstr_hmac(k, d), e);
16792     };
16793     /**
16794      * Perform a simple self-test to see if the VM is working
16795      * @return {String} Hexadecimal hash sample
16796      * @public
16797      */
16798     this.vm_test = function () {
16799       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16800     };
16801     /**
16802      * @description Enable/disable uppercase hexadecimal returned string
16803      * @param {boolean}
16804      * @return {Object} this
16805      * @public
16806      */
16807     this.setUpperCase = function (a) {
16808       if (typeof a === 'boolean') {
16809         hexcase = a;
16810       }
16811       return this;
16812     };
16813     /**
16814      * @description Defines a base64 pad string
16815      * @param {string} Pad
16816      * @return {Object} this
16817      * @public
16818      */
16819     this.setPad = function (a) {
16820       b64pad = a || b64pad;
16821       return this;
16822     };
16823     /**
16824      * @description Defines a base64 pad string
16825      * @param {boolean}
16826      * @return {Object} this
16827      * @public
16828      */
16829     this.setUTF8 = function (a) {
16830       if (typeof a === 'boolean') {
16831         utf8 = a;
16832       }
16833       return this;
16834     };
16835
16836     /* private methods */
16837
16838     /**
16839      * Calculate the SHA-512 of a raw string
16840      */
16841     function rstr(s) {
16842       s = (utf8) ? utf8Encode(s) : s;
16843       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16844     }
16845     /*
16846      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
16847      */
16848     function rstr_hmac(key, data) {
16849       key = (utf8) ? utf8Encode(key) : key;
16850       data = (utf8) ? utf8Encode(data) : data;
16851
16852       var hash, i = 0,
16853           bkey = rstr2binb(key),
16854           ipad = Array(32), opad = Array(32);
16855
16856       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
16857
16858       for (; i < 32; i+=1) {
16859         ipad[i] = bkey[i] ^ 0x36363636;
16860         opad[i] = bkey[i] ^ 0x5C5C5C5C;
16861       }
16862
16863       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
16864       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
16865     }
16866
16867     /**
16868      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
16869      */
16870     function binb(x, len) {
16871       var j, i, l,
16872           W = new Array(80),
16873           hash = new Array(16),
16874           //Initial hash values
16875           H = [
16876             new int64(0x6a09e667, -205731576),
16877             new int64(-1150833019, -2067093701),
16878             new int64(0x3c6ef372, -23791573),
16879             new int64(-1521486534, 0x5f1d36f1),
16880             new int64(0x510e527f, -1377402159),
16881             new int64(-1694144372, 0x2b3e6c1f),
16882             new int64(0x1f83d9ab, -79577749),
16883             new int64(0x5be0cd19, 0x137e2179)
16884           ],
16885           T1 = new int64(0, 0),
16886           T2 = new int64(0, 0),
16887           a = new int64(0,0),
16888           b = new int64(0,0),
16889           c = new int64(0,0),
16890           d = new int64(0,0),
16891           e = new int64(0,0),
16892           f = new int64(0,0),
16893           g = new int64(0,0),
16894           h = new int64(0,0),
16895           //Temporary variables not specified by the document
16896           s0 = new int64(0, 0),
16897           s1 = new int64(0, 0),
16898           Ch = new int64(0, 0),
16899           Maj = new int64(0, 0),
16900           r1 = new int64(0, 0),
16901           r2 = new int64(0, 0),
16902           r3 = new int64(0, 0);
16903
16904       if (sha512_k === undefined) {
16905           //SHA512 constants
16906           sha512_k = [
16907             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
16908             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
16909             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
16910             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
16911             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
16912             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
16913             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
16914             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
16915             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
16916             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
16917             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
16918             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
16919             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
16920             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
16921             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
16922             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
16923             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
16924             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
16925             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
16926             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
16927             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
16928             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
16929             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
16930             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
16931             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
16932             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
16933             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
16934             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
16935             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
16936             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
16937             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
16938             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
16939             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
16940             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
16941             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
16942             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
16943             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
16944             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
16945             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
16946             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
16947           ];
16948       }
16949
16950       for (i=0; i<80; i+=1) {
16951         W[i] = new int64(0, 0);
16952       }
16953
16954       // append padding to the source string. The format is described in the FIPS.
16955       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
16956       x[((len + 128 >> 10)<< 5) + 31] = len;
16957       l = x.length;
16958       for (i = 0; i<l; i+=32) { //32 dwords is the block size
16959         int64copy(a, H[0]);
16960         int64copy(b, H[1]);
16961         int64copy(c, H[2]);
16962         int64copy(d, H[3]);
16963         int64copy(e, H[4]);
16964         int64copy(f, H[5]);
16965         int64copy(g, H[6]);
16966         int64copy(h, H[7]);
16967
16968         for (j=0; j<16; j+=1) {
16969           W[j].h = x[i + 2*j];
16970           W[j].l = x[i + 2*j + 1];
16971         }
16972
16973         for (j=16; j<80; j+=1) {
16974           //sigma1
16975           int64rrot(r1, W[j-2], 19);
16976           int64revrrot(r2, W[j-2], 29);
16977           int64shr(r3, W[j-2], 6);
16978           s1.l = r1.l ^ r2.l ^ r3.l;
16979           s1.h = r1.h ^ r2.h ^ r3.h;
16980           //sigma0
16981           int64rrot(r1, W[j-15], 1);
16982           int64rrot(r2, W[j-15], 8);
16983           int64shr(r3, W[j-15], 7);
16984           s0.l = r1.l ^ r2.l ^ r3.l;
16985           s0.h = r1.h ^ r2.h ^ r3.h;
16986
16987           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
16988         }
16989
16990         for (j = 0; j < 80; j+=1) {
16991           //Ch
16992           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
16993           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
16994
16995           //Sigma1
16996           int64rrot(r1, e, 14);
16997           int64rrot(r2, e, 18);
16998           int64revrrot(r3, e, 9);
16999           s1.l = r1.l ^ r2.l ^ r3.l;
17000           s1.h = r1.h ^ r2.h ^ r3.h;
17001
17002           //Sigma0
17003           int64rrot(r1, a, 28);
17004           int64revrrot(r2, a, 2);
17005           int64revrrot(r3, a, 7);
17006           s0.l = r1.l ^ r2.l ^ r3.l;
17007           s0.h = r1.h ^ r2.h ^ r3.h;
17008
17009           //Maj
17010           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
17011           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
17012
17013           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
17014           int64add(T2, s0, Maj);
17015
17016           int64copy(h, g);
17017           int64copy(g, f);
17018           int64copy(f, e);
17019           int64add(e, d, T1);
17020           int64copy(d, c);
17021           int64copy(c, b);
17022           int64copy(b, a);
17023           int64add(a, T1, T2);
17024         }
17025         int64add(H[0], H[0], a);
17026         int64add(H[1], H[1], b);
17027         int64add(H[2], H[2], c);
17028         int64add(H[3], H[3], d);
17029         int64add(H[4], H[4], e);
17030         int64add(H[5], H[5], f);
17031         int64add(H[6], H[6], g);
17032         int64add(H[7], H[7], h);
17033       }
17034
17035       //represent the hash as an array of 32-bit dwords
17036       for (i=0; i<8; i+=1) {
17037         hash[2*i] = H[i].h;
17038         hash[2*i + 1] = H[i].l;
17039       }
17040       return hash;
17041     }
17042
17043     //A constructor for 64-bit numbers
17044     function int64(h, l) {
17045       this.h = h;
17046       this.l = l;
17047       //this.toString = int64toString;
17048     }
17049
17050     //Copies src into dst, assuming both are 64-bit numbers
17051     function int64copy(dst, src) {
17052       dst.h = src.h;
17053       dst.l = src.l;
17054     }
17055
17056     //Right-rotates a 64-bit number by shift
17057     //Won't handle cases of shift>=32
17058     //The function revrrot() is for that
17059     function int64rrot(dst, x, shift) {
17060       dst.l = (x.l >>> shift) | (x.h << (32-shift));
17061       dst.h = (x.h >>> shift) | (x.l << (32-shift));
17062     }
17063
17064     //Reverses the dwords of the source and then rotates right by shift.
17065     //This is equivalent to rotation by 32+shift
17066     function int64revrrot(dst, x, shift) {
17067       dst.l = (x.h >>> shift) | (x.l << (32-shift));
17068       dst.h = (x.l >>> shift) | (x.h << (32-shift));
17069     }
17070
17071     //Bitwise-shifts right a 64-bit number by shift
17072     //Won't handle shift>=32, but it's never needed in SHA512
17073     function int64shr(dst, x, shift) {
17074       dst.l = (x.l >>> shift) | (x.h << (32-shift));
17075       dst.h = (x.h >>> shift);
17076     }
17077
17078     //Adds two 64-bit numbers
17079     //Like the original implementation, does not rely on 32-bit operations
17080     function int64add(dst, x, y) {
17081        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
17082        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
17083        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
17084        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
17085        dst.l = (w0 & 0xffff) | (w1 << 16);
17086        dst.h = (w2 & 0xffff) | (w3 << 16);
17087     }
17088
17089     //Same, except with 4 addends. Works faster than adding them one by one.
17090     function int64add4(dst, a, b, c, d) {
17091        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
17092        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
17093        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
17094        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
17095        dst.l = (w0 & 0xffff) | (w1 << 16);
17096        dst.h = (w2 & 0xffff) | (w3 << 16);
17097     }
17098
17099     //Same, except with 5 addends
17100     function int64add5(dst, a, b, c, d, e) {
17101       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
17102           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
17103           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
17104           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
17105        dst.l = (w0 & 0xffff) | (w1 << 16);
17106        dst.h = (w2 & 0xffff) | (w3 << 16);
17107     }
17108   },
17109   /**
17110    * @class Hashes.RMD160
17111    * @constructor
17112    * @param {Object} [config]
17113    *
17114    * A JavaScript implementation of the RIPEMD-160 Algorithm
17115    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
17116    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
17117    * See http://pajhome.org.uk/crypt/md5 for details.
17118    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
17119    */
17120   RMD160 : function (options) {
17121     /**
17122      * Private properties configuration variables. You may need to tweak these to be compatible with
17123      * the server-side, but the defaults work in most cases.
17124      * @see this.setUpperCase() method
17125      * @see this.setPad() method
17126      */
17127     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
17128         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
17129         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
17130         rmd160_r1 = [
17131            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
17132            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
17133            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
17134            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
17135            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
17136         ],
17137         rmd160_r2 = [
17138            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
17139            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
17140           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
17141            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
17142           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
17143         ],
17144         rmd160_s1 = [
17145           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
17146            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
17147           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
17148           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
17149            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
17150         ],
17151         rmd160_s2 = [
17152            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
17153            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
17154            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
17155           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
17156            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
17157         ];
17158
17159     /* privileged (public) methods */
17160     this.hex = function (s) {
17161       return rstr2hex(rstr(s, utf8));
17162     };
17163     this.b64 = function (s) {
17164       return rstr2b64(rstr(s, utf8), b64pad);
17165     };
17166     this.any = function (s, e) {
17167       return rstr2any(rstr(s, utf8), e);
17168     };
17169     this.hex_hmac = function (k, d) {
17170       return rstr2hex(rstr_hmac(k, d));
17171     };
17172     this.b64_hmac = function (k, d) {
17173       return rstr2b64(rstr_hmac(k, d), b64pad);
17174     };
17175     this.any_hmac = function (k, d, e) {
17176       return rstr2any(rstr_hmac(k, d), e);
17177     };
17178     /**
17179      * Perform a simple self-test to see if the VM is working
17180      * @return {String} Hexadecimal hash sample
17181      * @public
17182      */
17183     this.vm_test = function () {
17184       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
17185     };
17186     /**
17187      * @description Enable/disable uppercase hexadecimal returned string
17188      * @param {boolean}
17189      * @return {Object} this
17190      * @public
17191      */
17192     this.setUpperCase = function (a) {
17193       if (typeof a === 'boolean' ) { hexcase = a; }
17194       return this;
17195     };
17196     /**
17197      * @description Defines a base64 pad string
17198      * @param {string} Pad
17199      * @return {Object} this
17200      * @public
17201      */
17202     this.setPad = function (a) {
17203       if (typeof a !== 'undefined' ) { b64pad = a; }
17204       return this;
17205     };
17206     /**
17207      * @description Defines a base64 pad string
17208      * @param {boolean}
17209      * @return {Object} this
17210      * @public
17211      */
17212     this.setUTF8 = function (a) {
17213       if (typeof a === 'boolean') { utf8 = a; }
17214       return this;
17215     };
17216
17217     /* private methods */
17218
17219     /**
17220      * Calculate the rmd160 of a raw string
17221      */
17222     function rstr(s) {
17223       s = (utf8) ? utf8Encode(s) : s;
17224       return binl2rstr(binl(rstr2binl(s), s.length * 8));
17225     }
17226
17227     /**
17228      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
17229      */
17230     function rstr_hmac(key, data) {
17231       key = (utf8) ? utf8Encode(key) : key;
17232       data = (utf8) ? utf8Encode(data) : data;
17233       var i, hash,
17234           bkey = rstr2binl(key),
17235           ipad = Array(16), opad = Array(16);
17236
17237       if (bkey.length > 16) {
17238         bkey = binl(bkey, key.length * 8);
17239       }
17240
17241       for (i = 0; i < 16; i+=1) {
17242         ipad[i] = bkey[i] ^ 0x36363636;
17243         opad[i] = bkey[i] ^ 0x5C5C5C5C;
17244       }
17245       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
17246       return binl2rstr(binl(opad.concat(hash), 512 + 160));
17247     }
17248
17249     /**
17250      * Convert an array of little-endian words to a string
17251      */
17252     function binl2rstr(input) {
17253       var i, output = '', l = input.length * 32;
17254       for (i = 0; i < l; i += 8) {
17255         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
17256       }
17257       return output;
17258     }
17259
17260     /**
17261      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
17262      */
17263     function binl(x, len) {
17264       var T, j, i, l,
17265           h0 = 0x67452301,
17266           h1 = 0xefcdab89,
17267           h2 = 0x98badcfe,
17268           h3 = 0x10325476,
17269           h4 = 0xc3d2e1f0,
17270           A1, B1, C1, D1, E1,
17271           A2, B2, C2, D2, E2;
17272
17273       /* append padding */
17274       x[len >> 5] |= 0x80 << (len % 32);
17275       x[(((len + 64) >>> 9) << 4) + 14] = len;
17276       l = x.length;
17277
17278       for (i = 0; i < l; i+=16) {
17279         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
17280         for (j = 0; j <= 79; j+=1) {
17281           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
17282           T = safe_add(T, x[i + rmd160_r1[j]]);
17283           T = safe_add(T, rmd160_K1(j));
17284           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
17285           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
17286           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
17287           T = safe_add(T, x[i + rmd160_r2[j]]);
17288           T = safe_add(T, rmd160_K2(j));
17289           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
17290           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
17291         }
17292
17293         T = safe_add(h1, safe_add(C1, D2));
17294         h1 = safe_add(h2, safe_add(D1, E2));
17295         h2 = safe_add(h3, safe_add(E1, A2));
17296         h3 = safe_add(h4, safe_add(A1, B2));
17297         h4 = safe_add(h0, safe_add(B1, C2));
17298         h0 = T;
17299       }
17300       return [h0, h1, h2, h3, h4];
17301     }
17302
17303     // specific algorithm methods
17304     function rmd160_f(j, x, y, z) {
17305       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
17306          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
17307          (32 <= j && j <= 47) ? (x | ~y) ^ z :
17308          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
17309          (64 <= j && j <= 79) ? x ^ (y | ~z) :
17310          'rmd160_f: j out of range';
17311     }
17312
17313     function rmd160_K1(j) {
17314       return ( 0 <= j && j <= 15) ? 0x00000000 :
17315          (16 <= j && j <= 31) ? 0x5a827999 :
17316          (32 <= j && j <= 47) ? 0x6ed9eba1 :
17317          (48 <= j && j <= 63) ? 0x8f1bbcdc :
17318          (64 <= j && j <= 79) ? 0xa953fd4e :
17319          'rmd160_K1: j out of range';
17320     }
17321
17322     function rmd160_K2(j){
17323       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
17324          (16 <= j && j <= 31) ? 0x5c4dd124 :
17325          (32 <= j && j <= 47) ? 0x6d703ef3 :
17326          (48 <= j && j <= 63) ? 0x7a6d76e9 :
17327          (64 <= j && j <= 79) ? 0x00000000 :
17328          'rmd160_K2: j out of range';
17329     }
17330   }
17331 };
17332
17333   // exposes Hashes
17334   (function( window, undefined ) {
17335     var freeExports = false;
17336     if (typeof exports === 'object' ) {
17337       freeExports = exports;
17338       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
17339     }
17340
17341     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
17342       // define as an anonymous module, so, through path mapping, it can be aliased
17343       define(function () { return Hashes; });
17344     }
17345     else if ( freeExports ) {
17346       // in Node.js or RingoJS v0.8.0+
17347       if ( typeof module === 'object' && module && module.exports === freeExports ) {
17348         module.exports = Hashes;
17349       }
17350       // in Narwhal or RingoJS v0.7.0-
17351       else {
17352         freeExports.Hashes = Hashes;
17353       }
17354     }
17355     else {
17356       // in a browser or Rhino
17357       window.Hashes = Hashes;
17358     }
17359   }( this ));
17360 }()); // IIFE
17361
17362 })(window)
17363 },{}],2:[function(require,module,exports){
17364 'use strict';
17365
17366 var hashes = require('jshashes'),
17367     xtend = require('xtend'),
17368     sha1 = new hashes.SHA1();
17369
17370 var ohauth = {};
17371
17372 ohauth.qsString = function(obj) {
17373     return Object.keys(obj).sort().map(function(key) {
17374         return ohauth.percentEncode(key) + '=' +
17375             ohauth.percentEncode(obj[key]);
17376     }).join('&');
17377 };
17378
17379 ohauth.stringQs = function(str) {
17380     return str.split('&').reduce(function(obj, pair){
17381         var parts = pair.split('=');
17382         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
17383             '' : decodeURIComponent(parts[1]);
17384         return obj;
17385     }, {});
17386 };
17387
17388 ohauth.rawxhr = function(method, url, data, headers, callback) {
17389     var xhr = new XMLHttpRequest(),
17390         twoHundred = /^20\d$/;
17391     xhr.onreadystatechange = function() {
17392         if (4 == xhr.readyState && 0 !== xhr.status) {
17393             if (twoHundred.test(xhr.status)) callback(null, xhr);
17394             else return callback(xhr, null);
17395         }
17396     };
17397     xhr.onerror = function(e) { return callback(e, null); };
17398     xhr.open(method, url, true);
17399     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
17400     xhr.send(data);
17401 };
17402
17403 ohauth.xhr = function(method, url, auth, data, options, callback) {
17404     var headers = (options && options.header) || {
17405         'Content-Type': 'application/x-www-form-urlencoded'
17406     };
17407     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
17408     ohauth.rawxhr(method, url, data, headers, callback);
17409 };
17410
17411 ohauth.nonce = function() {
17412     for (var o = ''; o.length < 6;) {
17413         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
17414     }
17415     return o;
17416 };
17417
17418 ohauth.authHeader = function(obj) {
17419     return Object.keys(obj).sort().map(function(key) {
17420         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
17421     }).join(', ');
17422 };
17423
17424 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
17425
17426 ohauth.percentEncode = function(s) {
17427     return encodeURIComponent(s)
17428         .replace(/\!/g, '%21').replace(/\'/g, '%27')
17429         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
17430 };
17431
17432 ohauth.baseString = function(method, url, params) {
17433     if (params.oauth_signature) delete params.oauth_signature;
17434     return [
17435         method,
17436         ohauth.percentEncode(url),
17437         ohauth.percentEncode(ohauth.qsString(params))].join('&');
17438 };
17439
17440 ohauth.signature = function(oauth_secret, token_secret, baseString) {
17441     return sha1.b64_hmac(
17442         ohauth.percentEncode(oauth_secret) + '&' +
17443         ohauth.percentEncode(token_secret),
17444         baseString);
17445 };
17446
17447 /**
17448  * Takes an options object for configuration (consumer_key,
17449  * consumer_secret, version, signature_method, token) and returns a
17450  * function that generates the Authorization header for given data.
17451  *
17452  * The returned function takes these parameters:
17453  * - method: GET/POST/...
17454  * - uri: full URI with protocol, port, path and query string
17455  * - extra_params: any extra parameters (that are passed in the POST data),
17456  *   can be an object or a from-urlencoded string.
17457  *
17458  * Returned function returns full OAuth header with "OAuth" string in it.
17459  */
17460
17461 ohauth.headerGenerator = function(options) {
17462     options = options || {};
17463     var consumer_key = options.consumer_key || '',
17464         consumer_secret = options.consumer_secret || '',
17465         signature_method = options.signature_method || 'HMAC-SHA1',
17466         version = options.version || '1.0',
17467         token = options.token || '';
17468
17469     return function(method, uri, extra_params) {
17470         method = method.toUpperCase();
17471         if (typeof extra_params === 'string' && extra_params.length > 0) {
17472             extra_params = ohauth.stringQs(extra_params);
17473         }
17474
17475         var uri_parts = uri.split('?', 2),
17476         base_uri = uri_parts[0];
17477
17478         var query_params = uri_parts.length === 2 ?
17479             ohauth.stringQs(uri_parts[1]) : {};
17480
17481         var oauth_params = {
17482             oauth_consumer_key: consumer_key,
17483             oauth_signature_method: signature_method,
17484             oauth_version: version,
17485             oauth_timestamp: ohauth.timestamp(),
17486             oauth_nonce: ohauth.nonce()
17487         };
17488
17489         if (token) oauth_params.oauth_token = token;
17490
17491         var all_params = xtend({}, oauth_params, query_params, extra_params),
17492             base_str = ohauth.baseString(method, base_uri, all_params);
17493
17494         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
17495
17496         return 'OAuth ' + ohauth.authHeader(oauth_params);
17497     };
17498 };
17499
17500 module.exports = ohauth;
17501
17502 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
17503 module.exports = Object.keys || require('./shim');
17504
17505
17506 },{"./shim":8}],8:[function(require,module,exports){
17507 (function () {
17508         "use strict";
17509
17510         // modified from https://github.com/kriskowal/es5-shim
17511         var has = Object.prototype.hasOwnProperty,
17512                 is = require('is'),
17513                 forEach = require('foreach'),
17514                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
17515                 dontEnums = [
17516                         "toString",
17517                         "toLocaleString",
17518                         "valueOf",
17519                         "hasOwnProperty",
17520                         "isPrototypeOf",
17521                         "propertyIsEnumerable",
17522                         "constructor"
17523                 ],
17524                 keysShim;
17525
17526         keysShim = function keys(object) {
17527                 if (!is.object(object) && !is.array(object)) {
17528                         throw new TypeError("Object.keys called on a non-object");
17529                 }
17530
17531                 var name, theKeys = [];
17532                 for (name in object) {
17533                         if (has.call(object, name)) {
17534                                 theKeys.push(name);
17535                         }
17536                 }
17537
17538                 if (hasDontEnumBug) {
17539                         forEach(dontEnums, function (dontEnum) {
17540                                 if (has.call(object, dontEnum)) {
17541                                         theKeys.push(dontEnum);
17542                                 }
17543                         });
17544                 }
17545                 return theKeys;
17546         };
17547
17548         module.exports = keysShim;
17549 }());
17550
17551
17552 },{"is":9,"foreach":10}],9:[function(require,module,exports){
17553
17554 /**!
17555  * is
17556  * the definitive JavaScript type testing library
17557  *
17558  * @copyright 2013 Enrico Marino
17559  * @license MIT
17560  */
17561
17562 var objProto = Object.prototype;
17563 var owns = objProto.hasOwnProperty;
17564 var toString = objProto.toString;
17565 var isActualNaN = function (value) {
17566   return value !== value;
17567 };
17568 var NON_HOST_TYPES = {
17569   "boolean": 1,
17570   "number": 1,
17571   "string": 1,
17572   "undefined": 1
17573 };
17574
17575 /**
17576  * Expose `is`
17577  */
17578
17579 var is = module.exports = {};
17580
17581 /**
17582  * Test general.
17583  */
17584
17585 /**
17586  * is.type
17587  * Test if `value` is a type of `type`.
17588  *
17589  * @param {Mixed} value value to test
17590  * @param {String} type type
17591  * @return {Boolean} true if `value` is a type of `type`, false otherwise
17592  * @api public
17593  */
17594
17595 is.a =
17596 is.type = function (value, type) {
17597   return typeof value === type;
17598 };
17599
17600 /**
17601  * is.defined
17602  * Test if `value` is defined.
17603  *
17604  * @param {Mixed} value value to test
17605  * @return {Boolean} true if 'value' is defined, false otherwise
17606  * @api public
17607  */
17608
17609 is.defined = function (value) {
17610   return value !== undefined;
17611 };
17612
17613 /**
17614  * is.empty
17615  * Test if `value` is empty.
17616  *
17617  * @param {Mixed} value value to test
17618  * @return {Boolean} true if `value` is empty, false otherwise
17619  * @api public
17620  */
17621
17622 is.empty = function (value) {
17623   var type = toString.call(value);
17624   var key;
17625
17626   if ('[object Array]' === type || '[object Arguments]' === type) {
17627     return value.length === 0;
17628   }
17629
17630   if ('[object Object]' === type) {
17631     for (key in value) if (owns.call(value, key)) return false;
17632     return true;
17633   }
17634
17635   if ('[object String]' === type) {
17636     return '' === value;
17637   }
17638
17639   return false;
17640 };
17641
17642 /**
17643  * is.equal
17644  * Test if `value` is equal to `other`.
17645  *
17646  * @param {Mixed} value value to test
17647  * @param {Mixed} other value to compare with
17648  * @return {Boolean} true if `value` is equal to `other`, false otherwise
17649  */
17650
17651 is.equal = function (value, other) {
17652   var type = toString.call(value)
17653   var key;
17654
17655   if (type !== toString.call(other)) {
17656     return false;
17657   }
17658
17659   if ('[object Object]' === type) {
17660     for (key in value) {
17661       if (!is.equal(value[key], other[key])) {
17662         return false;
17663       }
17664     }
17665     return true;
17666   }
17667
17668   if ('[object Array]' === type) {
17669     key = value.length;
17670     if (key !== other.length) {
17671       return false;
17672     }
17673     while (--key) {
17674       if (!is.equal(value[key], other[key])) {
17675         return false;
17676       }
17677     }
17678     return true;
17679   }
17680
17681   if ('[object Function]' === type) {
17682     return value.prototype === other.prototype;
17683   }
17684
17685   if ('[object Date]' === type) {
17686     return value.getTime() === other.getTime();
17687   }
17688
17689   return value === other;
17690 };
17691
17692 /**
17693  * is.hosted
17694  * Test if `value` is hosted by `host`.
17695  *
17696  * @param {Mixed} value to test
17697  * @param {Mixed} host host to test with
17698  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
17699  * @api public
17700  */
17701
17702 is.hosted = function (value, host) {
17703   var type = typeof host[value];
17704   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
17705 };
17706
17707 /**
17708  * is.instance
17709  * Test if `value` is an instance of `constructor`.
17710  *
17711  * @param {Mixed} value value to test
17712  * @return {Boolean} true if `value` is an instance of `constructor`
17713  * @api public
17714  */
17715
17716 is.instance = is['instanceof'] = function (value, constructor) {
17717   return value instanceof constructor;
17718 };
17719
17720 /**
17721  * is.null
17722  * Test if `value` is null.
17723  *
17724  * @param {Mixed} value value to test
17725  * @return {Boolean} true if `value` is null, false otherwise
17726  * @api public
17727  */
17728
17729 is['null'] = function (value) {
17730   return value === null;
17731 };
17732
17733 /**
17734  * is.undefined
17735  * Test if `value` is undefined.
17736  *
17737  * @param {Mixed} value value to test
17738  * @return {Boolean} true if `value` is undefined, false otherwise
17739  * @api public
17740  */
17741
17742 is.undefined = function (value) {
17743   return value === undefined;
17744 };
17745
17746 /**
17747  * Test arguments.
17748  */
17749
17750 /**
17751  * is.arguments
17752  * Test if `value` is an arguments object.
17753  *
17754  * @param {Mixed} value value to test
17755  * @return {Boolean} true if `value` is an arguments object, false otherwise
17756  * @api public
17757  */
17758
17759 is.arguments = function (value) {
17760   var isStandardArguments = '[object Arguments]' === toString.call(value);
17761   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
17762   return isStandardArguments || isOldArguments;
17763 };
17764
17765 /**
17766  * Test array.
17767  */
17768
17769 /**
17770  * is.array
17771  * Test if 'value' is an array.
17772  *
17773  * @param {Mixed} value value to test
17774  * @return {Boolean} true if `value` is an array, false otherwise
17775  * @api public
17776  */
17777
17778 is.array = function (value) {
17779   return '[object Array]' === toString.call(value);
17780 };
17781
17782 /**
17783  * is.arguments.empty
17784  * Test if `value` is an empty arguments object.
17785  *
17786  * @param {Mixed} value value to test
17787  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
17788  * @api public
17789  */
17790 is.arguments.empty = function (value) {
17791   return is.arguments(value) && value.length === 0;
17792 };
17793
17794 /**
17795  * is.array.empty
17796  * Test if `value` is an empty array.
17797  *
17798  * @param {Mixed} value value to test
17799  * @return {Boolean} true if `value` is an empty array, false otherwise
17800  * @api public
17801  */
17802 is.array.empty = function (value) {
17803   return is.array(value) && value.length === 0;
17804 };
17805
17806 /**
17807  * is.arraylike
17808  * Test if `value` is an arraylike object.
17809  *
17810  * @param {Mixed} value value to test
17811  * @return {Boolean} true if `value` is an arguments object, false otherwise
17812  * @api public
17813  */
17814
17815 is.arraylike = function (value) {
17816   return !!value && !is.boolean(value)
17817     && owns.call(value, 'length')
17818     && isFinite(value.length)
17819     && is.number(value.length)
17820     && value.length >= 0;
17821 };
17822
17823 /**
17824  * Test boolean.
17825  */
17826
17827 /**
17828  * is.boolean
17829  * Test if `value` is a boolean.
17830  *
17831  * @param {Mixed} value value to test
17832  * @return {Boolean} true if `value` is a boolean, false otherwise
17833  * @api public
17834  */
17835
17836 is.boolean = function (value) {
17837   return '[object Boolean]' === toString.call(value);
17838 };
17839
17840 /**
17841  * is.false
17842  * Test if `value` is false.
17843  *
17844  * @param {Mixed} value value to test
17845  * @return {Boolean} true if `value` is false, false otherwise
17846  * @api public
17847  */
17848
17849 is['false'] = function (value) {
17850   return is.boolean(value) && (value === false || value.valueOf() === false);
17851 };
17852
17853 /**
17854  * is.true
17855  * Test if `value` is true.
17856  *
17857  * @param {Mixed} value value to test
17858  * @return {Boolean} true if `value` is true, false otherwise
17859  * @api public
17860  */
17861
17862 is['true'] = function (value) {
17863   return is.boolean(value) && (value === true || value.valueOf() === true);
17864 };
17865
17866 /**
17867  * Test date.
17868  */
17869
17870 /**
17871  * is.date
17872  * Test if `value` is a date.
17873  *
17874  * @param {Mixed} value value to test
17875  * @return {Boolean} true if `value` is a date, false otherwise
17876  * @api public
17877  */
17878
17879 is.date = function (value) {
17880   return '[object Date]' === toString.call(value);
17881 };
17882
17883 /**
17884  * Test element.
17885  */
17886
17887 /**
17888  * is.element
17889  * Test if `value` is an html element.
17890  *
17891  * @param {Mixed} value value to test
17892  * @return {Boolean} true if `value` is an HTML Element, false otherwise
17893  * @api public
17894  */
17895
17896 is.element = function (value) {
17897   return value !== undefined
17898     && typeof HTMLElement !== 'undefined'
17899     && value instanceof HTMLElement
17900     && value.nodeType === 1;
17901 };
17902
17903 /**
17904  * Test error.
17905  */
17906
17907 /**
17908  * is.error
17909  * Test if `value` is an error object.
17910  *
17911  * @param {Mixed} value value to test
17912  * @return {Boolean} true if `value` is an error object, false otherwise
17913  * @api public
17914  */
17915
17916 is.error = function (value) {
17917   return '[object Error]' === toString.call(value);
17918 };
17919
17920 /**
17921  * Test function.
17922  */
17923
17924 /**
17925  * is.fn / is.function (deprecated)
17926  * Test if `value` is a function.
17927  *
17928  * @param {Mixed} value value to test
17929  * @return {Boolean} true if `value` is a function, false otherwise
17930  * @api public
17931  */
17932
17933 is.fn = is['function'] = function (value) {
17934   var isAlert = typeof window !== 'undefined' && value === window.alert;
17935   return isAlert || '[object Function]' === toString.call(value);
17936 };
17937
17938 /**
17939  * Test number.
17940  */
17941
17942 /**
17943  * is.number
17944  * Test if `value` is a number.
17945  *
17946  * @param {Mixed} value value to test
17947  * @return {Boolean} true if `value` is a number, false otherwise
17948  * @api public
17949  */
17950
17951 is.number = function (value) {
17952   return '[object Number]' === toString.call(value);
17953 };
17954
17955 /**
17956  * is.infinite
17957  * Test if `value` is positive or negative infinity.
17958  *
17959  * @param {Mixed} value value to test
17960  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
17961  * @api public
17962  */
17963 is.infinite = function (value) {
17964   return value === Infinity || value === -Infinity;
17965 };
17966
17967 /**
17968  * is.decimal
17969  * Test if `value` is a decimal number.
17970  *
17971  * @param {Mixed} value value to test
17972  * @return {Boolean} true if `value` is a decimal number, false otherwise
17973  * @api public
17974  */
17975
17976 is.decimal = function (value) {
17977   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
17978 };
17979
17980 /**
17981  * is.divisibleBy
17982  * Test if `value` is divisible by `n`.
17983  *
17984  * @param {Number} value value to test
17985  * @param {Number} n dividend
17986  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
17987  * @api public
17988  */
17989
17990 is.divisibleBy = function (value, n) {
17991   var isDividendInfinite = is.infinite(value);
17992   var isDivisorInfinite = is.infinite(n);
17993   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
17994   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
17995 };
17996
17997 /**
17998  * is.int
17999  * Test if `value` is an integer.
18000  *
18001  * @param value to test
18002  * @return {Boolean} true if `value` is an integer, false otherwise
18003  * @api public
18004  */
18005
18006 is.int = function (value) {
18007   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
18008 };
18009
18010 /**
18011  * is.maximum
18012  * Test if `value` is greater than 'others' values.
18013  *
18014  * @param {Number} value value to test
18015  * @param {Array} others values to compare with
18016  * @return {Boolean} true if `value` is greater than `others` values
18017  * @api public
18018  */
18019
18020 is.maximum = function (value, others) {
18021   if (isActualNaN(value)) {
18022     throw new TypeError('NaN is not a valid value');
18023   } else if (!is.arraylike(others)) {
18024     throw new TypeError('second argument must be array-like');
18025   }
18026   var len = others.length;
18027
18028   while (--len >= 0) {
18029     if (value < others[len]) {
18030       return false;
18031     }
18032   }
18033
18034   return true;
18035 };
18036
18037 /**
18038  * is.minimum
18039  * Test if `value` is less than `others` values.
18040  *
18041  * @param {Number} value value to test
18042  * @param {Array} others values to compare with
18043  * @return {Boolean} true if `value` is less than `others` values
18044  * @api public
18045  */
18046
18047 is.minimum = function (value, others) {
18048   if (isActualNaN(value)) {
18049     throw new TypeError('NaN is not a valid value');
18050   } else if (!is.arraylike(others)) {
18051     throw new TypeError('second argument must be array-like');
18052   }
18053   var len = others.length;
18054
18055   while (--len >= 0) {
18056     if (value > others[len]) {
18057       return false;
18058     }
18059   }
18060
18061   return true;
18062 };
18063
18064 /**
18065  * is.nan
18066  * Test if `value` is not a number.
18067  *
18068  * @param {Mixed} value value to test
18069  * @return {Boolean} true if `value` is not a number, false otherwise
18070  * @api public
18071  */
18072
18073 is.nan = function (value) {
18074   return !is.number(value) || value !== value;
18075 };
18076
18077 /**
18078  * is.even
18079  * Test if `value` is an even number.
18080  *
18081  * @param {Number} value value to test
18082  * @return {Boolean} true if `value` is an even number, false otherwise
18083  * @api public
18084  */
18085
18086 is.even = function (value) {
18087   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
18088 };
18089
18090 /**
18091  * is.odd
18092  * Test if `value` is an odd number.
18093  *
18094  * @param {Number} value value to test
18095  * @return {Boolean} true if `value` is an odd number, false otherwise
18096  * @api public
18097  */
18098
18099 is.odd = function (value) {
18100   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
18101 };
18102
18103 /**
18104  * is.ge
18105  * Test if `value` is greater than or equal to `other`.
18106  *
18107  * @param {Number} value value to test
18108  * @param {Number} other value to compare with
18109  * @return {Boolean}
18110  * @api public
18111  */
18112
18113 is.ge = function (value, other) {
18114   if (isActualNaN(value) || isActualNaN(other)) {
18115     throw new TypeError('NaN is not a valid value');
18116   }
18117   return !is.infinite(value) && !is.infinite(other) && value >= other;
18118 };
18119
18120 /**
18121  * is.gt
18122  * Test if `value` is greater than `other`.
18123  *
18124  * @param {Number} value value to test
18125  * @param {Number} other value to compare with
18126  * @return {Boolean}
18127  * @api public
18128  */
18129
18130 is.gt = function (value, other) {
18131   if (isActualNaN(value) || isActualNaN(other)) {
18132     throw new TypeError('NaN is not a valid value');
18133   }
18134   return !is.infinite(value) && !is.infinite(other) && value > other;
18135 };
18136
18137 /**
18138  * is.le
18139  * Test if `value` is less than or equal to `other`.
18140  *
18141  * @param {Number} value value to test
18142  * @param {Number} other value to compare with
18143  * @return {Boolean} if 'value' is less than or equal to 'other'
18144  * @api public
18145  */
18146
18147 is.le = function (value, other) {
18148   if (isActualNaN(value) || isActualNaN(other)) {
18149     throw new TypeError('NaN is not a valid value');
18150   }
18151   return !is.infinite(value) && !is.infinite(other) && value <= other;
18152 };
18153
18154 /**
18155  * is.lt
18156  * Test if `value` is less than `other`.
18157  *
18158  * @param {Number} value value to test
18159  * @param {Number} other value to compare with
18160  * @return {Boolean} if `value` is less than `other`
18161  * @api public
18162  */
18163
18164 is.lt = function (value, other) {
18165   if (isActualNaN(value) || isActualNaN(other)) {
18166     throw new TypeError('NaN is not a valid value');
18167   }
18168   return !is.infinite(value) && !is.infinite(other) && value < other;
18169 };
18170
18171 /**
18172  * is.within
18173  * Test if `value` is within `start` and `finish`.
18174  *
18175  * @param {Number} value value to test
18176  * @param {Number} start lower bound
18177  * @param {Number} finish upper bound
18178  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
18179  * @api public
18180  */
18181 is.within = function (value, start, finish) {
18182   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
18183     throw new TypeError('NaN is not a valid value');
18184   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
18185     throw new TypeError('all arguments must be numbers');
18186   }
18187   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
18188   return isAnyInfinite || (value >= start && value <= finish);
18189 };
18190
18191 /**
18192  * Test object.
18193  */
18194
18195 /**
18196  * is.object
18197  * Test if `value` is an object.
18198  *
18199  * @param {Mixed} value value to test
18200  * @return {Boolean} true if `value` is an object, false otherwise
18201  * @api public
18202  */
18203
18204 is.object = function (value) {
18205   return value && '[object Object]' === toString.call(value);
18206 };
18207
18208 /**
18209  * is.hash
18210  * Test if `value` is a hash - a plain object literal.
18211  *
18212  * @param {Mixed} value value to test
18213  * @return {Boolean} true if `value` is a hash, false otherwise
18214  * @api public
18215  */
18216
18217 is.hash = function (value) {
18218   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
18219 };
18220
18221 /**
18222  * Test regexp.
18223  */
18224
18225 /**
18226  * is.regexp
18227  * Test if `value` is a regular expression.
18228  *
18229  * @param {Mixed} value value to test
18230  * @return {Boolean} true if `value` is a regexp, false otherwise
18231  * @api public
18232  */
18233
18234 is.regexp = function (value) {
18235   return '[object RegExp]' === toString.call(value);
18236 };
18237
18238 /**
18239  * Test string.
18240  */
18241
18242 /**
18243  * is.string
18244  * Test if `value` is a string.
18245  *
18246  * @param {Mixed} value value to test
18247  * @return {Boolean} true if 'value' is a string, false otherwise
18248  * @api public
18249  */
18250
18251 is.string = function (value) {
18252   return '[object String]' === toString.call(value);
18253 };
18254
18255
18256 },{}],10:[function(require,module,exports){
18257
18258 var hasOwn = Object.prototype.hasOwnProperty;
18259 var toString = Object.prototype.toString;
18260
18261 module.exports = function forEach (obj, fn, ctx) {
18262     if (toString.call(fn) !== '[object Function]') {
18263         throw new TypeError('iterator must be a function');
18264     }
18265     var l = obj.length;
18266     if (l === +l) {
18267         for (var i = 0; i < l; i++) {
18268             fn.call(ctx, obj[i], i, obj);
18269         }
18270     } else {
18271         for (var k in obj) {
18272             if (hasOwn.call(obj, k)) {
18273                 fn.call(ctx, obj[k], k, obj);
18274             }
18275         }
18276     }
18277 };
18278
18279
18280 },{}]},{},[1])(1)
18281 });
18282 ;
18283 /*
18284  (c) 2015, Vladimir Agafonkin
18285  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
18286  https://github.com/mourner/rbush
18287 */
18288
18289 (function () {
18290 'use strict';
18291
18292 function rbush(maxEntries, format) {
18293
18294     // jshint newcap: false, validthis: true
18295     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
18296
18297     // max entries in a node is 9 by default; min node fill is 40% for best performance
18298     this._maxEntries = Math.max(4, maxEntries || 9);
18299     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
18300
18301     if (format) {
18302         this._initFormat(format);
18303     }
18304
18305     this.clear();
18306 }
18307
18308 rbush.prototype = {
18309
18310     all: function () {
18311         return this._all(this.data, []);
18312     },
18313
18314     search: function (bbox) {
18315
18316         var node = this.data,
18317             result = [],
18318             toBBox = this.toBBox;
18319
18320         if (!intersects(bbox, node.bbox)) return result;
18321
18322         var nodesToSearch = [],
18323             i, len, child, childBBox;
18324
18325         while (node) {
18326             for (i = 0, len = node.children.length; i < len; i++) {
18327
18328                 child = node.children[i];
18329                 childBBox = node.leaf ? toBBox(child) : child.bbox;
18330
18331                 if (intersects(bbox, childBBox)) {
18332                     if (node.leaf) result.push(child);
18333                     else if (contains(bbox, childBBox)) this._all(child, result);
18334                     else nodesToSearch.push(child);
18335                 }
18336             }
18337             node = nodesToSearch.pop();
18338         }
18339
18340         return result;
18341     },
18342
18343     collides: function (bbox) {
18344
18345         var node = this.data,
18346             toBBox = this.toBBox;
18347
18348         if (!intersects(bbox, node.bbox)) return false;
18349
18350         var nodesToSearch = [],
18351             i, len, child, childBBox;
18352
18353         while (node) {
18354             for (i = 0, len = node.children.length; i < len; i++) {
18355
18356                 child = node.children[i];
18357                 childBBox = node.leaf ? toBBox(child) : child.bbox;
18358
18359                 if (intersects(bbox, childBBox)) {
18360                     if (node.leaf || contains(bbox, childBBox)) return true;
18361                     nodesToSearch.push(child);
18362                 }
18363             }
18364             node = nodesToSearch.pop();
18365         }
18366
18367         return false;
18368     },
18369
18370     load: function (data) {
18371         if (!(data && data.length)) return this;
18372
18373         if (data.length < this._minEntries) {
18374             for (var i = 0, len = data.length; i < len; i++) {
18375                 this.insert(data[i]);
18376             }
18377             return this;
18378         }
18379
18380         // recursively build the tree with the given data from stratch using OMT algorithm
18381         var node = this._build(data.slice(), 0, data.length - 1, 0);
18382
18383         if (!this.data.children.length) {
18384             // save as is if tree is empty
18385             this.data = node;
18386
18387         } else if (this.data.height === node.height) {
18388             // split root if trees have the same height
18389             this._splitRoot(this.data, node);
18390
18391         } else {
18392             if (this.data.height < node.height) {
18393                 // swap trees if inserted one is bigger
18394                 var tmpNode = this.data;
18395                 this.data = node;
18396                 node = tmpNode;
18397             }
18398
18399             // insert the small tree into the large tree at appropriate level
18400             this._insert(node, this.data.height - node.height - 1, true);
18401         }
18402
18403         return this;
18404     },
18405
18406     insert: function (item) {
18407         if (item) this._insert(item, this.data.height - 1);
18408         return this;
18409     },
18410
18411     clear: function () {
18412         this.data = {
18413             children: [],
18414             height: 1,
18415             bbox: empty(),
18416             leaf: true
18417         };
18418         return this;
18419     },
18420
18421     remove: function (item) {
18422         if (!item) return this;
18423
18424         var node = this.data,
18425             bbox = this.toBBox(item),
18426             path = [],
18427             indexes = [],
18428             i, parent, index, goingUp;
18429
18430         // depth-first iterative tree traversal
18431         while (node || path.length) {
18432
18433             if (!node) { // go up
18434                 node = path.pop();
18435                 parent = path[path.length - 1];
18436                 i = indexes.pop();
18437                 goingUp = true;
18438             }
18439
18440             if (node.leaf) { // check current node
18441                 index = node.children.indexOf(item);
18442
18443                 if (index !== -1) {
18444                     // item found, remove the item and condense tree upwards
18445                     node.children.splice(index, 1);
18446                     path.push(node);
18447                     this._condense(path);
18448                     return this;
18449                 }
18450             }
18451
18452             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
18453                 path.push(node);
18454                 indexes.push(i);
18455                 i = 0;
18456                 parent = node;
18457                 node = node.children[0];
18458
18459             } else if (parent) { // go right
18460                 i++;
18461                 node = parent.children[i];
18462                 goingUp = false;
18463
18464             } else node = null; // nothing found
18465         }
18466
18467         return this;
18468     },
18469
18470     toBBox: function (item) { return item; },
18471
18472     compareMinX: function (a, b) { return a[0] - b[0]; },
18473     compareMinY: function (a, b) { return a[1] - b[1]; },
18474
18475     toJSON: function () { return this.data; },
18476
18477     fromJSON: function (data) {
18478         this.data = data;
18479         return this;
18480     },
18481
18482     _all: function (node, result) {
18483         var nodesToSearch = [];
18484         while (node) {
18485             if (node.leaf) result.push.apply(result, node.children);
18486             else nodesToSearch.push.apply(nodesToSearch, node.children);
18487
18488             node = nodesToSearch.pop();
18489         }
18490         return result;
18491     },
18492
18493     _build: function (items, left, right, height) {
18494
18495         var N = right - left + 1,
18496             M = this._maxEntries,
18497             node;
18498
18499         if (N <= M) {
18500             // reached leaf level; return leaf
18501             node = {
18502                 children: items.slice(left, right + 1),
18503                 height: 1,
18504                 bbox: null,
18505                 leaf: true
18506             };
18507             calcBBox(node, this.toBBox);
18508             return node;
18509         }
18510
18511         if (!height) {
18512             // target height of the bulk-loaded tree
18513             height = Math.ceil(Math.log(N) / Math.log(M));
18514
18515             // target number of root entries to maximize storage utilization
18516             M = Math.ceil(N / Math.pow(M, height - 1));
18517         }
18518
18519         node = {
18520             children: [],
18521             height: height,
18522             bbox: null,
18523             leaf: false
18524         };
18525
18526         // split the items into M mostly square tiles
18527
18528         var N2 = Math.ceil(N / M),
18529             N1 = N2 * Math.ceil(Math.sqrt(M)),
18530             i, j, right2, right3;
18531
18532         multiSelect(items, left, right, N1, this.compareMinX);
18533
18534         for (i = left; i <= right; i += N1) {
18535
18536             right2 = Math.min(i + N1 - 1, right);
18537
18538             multiSelect(items, i, right2, N2, this.compareMinY);
18539
18540             for (j = i; j <= right2; j += N2) {
18541
18542                 right3 = Math.min(j + N2 - 1, right2);
18543
18544                 // pack each entry recursively
18545                 node.children.push(this._build(items, j, right3, height - 1));
18546             }
18547         }
18548
18549         calcBBox(node, this.toBBox);
18550
18551         return node;
18552     },
18553
18554     _chooseSubtree: function (bbox, node, level, path) {
18555
18556         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
18557
18558         while (true) {
18559             path.push(node);
18560
18561             if (node.leaf || path.length - 1 === level) break;
18562
18563             minArea = minEnlargement = Infinity;
18564
18565             for (i = 0, len = node.children.length; i < len; i++) {
18566                 child = node.children[i];
18567                 area = bboxArea(child.bbox);
18568                 enlargement = enlargedArea(bbox, child.bbox) - area;
18569
18570                 // choose entry with the least area enlargement
18571                 if (enlargement < minEnlargement) {
18572                     minEnlargement = enlargement;
18573                     minArea = area < minArea ? area : minArea;
18574                     targetNode = child;
18575
18576                 } else if (enlargement === minEnlargement) {
18577                     // otherwise choose one with the smallest area
18578                     if (area < minArea) {
18579                         minArea = area;
18580                         targetNode = child;
18581                     }
18582                 }
18583             }
18584
18585             node = targetNode;
18586         }
18587
18588         return node;
18589     },
18590
18591     _insert: function (item, level, isNode) {
18592
18593         var toBBox = this.toBBox,
18594             bbox = isNode ? item.bbox : toBBox(item),
18595             insertPath = [];
18596
18597         // find the best node for accommodating the item, saving all nodes along the path too
18598         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
18599
18600         // put the item into the node
18601         node.children.push(item);
18602         extend(node.bbox, bbox);
18603
18604         // split on node overflow; propagate upwards if necessary
18605         while (level >= 0) {
18606             if (insertPath[level].children.length > this._maxEntries) {
18607                 this._split(insertPath, level);
18608                 level--;
18609             } else break;
18610         }
18611
18612         // adjust bboxes along the insertion path
18613         this._adjustParentBBoxes(bbox, insertPath, level);
18614     },
18615
18616     // split overflowed node into two
18617     _split: function (insertPath, level) {
18618
18619         var node = insertPath[level],
18620             M = node.children.length,
18621             m = this._minEntries;
18622
18623         this._chooseSplitAxis(node, m, M);
18624
18625         var splitIndex = this._chooseSplitIndex(node, m, M);
18626
18627         var newNode = {
18628             children: node.children.splice(splitIndex, node.children.length - splitIndex),
18629             height: node.height,
18630             bbox: null,
18631             leaf: false
18632         };
18633
18634         if (node.leaf) newNode.leaf = true;
18635
18636         calcBBox(node, this.toBBox);
18637         calcBBox(newNode, this.toBBox);
18638
18639         if (level) insertPath[level - 1].children.push(newNode);
18640         else this._splitRoot(node, newNode);
18641     },
18642
18643     _splitRoot: function (node, newNode) {
18644         // split root node
18645         this.data = {
18646             children: [node, newNode],
18647             height: node.height + 1,
18648             bbox: null,
18649             leaf: false
18650         };
18651         calcBBox(this.data, this.toBBox);
18652     },
18653
18654     _chooseSplitIndex: function (node, m, M) {
18655
18656         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
18657
18658         minOverlap = minArea = Infinity;
18659
18660         for (i = m; i <= M - m; i++) {
18661             bbox1 = distBBox(node, 0, i, this.toBBox);
18662             bbox2 = distBBox(node, i, M, this.toBBox);
18663
18664             overlap = intersectionArea(bbox1, bbox2);
18665             area = bboxArea(bbox1) + bboxArea(bbox2);
18666
18667             // choose distribution with minimum overlap
18668             if (overlap < minOverlap) {
18669                 minOverlap = overlap;
18670                 index = i;
18671
18672                 minArea = area < minArea ? area : minArea;
18673
18674             } else if (overlap === minOverlap) {
18675                 // otherwise choose distribution with minimum area
18676                 if (area < minArea) {
18677                     minArea = area;
18678                     index = i;
18679                 }
18680             }
18681         }
18682
18683         return index;
18684     },
18685
18686     // sorts node children by the best axis for split
18687     _chooseSplitAxis: function (node, m, M) {
18688
18689         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
18690             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
18691             xMargin = this._allDistMargin(node, m, M, compareMinX),
18692             yMargin = this._allDistMargin(node, m, M, compareMinY);
18693
18694         // if total distributions margin value is minimal for x, sort by minX,
18695         // otherwise it's already sorted by minY
18696         if (xMargin < yMargin) node.children.sort(compareMinX);
18697     },
18698
18699     // total margin of all possible split distributions where each node is at least m full
18700     _allDistMargin: function (node, m, M, compare) {
18701
18702         node.children.sort(compare);
18703
18704         var toBBox = this.toBBox,
18705             leftBBox = distBBox(node, 0, m, toBBox),
18706             rightBBox = distBBox(node, M - m, M, toBBox),
18707             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
18708             i, child;
18709
18710         for (i = m; i < M - m; i++) {
18711             child = node.children[i];
18712             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
18713             margin += bboxMargin(leftBBox);
18714         }
18715
18716         for (i = M - m - 1; i >= m; i--) {
18717             child = node.children[i];
18718             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
18719             margin += bboxMargin(rightBBox);
18720         }
18721
18722         return margin;
18723     },
18724
18725     _adjustParentBBoxes: function (bbox, path, level) {
18726         // adjust bboxes along the given tree path
18727         for (var i = level; i >= 0; i--) {
18728             extend(path[i].bbox, bbox);
18729         }
18730     },
18731
18732     _condense: function (path) {
18733         // go through the path, removing empty nodes and updating bboxes
18734         for (var i = path.length - 1, siblings; i >= 0; i--) {
18735             if (path[i].children.length === 0) {
18736                 if (i > 0) {
18737                     siblings = path[i - 1].children;
18738                     siblings.splice(siblings.indexOf(path[i]), 1);
18739
18740                 } else this.clear();
18741
18742             } else calcBBox(path[i], this.toBBox);
18743         }
18744     },
18745
18746     _initFormat: function (format) {
18747         // data format (minX, minY, maxX, maxY accessors)
18748
18749         // uses eval-type function compilation instead of just accepting a toBBox function
18750         // because the algorithms are very sensitive to sorting functions performance,
18751         // so they should be dead simple and without inner calls
18752
18753         // jshint evil: true
18754
18755         var compareArr = ['return a', ' - b', ';'];
18756
18757         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
18758         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
18759
18760         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
18761     }
18762 };
18763
18764
18765 // calculate node's bbox from bboxes of its children
18766 function calcBBox(node, toBBox) {
18767     node.bbox = distBBox(node, 0, node.children.length, toBBox);
18768 }
18769
18770 // min bounding rectangle of node children from k to p-1
18771 function distBBox(node, k, p, toBBox) {
18772     var bbox = empty();
18773
18774     for (var i = k, child; i < p; i++) {
18775         child = node.children[i];
18776         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
18777     }
18778
18779     return bbox;
18780 }
18781
18782 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
18783
18784 function extend(a, b) {
18785     a[0] = Math.min(a[0], b[0]);
18786     a[1] = Math.min(a[1], b[1]);
18787     a[2] = Math.max(a[2], b[2]);
18788     a[3] = Math.max(a[3], b[3]);
18789     return a;
18790 }
18791
18792 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
18793 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
18794
18795 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
18796 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
18797
18798 function enlargedArea(a, b) {
18799     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
18800            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
18801 }
18802
18803 function intersectionArea(a, b) {
18804     var minX = Math.max(a[0], b[0]),
18805         minY = Math.max(a[1], b[1]),
18806         maxX = Math.min(a[2], b[2]),
18807         maxY = Math.min(a[3], b[3]);
18808
18809     return Math.max(0, maxX - minX) *
18810            Math.max(0, maxY - minY);
18811 }
18812
18813 function contains(a, b) {
18814     return a[0] <= b[0] &&
18815            a[1] <= b[1] &&
18816            b[2] <= a[2] &&
18817            b[3] <= a[3];
18818 }
18819
18820 function intersects(a, b) {
18821     return b[0] <= a[2] &&
18822            b[1] <= a[3] &&
18823            b[2] >= a[0] &&
18824            b[3] >= a[1];
18825 }
18826
18827 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
18828 // combines selection algorithm with binary divide & conquer approach
18829
18830 function multiSelect(arr, left, right, n, compare) {
18831     var stack = [left, right],
18832         mid;
18833
18834     while (stack.length) {
18835         right = stack.pop();
18836         left = stack.pop();
18837
18838         if (right - left <= n) continue;
18839
18840         mid = left + Math.ceil((right - left) / n / 2) * n;
18841         select(arr, left, right, mid, compare);
18842
18843         stack.push(left, mid, mid, right);
18844     }
18845 }
18846
18847 // Floyd-Rivest selection algorithm:
18848 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
18849 function select(arr, left, right, k, compare) {
18850     var n, i, z, s, sd, newLeft, newRight, t, j;
18851
18852     while (right > left) {
18853         if (right - left > 600) {
18854             n = right - left + 1;
18855             i = k - left + 1;
18856             z = Math.log(n);
18857             s = 0.5 * Math.exp(2 * z / 3);
18858             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
18859             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
18860             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
18861             select(arr, newLeft, newRight, k, compare);
18862         }
18863
18864         t = arr[k];
18865         i = left;
18866         j = right;
18867
18868         swap(arr, left, k);
18869         if (compare(arr[right], t) > 0) swap(arr, left, right);
18870
18871         while (i < j) {
18872             swap(arr, i, j);
18873             i++;
18874             j--;
18875             while (compare(arr[i], t) < 0) i++;
18876             while (compare(arr[j], t) > 0) j--;
18877         }
18878
18879         if (compare(arr[left], t) === 0) swap(arr, left, j);
18880         else {
18881             j++;
18882             swap(arr, j, right);
18883         }
18884
18885         if (j <= k) left = j + 1;
18886         if (k <= j) right = j - 1;
18887     }
18888 }
18889
18890 function swap(arr, i, j) {
18891     var tmp = arr[i];
18892     arr[i] = arr[j];
18893     arr[j] = tmp;
18894 }
18895
18896
18897 // export as AMD/CommonJS module or global variable
18898 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
18899 else if (typeof module !== 'undefined') module.exports = rbush;
18900 else if (typeof self !== 'undefined') self.rbush = rbush;
18901 else window.rbush = rbush;
18902
18903 })();
18904 (function(e){if("function"==typeof bootstrap)bootstrap("sexagesimal",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeSexagesimal=e}else"undefined"!=typeof window?window.sexagesimal=e():global.sexagesimal=e()})(function(){var define,ses,bootstrap,module,exports;
18905 return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
18906 module.exports = element;
18907 module.exports.pair = pair;
18908 module.exports.format = format;
18909 module.exports.formatPair = formatPair;
18910
18911 function element(x, dims) {
18912     return search(x, dims).val;
18913 }
18914
18915 function formatPair(x) {
18916     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
18917 }
18918
18919 // Is 0 North or South?
18920 function format(x, dim) {
18921     var dirs = {
18922             lat: ['N', 'S'],
18923             lon: ['E', 'W']
18924         }[dim] || '',
18925         dir = dirs[x >= 0 ? 0 : 1],
18926         abs = Math.abs(x),
18927         whole = Math.floor(abs),
18928         fraction = abs - whole,
18929         fractionMinutes = fraction * 60,
18930         minutes = Math.floor(fractionMinutes),
18931         seconds = Math.floor((fractionMinutes - minutes) * 60);
18932
18933     return whole + '° ' +
18934         (minutes ? minutes + "' " : '') +
18935         (seconds ? seconds + '" ' : '') + dir;
18936 }
18937
18938 function search(x, dims, r) {
18939     if (!dims) dims = 'NSEW';
18940     if (typeof x !== 'string') return { val: null, regex: r };
18941     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
18942     var m = r.exec(x);
18943     if (!m) return { val: null, regex: r };
18944     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
18945     else return {
18946         val: (((m[1]) ? parseFloat(m[1]) : 0) +
18947             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
18948             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
18949             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
18950         regex: r,
18951         raw: m[0],
18952         dim: m[4]
18953     };
18954 }
18955
18956 function pair(x, dims) {
18957     x = x.trim();
18958     var one = search(x, dims);
18959     if (one.val === null) return null;
18960     var two = search(x, dims, one.regex);
18961     if (two.val === null) return null;
18962     // null if one/two are not contiguous.
18963     if (one.raw + two.raw !== x) return null;
18964     if (one.dim) return swapdim(one.val, two.val, one.dim);
18965     else return [one.val, two.val];
18966 }
18967
18968 function swapdim(a, b, dim) {
18969     if (dim == 'N' || dim == 'S') return [a, b];
18970     if (dim == 'W' || dim == 'E') return [b, a];
18971 }
18972
18973 },{}]},{},[1])
18974 (1)
18975 });
18976 ;toGeoJSON = (function() {
18977     'use strict';
18978
18979     var removeSpace = (/\s*/g),
18980         trimSpace = (/^\s*|\s*$/g),
18981         splitSpace = (/\s+/);
18982     // generate a short, numeric hash of a string
18983     function okhash(x) {
18984         if (!x || !x.length) return 0;
18985         for (var i = 0, h = 0; i < x.length; i++) {
18986             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
18987         } return h;
18988     }
18989     // all Y children of X
18990     function get(x, y) { return x.getElementsByTagName(y); }
18991     function attr(x, y) { return x.getAttribute(y); }
18992     function attrf(x, y) { return parseFloat(attr(x, y)); }
18993     // one Y child of X, if any, otherwise null
18994     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
18995     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
18996     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
18997     // cast array x into numbers
18998     function numarray(x) {
18999         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
19000         return o;
19001     }
19002     function clean(x) {
19003         var o = {};
19004         for (var i in x) if (x[i]) o[i] = x[i];
19005         return o;
19006     }
19007     // get the content of a text node, if any
19008     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
19009     // get one coordinate from a coordinate array, if any
19010     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
19011     // get all coordinates from a coordinate array as [[],[]]
19012     function coord(v) {
19013         var coords = v.replace(trimSpace, '').split(splitSpace),
19014             o = [];
19015         for (var i = 0; i < coords.length; i++) {
19016             o.push(coord1(coords[i]));
19017         }
19018         return o;
19019     }
19020     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
19021
19022     // create a new feature collection parent object
19023     function fc() {
19024         return {
19025             type: 'FeatureCollection',
19026             features: []
19027         };
19028     }
19029
19030     var styleSupport = false;
19031     if (typeof XMLSerializer !== 'undefined') {
19032         var serializer = new XMLSerializer();
19033         styleSupport = true;
19034     }
19035     function xml2str(str) { return serializer.serializeToString(str); }
19036
19037     var t = {
19038         kml: function(doc, o) {
19039             o = o || {};
19040
19041             var gj = fc(),
19042                 // styleindex keeps track of hashed styles in order to match features
19043                 styleIndex = {},
19044                 // atomic geospatial types supported by KML - MultiGeometry is
19045                 // handled separately
19046                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
19047                 // all root placemarks in the file
19048                 placemarks = get(doc, 'Placemark'),
19049                 styles = get(doc, 'Style');
19050
19051             if (styleSupport) for (var k = 0; k < styles.length; k++) {
19052                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
19053             }
19054             for (var j = 0; j < placemarks.length; j++) {
19055                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
19056             }
19057             function gxCoord(v) { return numarray(v.split(' ')); }
19058             function gxCoords(root) {
19059                 var elems = get(root, 'coord', 'gx'), coords = [];
19060                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
19061                 return coords;
19062             }
19063             function getGeometry(root) {
19064                 var geomNode, geomNodes, i, j, k, geoms = [];
19065                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
19066                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
19067                 for (i = 0; i < geotypes.length; i++) {
19068                     geomNodes = get(root, geotypes[i]);
19069                     if (geomNodes) {
19070                         for (j = 0; j < geomNodes.length; j++) {
19071                             geomNode = geomNodes[j];
19072                             if (geotypes[i] == 'Point') {
19073                                 geoms.push({
19074                                     type: 'Point',
19075                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
19076                                 });
19077                             } else if (geotypes[i] == 'LineString') {
19078                                 geoms.push({
19079                                     type: 'LineString',
19080                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
19081                                 });
19082                             } else if (geotypes[i] == 'Polygon') {
19083                                 var rings = get(geomNode, 'LinearRing'),
19084                                     coords = [];
19085                                 for (k = 0; k < rings.length; k++) {
19086                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
19087                                 }
19088                                 geoms.push({
19089                                     type: 'Polygon',
19090                                     coordinates: coords
19091                                 });
19092                             } else if (geotypes[i] == 'Track') {
19093                                 geoms.push({
19094                                     type: 'LineString',
19095                                     coordinates: gxCoords(geomNode)
19096                                 });
19097                             }
19098                         }
19099                     }
19100                 }
19101                 return geoms;
19102             }
19103             function getPlacemark(root) {
19104                 var geoms = getGeometry(root), i, properties = {},
19105                     name = nodeVal(get1(root, 'name')),
19106                     styleUrl = nodeVal(get1(root, 'styleUrl')),
19107                     description = nodeVal(get1(root, 'description')),
19108                     extendedData = get1(root, 'ExtendedData');
19109
19110                 if (!geoms.length) return [];
19111                 if (name) properties.name = name;
19112                 if (styleUrl && styleIndex[styleUrl]) {
19113                     properties.styleUrl = styleUrl;
19114                     properties.styleHash = styleIndex[styleUrl];
19115                 }
19116                 if (description) properties.description = description;
19117                 if (extendedData) {
19118                     var datas = get(extendedData, 'Data'),
19119                         simpleDatas = get(extendedData, 'SimpleData');
19120
19121                     for (i = 0; i < datas.length; i++) {
19122                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
19123                     }
19124                     for (i = 0; i < simpleDatas.length; i++) {
19125                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
19126                     }
19127                 }
19128                 return [{
19129                     type: 'Feature',
19130                     geometry: (geoms.length === 1) ? geoms[0] : {
19131                         type: 'GeometryCollection',
19132                         geometries: geoms
19133                     },
19134                     properties: properties
19135                 }];
19136             }
19137             return gj;
19138         },
19139         gpx: function(doc, o) {
19140             var i,
19141                 tracks = get(doc, 'trk'),
19142                 routes = get(doc, 'rte'),
19143                 waypoints = get(doc, 'wpt'),
19144                 // a feature collection
19145                 gj = fc();
19146             for (i = 0; i < tracks.length; i++) {
19147                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
19148             }
19149             for (i = 0; i < routes.length; i++) {
19150                 gj.features.push(getLinestring(routes[i], 'rtept'));
19151             }
19152             for (i = 0; i < waypoints.length; i++) {
19153                 gj.features.push(getPoint(waypoints[i]));
19154             }
19155             function getLinestring(node, pointname) {
19156                 var j, pts = get(node, pointname), line = [];
19157                 for (j = 0; j < pts.length; j++) {
19158                     line.push(coordPair(pts[j]));
19159                 }
19160                 return {
19161                     type: 'Feature',
19162                     properties: getProperties(node),
19163                     geometry: {
19164                         type: 'LineString',
19165                         coordinates: line
19166                     }
19167                 };
19168             }
19169             function getPoint(node) {
19170                 var prop = getProperties(node);
19171                 prop.ele = nodeVal(get1(node, 'ele'));
19172                 prop.sym = nodeVal(get1(node, 'sym'));
19173                 return {
19174                     type: 'Feature',
19175                     properties: prop,
19176                     geometry: {
19177                         type: 'Point',
19178                         coordinates: coordPair(node)
19179                     }
19180                 };
19181             }
19182             function getProperties(node) {
19183                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
19184                             'time', 'keywords'],
19185                     prop = {},
19186                     k;
19187                 for (k = 0; k < meta.length; k++) {
19188                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
19189                 }
19190                 return clean(prop);
19191             }
19192             return gj;
19193         }
19194     };
19195     return t;
19196 })();
19197
19198 if (typeof module !== 'undefined') module.exports = toGeoJSON;
19199 /**
19200  * marked - a markdown parser
19201  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
19202  * https://github.com/chjj/marked
19203  */
19204
19205 ;(function() {
19206
19207 /**
19208  * Block-Level Grammar
19209  */
19210
19211 var block = {
19212   newline: /^\n+/,
19213   code: /^( {4}[^\n]+\n*)+/,
19214   fences: noop,
19215   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
19216   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19217   nptable: noop,
19218   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
19219   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
19220   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
19221   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
19222   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
19223   table: noop,
19224   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
19225   text: /^[^\n]+/
19226 };
19227
19228 block.bullet = /(?:[*+-]|\d+\.)/;
19229 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
19230 block.item = replace(block.item, 'gm')
19231   (/bull/g, block.bullet)
19232   ();
19233
19234 block.list = replace(block.list)
19235   (/bull/g, block.bullet)
19236   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
19237   ();
19238
19239 block._tag = '(?!(?:'
19240   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
19241   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
19242   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
19243
19244 block.html = replace(block.html)
19245   ('comment', /<!--[\s\S]*?-->/)
19246   ('closed', /<(tag)[\s\S]+?<\/\1>/)
19247   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
19248   (/tag/g, block._tag)
19249   ();
19250
19251 block.paragraph = replace(block.paragraph)
19252   ('hr', block.hr)
19253   ('heading', block.heading)
19254   ('lheading', block.lheading)
19255   ('blockquote', block.blockquote)
19256   ('tag', '<' + block._tag)
19257   ('def', block.def)
19258   ();
19259
19260 /**
19261  * Normal Block Grammar
19262  */
19263
19264 block.normal = merge({}, block);
19265
19266 /**
19267  * GFM Block Grammar
19268  */
19269
19270 block.gfm = merge({}, block.normal, {
19271   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
19272   paragraph: /^/
19273 });
19274
19275 block.gfm.paragraph = replace(block.paragraph)
19276   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
19277   ();
19278
19279 /**
19280  * GFM + Tables Block Grammar
19281  */
19282
19283 block.tables = merge({}, block.gfm, {
19284   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
19285   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
19286 });
19287
19288 /**
19289  * Block Lexer
19290  */
19291
19292 function Lexer(options) {
19293   this.tokens = [];
19294   this.tokens.links = {};
19295   this.options = options || marked.defaults;
19296   this.rules = block.normal;
19297
19298   if (this.options.gfm) {
19299     if (this.options.tables) {
19300       this.rules = block.tables;
19301     } else {
19302       this.rules = block.gfm;
19303     }
19304   }
19305 }
19306
19307 /**
19308  * Expose Block Rules
19309  */
19310
19311 Lexer.rules = block;
19312
19313 /**
19314  * Static Lex Method
19315  */
19316
19317 Lexer.lex = function(src, options) {
19318   var lexer = new Lexer(options);
19319   return lexer.lex(src);
19320 };
19321
19322 /**
19323  * Preprocessing
19324  */
19325
19326 Lexer.prototype.lex = function(src) {
19327   src = src
19328     .replace(/\r\n|\r/g, '\n')
19329     .replace(/\t/g, '    ')
19330     .replace(/\u00a0/g, ' ')
19331     .replace(/\u2424/g, '\n');
19332
19333   return this.token(src, true);
19334 };
19335
19336 /**
19337  * Lexing
19338  */
19339
19340 Lexer.prototype.token = function(src, top) {
19341   var src = src.replace(/^ +$/gm, '')
19342     , next
19343     , loose
19344     , cap
19345     , bull
19346     , b
19347     , item
19348     , space
19349     , i
19350     , l;
19351
19352   while (src) {
19353     // newline
19354     if (cap = this.rules.newline.exec(src)) {
19355       src = src.substring(cap[0].length);
19356       if (cap[0].length > 1) {
19357         this.tokens.push({
19358           type: 'space'
19359         });
19360       }
19361     }
19362
19363     // code
19364     if (cap = this.rules.code.exec(src)) {
19365       src = src.substring(cap[0].length);
19366       cap = cap[0].replace(/^ {4}/gm, '');
19367       this.tokens.push({
19368         type: 'code',
19369         text: !this.options.pedantic
19370           ? cap.replace(/\n+$/, '')
19371           : cap
19372       });
19373       continue;
19374     }
19375
19376     // fences (gfm)
19377     if (cap = this.rules.fences.exec(src)) {
19378       src = src.substring(cap[0].length);
19379       this.tokens.push({
19380         type: 'code',
19381         lang: cap[2],
19382         text: cap[3]
19383       });
19384       continue;
19385     }
19386
19387     // heading
19388     if (cap = this.rules.heading.exec(src)) {
19389       src = src.substring(cap[0].length);
19390       this.tokens.push({
19391         type: 'heading',
19392         depth: cap[1].length,
19393         text: cap[2]
19394       });
19395       continue;
19396     }
19397
19398     // table no leading pipe (gfm)
19399     if (top && (cap = this.rules.nptable.exec(src))) {
19400       src = src.substring(cap[0].length);
19401
19402       item = {
19403         type: 'table',
19404         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19405         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19406         cells: cap[3].replace(/\n$/, '').split('\n')
19407       };
19408
19409       for (i = 0; i < item.align.length; i++) {
19410         if (/^ *-+: *$/.test(item.align[i])) {
19411           item.align[i] = 'right';
19412         } else if (/^ *:-+: *$/.test(item.align[i])) {
19413           item.align[i] = 'center';
19414         } else if (/^ *:-+ *$/.test(item.align[i])) {
19415           item.align[i] = 'left';
19416         } else {
19417           item.align[i] = null;
19418         }
19419       }
19420
19421       for (i = 0; i < item.cells.length; i++) {
19422         item.cells[i] = item.cells[i].split(/ *\| */);
19423       }
19424
19425       this.tokens.push(item);
19426
19427       continue;
19428     }
19429
19430     // lheading
19431     if (cap = this.rules.lheading.exec(src)) {
19432       src = src.substring(cap[0].length);
19433       this.tokens.push({
19434         type: 'heading',
19435         depth: cap[2] === '=' ? 1 : 2,
19436         text: cap[1]
19437       });
19438       continue;
19439     }
19440
19441     // hr
19442     if (cap = this.rules.hr.exec(src)) {
19443       src = src.substring(cap[0].length);
19444       this.tokens.push({
19445         type: 'hr'
19446       });
19447       continue;
19448     }
19449
19450     // blockquote
19451     if (cap = this.rules.blockquote.exec(src)) {
19452       src = src.substring(cap[0].length);
19453
19454       this.tokens.push({
19455         type: 'blockquote_start'
19456       });
19457
19458       cap = cap[0].replace(/^ *> ?/gm, '');
19459
19460       // Pass `top` to keep the current
19461       // "toplevel" state. This is exactly
19462       // how markdown.pl works.
19463       this.token(cap, top);
19464
19465       this.tokens.push({
19466         type: 'blockquote_end'
19467       });
19468
19469       continue;
19470     }
19471
19472     // list
19473     if (cap = this.rules.list.exec(src)) {
19474       src = src.substring(cap[0].length);
19475       bull = cap[2];
19476
19477       this.tokens.push({
19478         type: 'list_start',
19479         ordered: bull.length > 1
19480       });
19481
19482       // Get each top-level item.
19483       cap = cap[0].match(this.rules.item);
19484
19485       next = false;
19486       l = cap.length;
19487       i = 0;
19488
19489       for (; i < l; i++) {
19490         item = cap[i];
19491
19492         // Remove the list item's bullet
19493         // so it is seen as the next token.
19494         space = item.length;
19495         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19496
19497         // Outdent whatever the
19498         // list item contains. Hacky.
19499         if (~item.indexOf('\n ')) {
19500           space -= item.length;
19501           item = !this.options.pedantic
19502             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19503             : item.replace(/^ {1,4}/gm, '');
19504         }
19505
19506         // Determine whether the next list item belongs here.
19507         // Backpedal if it does not belong in this list.
19508         if (this.options.smartLists && i !== l - 1) {
19509           b = block.bullet.exec(cap[i+1])[0];
19510           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19511             src = cap.slice(i + 1).join('\n') + src;
19512             i = l - 1;
19513           }
19514         }
19515
19516         // Determine whether item is loose or not.
19517         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19518         // for discount behavior.
19519         loose = next || /\n\n(?!\s*$)/.test(item);
19520         if (i !== l - 1) {
19521           next = item[item.length-1] === '\n';
19522           if (!loose) loose = next;
19523         }
19524
19525         this.tokens.push({
19526           type: loose
19527             ? 'loose_item_start'
19528             : 'list_item_start'
19529         });
19530
19531         // Recurse.
19532         this.token(item, false);
19533
19534         this.tokens.push({
19535           type: 'list_item_end'
19536         });
19537       }
19538
19539       this.tokens.push({
19540         type: 'list_end'
19541       });
19542
19543       continue;
19544     }
19545
19546     // html
19547     if (cap = this.rules.html.exec(src)) {
19548       src = src.substring(cap[0].length);
19549       this.tokens.push({
19550         type: this.options.sanitize
19551           ? 'paragraph'
19552           : 'html',
19553         pre: cap[1] === 'pre' || cap[1] === 'script',
19554         text: cap[0]
19555       });
19556       continue;
19557     }
19558
19559     // def
19560     if (top && (cap = this.rules.def.exec(src))) {
19561       src = src.substring(cap[0].length);
19562       this.tokens.links[cap[1].toLowerCase()] = {
19563         href: cap[2],
19564         title: cap[3]
19565       };
19566       continue;
19567     }
19568
19569     // table (gfm)
19570     if (top && (cap = this.rules.table.exec(src))) {
19571       src = src.substring(cap[0].length);
19572
19573       item = {
19574         type: 'table',
19575         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19576         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19577         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19578       };
19579
19580       for (i = 0; i < item.align.length; i++) {
19581         if (/^ *-+: *$/.test(item.align[i])) {
19582           item.align[i] = 'right';
19583         } else if (/^ *:-+: *$/.test(item.align[i])) {
19584           item.align[i] = 'center';
19585         } else if (/^ *:-+ *$/.test(item.align[i])) {
19586           item.align[i] = 'left';
19587         } else {
19588           item.align[i] = null;
19589         }
19590       }
19591
19592       for (i = 0; i < item.cells.length; i++) {
19593         item.cells[i] = item.cells[i]
19594           .replace(/^ *\| *| *\| *$/g, '')
19595           .split(/ *\| */);
19596       }
19597
19598       this.tokens.push(item);
19599
19600       continue;
19601     }
19602
19603     // top-level paragraph
19604     if (top && (cap = this.rules.paragraph.exec(src))) {
19605       src = src.substring(cap[0].length);
19606       this.tokens.push({
19607         type: 'paragraph',
19608         text: cap[1][cap[1].length-1] === '\n'
19609           ? cap[1].slice(0, -1)
19610           : cap[1]
19611       });
19612       continue;
19613     }
19614
19615     // text
19616     if (cap = this.rules.text.exec(src)) {
19617       // Top-level should never reach here.
19618       src = src.substring(cap[0].length);
19619       this.tokens.push({
19620         type: 'text',
19621         text: cap[0]
19622       });
19623       continue;
19624     }
19625
19626     if (src) {
19627       throw new
19628         Error('Infinite loop on byte: ' + src.charCodeAt(0));
19629     }
19630   }
19631
19632   return this.tokens;
19633 };
19634
19635 /**
19636  * Inline-Level Grammar
19637  */
19638
19639 var inline = {
19640   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19641   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19642   url: noop,
19643   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19644   link: /^!?\[(inside)\]\(href\)/,
19645   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19646   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19647   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19648   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19649   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19650   br: /^ {2,}\n(?!\s*$)/,
19651   del: noop,
19652   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19653 };
19654
19655 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
19656 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19657
19658 inline.link = replace(inline.link)
19659   ('inside', inline._inside)
19660   ('href', inline._href)
19661   ();
19662
19663 inline.reflink = replace(inline.reflink)
19664   ('inside', inline._inside)
19665   ();
19666
19667 /**
19668  * Normal Inline Grammar
19669  */
19670
19671 inline.normal = merge({}, inline);
19672
19673 /**
19674  * Pedantic Inline Grammar
19675  */
19676
19677 inline.pedantic = merge({}, inline.normal, {
19678   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19679   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19680 });
19681
19682 /**
19683  * GFM Inline Grammar
19684  */
19685
19686 inline.gfm = merge({}, inline.normal, {
19687   escape: replace(inline.escape)('])', '~|])')(),
19688   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19689   del: /^~~(?=\S)([\s\S]*?\S)~~/,
19690   text: replace(inline.text)
19691     (']|', '~]|')
19692     ('|', '|https?://|')
19693     ()
19694 });
19695
19696 /**
19697  * GFM + Line Breaks Inline Grammar
19698  */
19699
19700 inline.breaks = merge({}, inline.gfm, {
19701   br: replace(inline.br)('{2,}', '*')(),
19702   text: replace(inline.gfm.text)('{2,}', '*')()
19703 });
19704
19705 /**
19706  * Inline Lexer & Compiler
19707  */
19708
19709 function InlineLexer(links, options) {
19710   this.options = options || marked.defaults;
19711   this.links = links;
19712   this.rules = inline.normal;
19713
19714   if (!this.links) {
19715     throw new
19716       Error('Tokens array requires a `links` property.');
19717   }
19718
19719   if (this.options.gfm) {
19720     if (this.options.breaks) {
19721       this.rules = inline.breaks;
19722     } else {
19723       this.rules = inline.gfm;
19724     }
19725   } else if (this.options.pedantic) {
19726     this.rules = inline.pedantic;
19727   }
19728 }
19729
19730 /**
19731  * Expose Inline Rules
19732  */
19733
19734 InlineLexer.rules = inline;
19735
19736 /**
19737  * Static Lexing/Compiling Method
19738  */
19739
19740 InlineLexer.output = function(src, links, options) {
19741   var inline = new InlineLexer(links, options);
19742   return inline.output(src);
19743 };
19744
19745 /**
19746  * Lexing/Compiling
19747  */
19748
19749 InlineLexer.prototype.output = function(src) {
19750   var out = ''
19751     , link
19752     , text
19753     , href
19754     , cap;
19755
19756   while (src) {
19757     // escape
19758     if (cap = this.rules.escape.exec(src)) {
19759       src = src.substring(cap[0].length);
19760       out += cap[1];
19761       continue;
19762     }
19763
19764     // autolink
19765     if (cap = this.rules.autolink.exec(src)) {
19766       src = src.substring(cap[0].length);
19767       if (cap[2] === '@') {
19768         text = cap[1][6] === ':'
19769           ? this.mangle(cap[1].substring(7))
19770           : this.mangle(cap[1]);
19771         href = this.mangle('mailto:') + text;
19772       } else {
19773         text = escape(cap[1]);
19774         href = text;
19775       }
19776       out += '<a href="'
19777         + href
19778         + '">'
19779         + text
19780         + '</a>';
19781       continue;
19782     }
19783
19784     // url (gfm)
19785     if (cap = this.rules.url.exec(src)) {
19786       src = src.substring(cap[0].length);
19787       text = escape(cap[1]);
19788       href = text;
19789       out += '<a href="'
19790         + href
19791         + '">'
19792         + text
19793         + '</a>';
19794       continue;
19795     }
19796
19797     // tag
19798     if (cap = this.rules.tag.exec(src)) {
19799       src = src.substring(cap[0].length);
19800       out += this.options.sanitize
19801         ? escape(cap[0])
19802         : cap[0];
19803       continue;
19804     }
19805
19806     // link
19807     if (cap = this.rules.link.exec(src)) {
19808       src = src.substring(cap[0].length);
19809       out += this.outputLink(cap, {
19810         href: cap[2],
19811         title: cap[3]
19812       });
19813       continue;
19814     }
19815
19816     // reflink, nolink
19817     if ((cap = this.rules.reflink.exec(src))
19818         || (cap = this.rules.nolink.exec(src))) {
19819       src = src.substring(cap[0].length);
19820       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19821       link = this.links[link.toLowerCase()];
19822       if (!link || !link.href) {
19823         out += cap[0][0];
19824         src = cap[0].substring(1) + src;
19825         continue;
19826       }
19827       out += this.outputLink(cap, link);
19828       continue;
19829     }
19830
19831     // strong
19832     if (cap = this.rules.strong.exec(src)) {
19833       src = src.substring(cap[0].length);
19834       out += '<strong>'
19835         + this.output(cap[2] || cap[1])
19836         + '</strong>';
19837       continue;
19838     }
19839
19840     // em
19841     if (cap = this.rules.em.exec(src)) {
19842       src = src.substring(cap[0].length);
19843       out += '<em>'
19844         + this.output(cap[2] || cap[1])
19845         + '</em>';
19846       continue;
19847     }
19848
19849     // code
19850     if (cap = this.rules.code.exec(src)) {
19851       src = src.substring(cap[0].length);
19852       out += '<code>'
19853         + escape(cap[2], true)
19854         + '</code>';
19855       continue;
19856     }
19857
19858     // br
19859     if (cap = this.rules.br.exec(src)) {
19860       src = src.substring(cap[0].length);
19861       out += '<br>';
19862       continue;
19863     }
19864
19865     // del (gfm)
19866     if (cap = this.rules.del.exec(src)) {
19867       src = src.substring(cap[0].length);
19868       out += '<del>'
19869         + this.output(cap[1])
19870         + '</del>';
19871       continue;
19872     }
19873
19874     // text
19875     if (cap = this.rules.text.exec(src)) {
19876       src = src.substring(cap[0].length);
19877       out += escape(cap[0]);
19878       continue;
19879     }
19880
19881     if (src) {
19882       throw new
19883         Error('Infinite loop on byte: ' + src.charCodeAt(0));
19884     }
19885   }
19886
19887   return out;
19888 };
19889
19890 /**
19891  * Compile Link
19892  */
19893
19894 InlineLexer.prototype.outputLink = function(cap, link) {
19895   if (cap[0][0] !== '!') {
19896     return '<a href="'
19897       + escape(link.href)
19898       + '"'
19899       + (link.title
19900       ? ' title="'
19901       + escape(link.title)
19902       + '"'
19903       : '')
19904       + '>'
19905       + this.output(cap[1])
19906       + '</a>';
19907   } else {
19908     return '<img src="'
19909       + escape(link.href)
19910       + '" alt="'
19911       + escape(cap[1])
19912       + '"'
19913       + (link.title
19914       ? ' title="'
19915       + escape(link.title)
19916       + '"'
19917       : '')
19918       + '>';
19919   }
19920 };
19921
19922 /**
19923  * Smartypants Transformations
19924  */
19925
19926 InlineLexer.prototype.smartypants = function(text) {
19927   if (!this.options.smartypants) return text;
19928   return text
19929     .replace(/--/g, '—')
19930     .replace(/'([^']*)'/g, '‘$1’')
19931     .replace(/"([^"]*)"/g, '“$1”')
19932     .replace(/\.{3}/g, '…');
19933 };
19934
19935 /**
19936  * Mangle Links
19937  */
19938
19939 InlineLexer.prototype.mangle = function(text) {
19940   var out = ''
19941     , l = text.length
19942     , i = 0
19943     , ch;
19944
19945   for (; i < l; i++) {
19946     ch = text.charCodeAt(i);
19947     if (Math.random() > 0.5) {
19948       ch = 'x' + ch.toString(16);
19949     }
19950     out += '&#' + ch + ';';
19951   }
19952
19953   return out;
19954 };
19955
19956 /**
19957  * Parsing & Compiling
19958  */
19959
19960 function Parser(options) {
19961   this.tokens = [];
19962   this.token = null;
19963   this.options = options || marked.defaults;
19964 }
19965
19966 /**
19967  * Static Parse Method
19968  */
19969
19970 Parser.parse = function(src, options) {
19971   var parser = new Parser(options);
19972   return parser.parse(src);
19973 };
19974
19975 /**
19976  * Parse Loop
19977  */
19978
19979 Parser.prototype.parse = function(src) {
19980   this.inline = new InlineLexer(src.links, this.options);
19981   this.tokens = src.reverse();
19982
19983   var out = '';
19984   while (this.next()) {
19985     out += this.tok();
19986   }
19987
19988   return out;
19989 };
19990
19991 /**
19992  * Next Token
19993  */
19994
19995 Parser.prototype.next = function() {
19996   return this.token = this.tokens.pop();
19997 };
19998
19999 /**
20000  * Preview Next Token
20001  */
20002
20003 Parser.prototype.peek = function() {
20004   return this.tokens[this.tokens.length-1] || 0;
20005 };
20006
20007 /**
20008  * Parse Text Tokens
20009  */
20010
20011 Parser.prototype.parseText = function() {
20012   var body = this.token.text;
20013
20014   while (this.peek().type === 'text') {
20015     body += '\n' + this.next().text;
20016   }
20017
20018   return this.inline.output(body);
20019 };
20020
20021 /**
20022  * Parse Current Token
20023  */
20024
20025 Parser.prototype.tok = function() {
20026   switch (this.token.type) {
20027     case 'space': {
20028       return '';
20029     }
20030     case 'hr': {
20031       return '<hr>\n';
20032     }
20033     case 'heading': {
20034       return '<h'
20035         + this.token.depth
20036         + '>'
20037         + this.inline.output(this.token.text)
20038         + '</h'
20039         + this.token.depth
20040         + '>\n';
20041     }
20042     case 'code': {
20043       if (this.options.highlight) {
20044         var code = this.options.highlight(this.token.text, this.token.lang);
20045         if (code != null && code !== this.token.text) {
20046           this.token.escaped = true;
20047           this.token.text = code;
20048         }
20049       }
20050
20051       if (!this.token.escaped) {
20052         this.token.text = escape(this.token.text, true);
20053       }
20054
20055       return '<pre><code'
20056         + (this.token.lang
20057         ? ' class="'
20058         + this.options.langPrefix
20059         + this.token.lang
20060         + '"'
20061         : '')
20062         + '>'
20063         + this.token.text
20064         + '</code></pre>\n';
20065     }
20066     case 'table': {
20067       var body = ''
20068         , heading
20069         , i
20070         , row
20071         , cell
20072         , j;
20073
20074       // header
20075       body += '<thead>\n<tr>\n';
20076       for (i = 0; i < this.token.header.length; i++) {
20077         heading = this.inline.output(this.token.header[i]);
20078         body += this.token.align[i]
20079           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
20080           : '<th>' + heading + '</th>\n';
20081       }
20082       body += '</tr>\n</thead>\n';
20083
20084       // body
20085       body += '<tbody>\n'
20086       for (i = 0; i < this.token.cells.length; i++) {
20087         row = this.token.cells[i];
20088         body += '<tr>\n';
20089         for (j = 0; j < row.length; j++) {
20090           cell = this.inline.output(row[j]);
20091           body += this.token.align[j]
20092             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
20093             : '<td>' + cell + '</td>\n';
20094         }
20095         body += '</tr>\n';
20096       }
20097       body += '</tbody>\n';
20098
20099       return '<table>\n'
20100         + body
20101         + '</table>\n';
20102     }
20103     case 'blockquote_start': {
20104       var body = '';
20105
20106       while (this.next().type !== 'blockquote_end') {
20107         body += this.tok();
20108       }
20109
20110       return '<blockquote>\n'
20111         + body
20112         + '</blockquote>\n';
20113     }
20114     case 'list_start': {
20115       var type = this.token.ordered ? 'ol' : 'ul'
20116         , body = '';
20117
20118       while (this.next().type !== 'list_end') {
20119         body += this.tok();
20120       }
20121
20122       return '<'
20123         + type
20124         + '>\n'
20125         + body
20126         + '</'
20127         + type
20128         + '>\n';
20129     }
20130     case 'list_item_start': {
20131       var body = '';
20132
20133       while (this.next().type !== 'list_item_end') {
20134         body += this.token.type === 'text'
20135           ? this.parseText()
20136           : this.tok();
20137       }
20138
20139       return '<li>'
20140         + body
20141         + '</li>\n';
20142     }
20143     case 'loose_item_start': {
20144       var body = '';
20145
20146       while (this.next().type !== 'list_item_end') {
20147         body += this.tok();
20148       }
20149
20150       return '<li>'
20151         + body
20152         + '</li>\n';
20153     }
20154     case 'html': {
20155       return !this.token.pre && !this.options.pedantic
20156         ? this.inline.output(this.token.text)
20157         : this.token.text;
20158     }
20159     case 'paragraph': {
20160       return '<p>'
20161         + this.inline.output(this.token.text)
20162         + '</p>\n';
20163     }
20164     case 'text': {
20165       return '<p>'
20166         + this.parseText()
20167         + '</p>\n';
20168     }
20169   }
20170 };
20171
20172 /**
20173  * Helpers
20174  */
20175
20176 function escape(html, encode) {
20177   return html
20178     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
20179     .replace(/</g, '&lt;')
20180     .replace(/>/g, '&gt;')
20181     .replace(/"/g, '&quot;')
20182     .replace(/'/g, '&#39;');
20183 }
20184
20185 function replace(regex, opt) {
20186   regex = regex.source;
20187   opt = opt || '';
20188   return function self(name, val) {
20189     if (!name) return new RegExp(regex, opt);
20190     val = val.source || val;
20191     val = val.replace(/(^|[^\[])\^/g, '$1');
20192     regex = regex.replace(name, val);
20193     return self;
20194   };
20195 }
20196
20197 function noop() {}
20198 noop.exec = noop;
20199
20200 function merge(obj) {
20201   var i = 1
20202     , target
20203     , key;
20204
20205   for (; i < arguments.length; i++) {
20206     target = arguments[i];
20207     for (key in target) {
20208       if (Object.prototype.hasOwnProperty.call(target, key)) {
20209         obj[key] = target[key];
20210       }
20211     }
20212   }
20213
20214   return obj;
20215 }
20216
20217 /**
20218  * Marked
20219  */
20220
20221 function marked(src, opt, callback) {
20222   if (callback || typeof opt === 'function') {
20223     if (!callback) {
20224       callback = opt;
20225       opt = null;
20226     }
20227
20228     if (opt) opt = merge({}, marked.defaults, opt);
20229
20230     var tokens = Lexer.lex(tokens, opt)
20231       , highlight = opt.highlight
20232       , pending = 0
20233       , l = tokens.length
20234       , i = 0;
20235
20236     if (!highlight || highlight.length < 3) {
20237       return callback(null, Parser.parse(tokens, opt));
20238     }
20239
20240     var done = function() {
20241       delete opt.highlight;
20242       var out = Parser.parse(tokens, opt);
20243       opt.highlight = highlight;
20244       return callback(null, out);
20245     };
20246
20247     for (; i < l; i++) {
20248       (function(token) {
20249         if (token.type !== 'code') return;
20250         pending++;
20251         return highlight(token.text, token.lang, function(err, code) {
20252           if (code == null || code === token.text) {
20253             return --pending || done();
20254           }
20255           token.text = code;
20256           token.escaped = true;
20257           --pending || done();
20258         });
20259       })(tokens[i]);
20260     }
20261
20262     return;
20263   }
20264   try {
20265     if (opt) opt = merge({}, marked.defaults, opt);
20266     return Parser.parse(Lexer.lex(src, opt), opt);
20267   } catch (e) {
20268     e.message += '\nPlease report this to https://github.com/chjj/marked.';
20269     if ((opt || marked.defaults).silent) {
20270       return '<p>An error occured:</p><pre>'
20271         + escape(e.message + '', true)
20272         + '</pre>';
20273     }
20274     throw e;
20275   }
20276 }
20277
20278 /**
20279  * Options
20280  */
20281
20282 marked.options =
20283 marked.setOptions = function(opt) {
20284   merge(marked.defaults, opt);
20285   return marked;
20286 };
20287
20288 marked.defaults = {
20289   gfm: true,
20290   tables: true,
20291   breaks: false,
20292   pedantic: false,
20293   sanitize: false,
20294   smartLists: false,
20295   silent: false,
20296   highlight: null,
20297   langPrefix: 'lang-'
20298 };
20299
20300 /**
20301  * Expose
20302  */
20303
20304 marked.Parser = Parser;
20305 marked.parser = Parser.parse;
20306
20307 marked.Lexer = Lexer;
20308 marked.lexer = Lexer.lex;
20309
20310 marked.InlineLexer = InlineLexer;
20311 marked.inlineLexer = InlineLexer.output;
20312
20313 marked.parse = marked;
20314
20315 if (typeof exports === 'object') {
20316   module.exports = marked;
20317 } else if (typeof define === 'function' && define.amd) {
20318   define(function() { return marked; });
20319 } else {
20320   this.marked = marked;
20321 }
20322
20323 }).call(function() {
20324   return this || (typeof window !== 'undefined' ? window : global);
20325 }());
20326 (function () {
20327 'use strict';
20328 window.iD = function () {
20329     window.locale.en = iD.data.en;
20330     window.locale.current('en');
20331
20332     var dispatch = d3.dispatch('enter', 'exit', 'change'),
20333         context = {};
20334
20335     // https://github.com/openstreetmap/iD/issues/772
20336     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
20337     var storage;
20338     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
20339     storage = storage || (function() {
20340         var s = {};
20341         return {
20342             getItem: function(k) { return s[k]; },
20343             setItem: function(k, v) { s[k] = v; },
20344             removeItem: function(k) { delete s[k]; }
20345         };
20346     })();
20347
20348     context.storage = function(k, v) {
20349         try {
20350             if (arguments.length === 1) return storage.getItem(k);
20351             else if (v === null) storage.removeItem(k);
20352             else storage.setItem(k, v);
20353         } catch(e) {
20354             // localstorage quota exceeded
20355             /* eslint-disable no-console */
20356             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
20357             /* eslint-enable no-console */
20358         }
20359     };
20360
20361
20362     /* Straight accessors. Avoid using these if you can. */
20363     var ui, connection, history;
20364     context.ui = function() { return ui; };
20365     context.connection = function() { return connection; };
20366     context.history = function() { return history; };
20367
20368
20369     /* Connection */
20370     function entitiesLoaded(err, result) {
20371         if (!err) history.merge(result.data, result.extent);
20372     }
20373
20374     context.preauth = function(options) {
20375         connection.switch(options);
20376         return context;
20377     };
20378
20379     context.loadTiles = function(projection, dimensions, callback) {
20380         function done(err, result) {
20381             entitiesLoaded(err, result);
20382             if (callback) callback(err, result);
20383         }
20384         connection.loadTiles(projection, dimensions, done);
20385     };
20386
20387     context.loadEntity = function(id, callback) {
20388         function done(err, result) {
20389             entitiesLoaded(err, result);
20390             if (callback) callback(err, result);
20391         }
20392         connection.loadEntity(id, done);
20393     };
20394
20395     context.zoomToEntity = function(id, zoomTo) {
20396         if (zoomTo !== false) {
20397             this.loadEntity(id, function(err, result) {
20398                 if (err) return;
20399                 var entity = _.find(result.data, function(e) { return e.id === id; });
20400                 if (entity) { map.zoomTo(entity); }
20401             });
20402         }
20403
20404         map.on('drawn.zoomToEntity', function() {
20405             if (!context.hasEntity(id)) return;
20406             map.on('drawn.zoomToEntity', null);
20407             context.on('enter.zoomToEntity', null);
20408             context.enter(iD.modes.Select(context, [id]));
20409         });
20410
20411         context.on('enter.zoomToEntity', function() {
20412             if (mode.id !== 'browse') {
20413                 map.on('drawn.zoomToEntity', null);
20414                 context.on('enter.zoomToEntity', null);
20415             }
20416         });
20417     };
20418
20419     var minEditableZoom = 16;
20420     context.minEditableZoom = function(_) {
20421         if (!arguments.length) return minEditableZoom;
20422         minEditableZoom = _;
20423         connection.tileZoom(_);
20424         return context;
20425     };
20426
20427
20428     /* History */
20429     var inIntro = false;
20430     context.inIntro = function(_) {
20431         if (!arguments.length) return inIntro;
20432         inIntro = _;
20433         return context;
20434     };
20435
20436     context.save = function() {
20437         if (inIntro || (mode && mode.id === 'save') || d3.select('.modal').size()) return;
20438         history.save();
20439         if (history.hasChanges()) return t('save.unsaved_changes');
20440     };
20441
20442     context.flush = function() {
20443         context.debouncedSave.cancel();
20444         connection.flush();
20445         features.reset();
20446         history.reset();
20447         _.each(iD.services, function(service) {
20448             var reset = service().reset;
20449             if (reset) reset(context);
20450         });
20451         return context;
20452     };
20453
20454
20455     /* Graph */
20456     context.hasEntity = function(id) {
20457         return history.graph().hasEntity(id);
20458     };
20459     context.entity = function(id) {
20460         return history.graph().entity(id);
20461     };
20462     context.childNodes = function(way) {
20463         return history.graph().childNodes(way);
20464     };
20465     context.geometry = function(id) {
20466         return context.entity(id).geometry(history.graph());
20467     };
20468
20469
20470     /* Modes */
20471     var mode;
20472     context.mode = function() {
20473         return mode;
20474     };
20475     context.enter = function(newMode) {
20476         if (mode) {
20477             mode.exit();
20478             dispatch.exit(mode);
20479         }
20480
20481         mode = newMode;
20482         mode.enter();
20483         dispatch.enter(mode);
20484     };
20485
20486     context.selectedIDs = function() {
20487         if (mode && mode.selectedIDs) {
20488             return mode.selectedIDs();
20489         } else {
20490             return [];
20491         }
20492     };
20493
20494
20495     /* Behaviors */
20496     context.install = function(behavior) {
20497         context.surface().call(behavior);
20498     };
20499     context.uninstall = function(behavior) {
20500         context.surface().call(behavior.off);
20501     };
20502
20503
20504     /* Copy/Paste */
20505     var copyIDs = [], copyGraph;
20506     context.copyGraph = function() { return copyGraph; };
20507     context.copyIDs = function(_) {
20508         if (!arguments.length) return copyIDs;
20509         copyIDs = _;
20510         copyGraph = history.graph();
20511         return context;
20512     };
20513
20514
20515     /* Background */
20516     var background;
20517     context.background = function() { return background; };
20518
20519
20520     /* Features */
20521     var features;
20522     context.features = function() { return features; };
20523     context.hasHiddenConnections = function(id) {
20524         var graph = history.graph(),
20525             entity = graph.entity(id);
20526         return features.hasHiddenConnections(entity, graph);
20527     };
20528
20529
20530     /* Map */
20531     var map;
20532     context.map = function() { return map; };
20533     context.layers = function() { return map.layers; };
20534     context.surface = function() { return map.surface; };
20535     context.editable = function() { return map.editable(); };
20536
20537     context.surfaceRect = function() {
20538         // Work around a bug in Firefox.
20539         //   http://stackoverflow.com/questions/18153989/
20540         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
20541         return context.surface().node().parentNode.getBoundingClientRect();
20542     };
20543
20544
20545     /* Debug */
20546     var debugTile = false, debugCollision = false;
20547     context.debugTile = function(_) {
20548         if (!arguments.length) return debugTile;
20549         debugTile = _;
20550         dispatch.change();
20551         return context;
20552     };
20553     context.debugCollision = function(_) {
20554         if (!arguments.length) return debugCollision;
20555         debugCollision = _;
20556         dispatch.change();
20557         return context;
20558     };
20559
20560
20561     /* Presets */
20562     var presets;
20563     context.presets = function(_) {
20564         if (!arguments.length) return presets;
20565         presets.load(_);
20566         iD.areaKeys = presets.areaKeys();
20567         return context;
20568     };
20569
20570
20571     /* Imagery */
20572     context.imagery = function(_) {
20573         background.load(_);
20574         return context;
20575     };
20576
20577
20578     /* Container */
20579     var container, embed;
20580     context.container = function(_) {
20581         if (!arguments.length) return container;
20582         container = _;
20583         container.classed('id-container', true);
20584         return context;
20585     };
20586     context.embed = function(_) {
20587         if (!arguments.length) return embed;
20588         embed = _;
20589         return context;
20590     };
20591
20592
20593     /* Taginfo */
20594     var taginfo;
20595     context.taginfo = function(_) {
20596         if (!arguments.length) return taginfo;
20597         taginfo = _;
20598         return context;
20599     };
20600
20601
20602     /* Assets */
20603     var assetPath = '';
20604     context.assetPath = function(_) {
20605         if (!arguments.length) return assetPath;
20606         assetPath = _;
20607         return context;
20608     };
20609
20610     var assetMap = {};
20611     context.assetMap = function(_) {
20612         if (!arguments.length) return assetMap;
20613         assetMap = _;
20614         return context;
20615     };
20616
20617     context.asset = function(_) {
20618         var filename = assetPath + _;
20619         return assetMap[filename] || filename;
20620     };
20621
20622     context.imagePath = function(_) {
20623         return context.asset('img/' + _);
20624     };
20625
20626     var locale, localePath;
20627     context.locale = function(loc, path) {
20628         locale = loc;
20629         localePath = path;
20630
20631         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
20632         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
20633             iD.detect().locale = loc;
20634         }
20635
20636         return context;
20637     };
20638
20639     context.loadLocale = function(cb) {
20640         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
20641             localePath = localePath || context.asset('locales/' + locale + '.json');
20642             d3.json(localePath, function(err, result) {
20643                 window.locale[locale] = result;
20644                 window.locale.current(locale);
20645                 cb();
20646             });
20647         } else {
20648             cb();
20649         }
20650     };
20651
20652
20653     /* Init */
20654
20655     context.projection = iD.geo.RawMercator();
20656
20657     locale = iD.detect().locale;
20658     if (locale && iD.data.locales.indexOf(locale) === -1) {
20659         locale = locale.split('-')[0];
20660     }
20661
20662     history = iD.History(context);
20663     context.graph = history.graph;
20664     context.changes = history.changes;
20665     context.intersects = history.intersects;
20666
20667     // Debounce save, since it's a synchronous localStorage write,
20668     // and history changes can happen frequently (e.g. when dragging).
20669     context.debouncedSave = _.debounce(context.save, 350);
20670     function withDebouncedSave(fn) {
20671         return function() {
20672             var result = fn.apply(history, arguments);
20673             context.debouncedSave();
20674             return result;
20675         };
20676     }
20677
20678     context.perform = withDebouncedSave(history.perform);
20679     context.replace = withDebouncedSave(history.replace);
20680     context.pop = withDebouncedSave(history.pop);
20681     context.overwrite = withDebouncedSave(history.overwrite);
20682     context.undo = withDebouncedSave(history.undo);
20683     context.redo = withDebouncedSave(history.redo);
20684
20685     ui = iD.ui(context);
20686
20687     connection = iD.Connection();
20688
20689     background = iD.Background(context);
20690
20691     features = iD.Features(context);
20692
20693     map = iD.Map(context);
20694     context.mouse = map.mouse;
20695     context.extent = map.extent;
20696     context.pan = map.pan;
20697     context.zoomIn = map.zoomIn;
20698     context.zoomOut = map.zoomOut;
20699     context.zoomInFurther = map.zoomInFurther;
20700     context.zoomOutFurther = map.zoomOutFurther;
20701     context.redrawEnable = map.redrawEnable;
20702
20703     presets = iD.presets();
20704
20705     return d3.rebind(context, dispatch, 'on');
20706 };
20707
20708
20709 iD.version = '1.9.5';
20710
20711 (function() {
20712     var detected = {};
20713
20714     var ua = navigator.userAgent,
20715         m = null;
20716
20717     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
20718     if (m !== null) {
20719         detected.browser = m[1];
20720         detected.version = m[2];
20721     }
20722     if (!detected.browser) {
20723         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
20724         if (m !== null) {
20725             detected.browser = 'msie';
20726             detected.version = m[1];
20727         }
20728     }
20729     if (!detected.browser) {
20730         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
20731         if (m !== null) {
20732             detected.browser = 'Opera';
20733             detected.version = m[2];
20734         }
20735     }
20736     if (!detected.browser) {
20737         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
20738         if (m !== null) {
20739             detected.browser = m[1];
20740             detected.version = m[2];
20741             m = ua.match(/version\/([\.\d]+)/i);
20742             if (m !== null) detected.version = m[1];
20743         }
20744     }
20745     if (!detected.browser) {
20746         detected.browser = navigator.appName;
20747         detected.version = navigator.appVersion;
20748     }
20749
20750     // keep major.minor version only..
20751     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
20752
20753     if (detected.browser.toLowerCase() === 'msie') {
20754         detected.ie = true;
20755         detected.browser = 'Internet Explorer';
20756         detected.support = parseFloat(detected.version) >= 11;
20757     } else {
20758         detected.ie = false;
20759         detected.support = true;
20760     }
20761
20762     // Added due to incomplete svg style support. See #715
20763     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
20764
20765     detected.locale = (navigator.languages && navigator.languages.length)
20766         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
20767
20768     detected.filedrop = (window.FileReader && 'ondrop' in window);
20769
20770     function nav(x) {
20771         return navigator.userAgent.indexOf(x) !== -1;
20772     }
20773
20774     if (nav('Win')) {
20775         detected.os = 'win';
20776         detected.platform = 'Windows';
20777     }
20778     else if (nav('Mac')) {
20779         detected.os = 'mac';
20780         detected.platform = 'Macintosh';
20781     }
20782     else if (nav('X11') || nav('Linux')) {
20783         detected.os = 'linux';
20784         detected.platform = 'Linux';
20785     }
20786     else {
20787         detected.os = 'win';
20788         detected.platform = 'Unknown';
20789     }
20790
20791     iD.detect = function() { return detected; };
20792 })();
20793 iD.services = {};
20794 iD.services.mapillary = function() {
20795     var mapillary = {},
20796         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
20797         apibase = 'https://a.mapillary.com/v2/',
20798         urlImage = 'https://www.mapillary.com/map/im/',
20799         urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/',
20800         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
20801         maxResults = 1000,
20802         tileZoom = 14;
20803
20804
20805     function loadSignStyles(context) {
20806         d3.select('head').selectAll('#traffico')
20807             .data([0])
20808             .enter()
20809             .append('link')
20810             .attr('id', 'traffico')
20811             .attr('rel', 'stylesheet')
20812             .attr('href', context.asset('traffico/stylesheets/traffico.css'));
20813     }
20814
20815     function loadSignDefs(context) {
20816         if (!iD.services.mapillary.sign_defs) {
20817             iD.services.mapillary.sign_defs = {};
20818             _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
20819                 d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
20820                     if (err) return;
20821                     if (region === 'de') region = 'eu';
20822                     iD.services.mapillary.sign_defs[region] = data;
20823                 });
20824             });
20825         }
20826     }
20827
20828     function abortRequest(i) {
20829         i.abort();
20830     }
20831
20832     function nearNullIsland(x, y, z) {
20833         if (z >= 7) {
20834             var center = Math.pow(2, z - 1),
20835                 width = Math.pow(2, z - 6),
20836                 min = center - (width / 2),
20837                 max = center + (width / 2) - 1;
20838             return x >= min && x <= max && y >= min && y <= max;
20839         }
20840         return false;
20841     }
20842
20843     function getTiles(projection, dimensions) {
20844         var s = projection.scale() * 2 * Math.PI,
20845             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
20846             ts = 256 * Math.pow(2, z - tileZoom),
20847             origin = [
20848                 s / 2 - projection.translate()[0],
20849                 s / 2 - projection.translate()[1]];
20850
20851         return d3.geo.tile()
20852             .scaleExtent([tileZoom, tileZoom])
20853             .scale(s)
20854             .size(dimensions)
20855             .translate(projection.translate())()
20856             .map(function(tile) {
20857                 var x = tile[0] * ts - origin[0],
20858                     y = tile[1] * ts - origin[1];
20859
20860                 return {
20861                     id: tile.toString(),
20862                     extent: iD.geo.Extent(
20863                         projection.invert([x, y + ts]),
20864                         projection.invert([x + ts, y]))
20865                 };
20866             });
20867     }
20868
20869
20870     function loadTiles(which, url, projection, dimensions) {
20871         var tiles = getTiles(projection, dimensions).filter(function(t) {
20872                 var xyz = t.id.split(',');
20873                 return !nearNullIsland(xyz[0], xyz[1], xyz[2]);
20874             });
20875
20876         _.filter(which.inflight, function(v, k) {
20877             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
20878             if (!wanted) delete which.inflight[k];
20879             return !wanted;
20880         }).map(abortRequest);
20881
20882         tiles.forEach(function(tile) {
20883             loadTilePage(which, url, tile, 0);
20884         });
20885     }
20886
20887     function loadTilePage(which, url, tile, page) {
20888         var cache = iD.services.mapillary.cache[which],
20889             id = tile.id + ',' + String(page),
20890             rect = tile.extent.rectangle();
20891
20892         if (cache.loaded[id] || cache.inflight[id]) return;
20893
20894         cache.inflight[id] = d3.json(url +
20895             iD.util.qsString({
20896                 geojson: 'true',
20897                 limit: maxResults,
20898                 page: page,
20899                 client_id: clientId,
20900                 min_lon: rect[0],
20901                 min_lat: rect[1],
20902                 max_lon: rect[2],
20903                 max_lat: rect[3]
20904             }), function(err, data) {
20905                 cache.loaded[id] = true;
20906                 delete cache.inflight[id];
20907                 if (err || !data.features || !data.features.length) return;
20908
20909                 var features = [],
20910                     feature, loc, d;
20911
20912                 for (var i = 0; i < data.features.length; i++) {
20913                     feature = data.features[i];
20914                     loc = feature.geometry.coordinates;
20915                     d = { key: feature.properties.key, loc: loc };
20916                     if (which === 'images') d.ca = feature.properties.ca;
20917                     if (which === 'signs') d.signs = feature.properties.rects;
20918
20919                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
20920                 }
20921
20922                 cache.rtree.load(features);
20923
20924                 if (which === 'images') dispatch.loadedImages();
20925                 if (which === 'signs') dispatch.loadedSigns();
20926
20927                 if (data.features.length === maxResults) {
20928                     loadTilePage(which, url, tile, ++page);
20929                 }
20930             }
20931         );
20932     }
20933
20934     mapillary.loadImages = function(projection, dimensions) {
20935         var url = apibase + 'search/im/geojson?';
20936         loadTiles('images', url, projection, dimensions);
20937     };
20938
20939     mapillary.loadSigns = function(context, projection, dimensions) {
20940         var url = apibase + 'search/im/geojson/or?';
20941         loadSignStyles(context);
20942         loadSignDefs(context);
20943         loadTiles('signs', url, projection, dimensions);
20944     };
20945
20946
20947     // partition viewport into `psize` x `psize` regions
20948     function partitionViewport(psize, projection, dimensions) {
20949         psize = psize || 16;
20950         var cols = d3.range(0, dimensions[0], psize),
20951             rows = d3.range(0, dimensions[1], psize),
20952             partitions = [];
20953
20954         rows.forEach(function(y) {
20955             cols.forEach(function(x) {
20956                 var min = [x, y + psize],
20957                     max = [x + psize, y];
20958                 partitions.push(
20959                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
20960             });
20961         });
20962
20963         return partitions;
20964     }
20965
20966     // no more than `limit` results per partition.
20967     function searchLimited(psize, limit, projection, dimensions, rtree) {
20968         limit = limit || 3;
20969
20970         var partitions = partitionViewport(psize, projection, dimensions);
20971         return _.flatten(_.compact(_.map(partitions, function(extent) {
20972             return rtree.search(extent.rectangle())
20973                 .slice(0, limit)
20974                 .map(function(d) { return d[4]; });
20975         })));
20976     }
20977
20978     mapillary.images = function(projection, dimensions) {
20979         var psize = 16, limit = 3;
20980         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
20981     };
20982
20983     mapillary.signs = function(projection, dimensions) {
20984         var psize = 32, limit = 3;
20985         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
20986     };
20987
20988     mapillary.signsSupported = function() {
20989         var detected = iD.detect();
20990         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
20991     };
20992
20993     mapillary.signHTML = function(d) {
20994         if (!iD.services.mapillary.sign_defs) return;
20995
20996         var detectionPackage = d.signs[0].package,
20997             type = d.signs[0].type,
20998             country = detectionPackage.split('_')[1];
20999         return iD.services.mapillary.sign_defs[country][type];
21000     };
21001
21002     mapillary.showThumbnail = function(imageKey, position) {
21003         if (!imageKey) return;
21004
21005         var positionClass = {
21006             'ar': (position !== 'left'),
21007             'al': (position === 'left')
21008         };
21009
21010         var thumbnail = d3.select('#content').selectAll('.mapillary-image')
21011             .data([0]);
21012
21013         // Enter
21014         var enter = thumbnail.enter().append('div')
21015             .attr('class', 'mapillary-image ar');
21016
21017         enter.append('button')
21018             .on('click', function () {
21019                 mapillary.hideThumbnail();
21020             })
21021             .append('div')
21022             .call(iD.svg.Icon('#icon-close'));
21023
21024         enter.append('img');
21025
21026         enter.append('a')
21027             .attr('class', 'link ar')
21028             .attr('target', '_blank')
21029             .call(iD.svg.Icon('#icon-out-link', 'inline'))
21030             .append('span')
21031             .text(t('mapillary.view_on_mapillary'));
21032
21033         // Update
21034         thumbnail.selectAll('img')
21035             .attr('src', urlThumb + imageKey + '/thumb-320.jpg');
21036
21037         var link = thumbnail.selectAll('a')
21038             .attr('href', urlImage + imageKey);
21039
21040         if (position) {
21041             thumbnail.classed(positionClass);
21042             link.classed(positionClass);
21043         }
21044
21045         thumbnail
21046             .transition()
21047             .duration(200)
21048             .style('opacity', 1);
21049     };
21050
21051     mapillary.hideThumbnail = function() {
21052         if (iD.services.mapillary) {
21053             iD.services.mapillary.thumb = null;
21054         }
21055         d3.select('#content').selectAll('.mapillary-image')
21056             .transition()
21057             .duration(200)
21058             .style('opacity', 0)
21059             .remove();
21060     };
21061
21062     mapillary.selectedThumbnail = function(d) {
21063         if (!iD.services.mapillary) return null;
21064         if (!arguments.length) return iD.services.mapillary.thumb;
21065         iD.services.mapillary.thumb = d;
21066     };
21067
21068     mapillary.reset = function() {
21069         var cache = iD.services.mapillary.cache;
21070
21071         if (cache) {
21072             _.forEach(cache.images.inflight, abortRequest);
21073             _.forEach(cache.signs.inflight, abortRequest);
21074         }
21075
21076         iD.services.mapillary.cache = {
21077             images: { inflight: {}, loaded: {}, rtree: rbush() },
21078             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
21079         };
21080
21081         iD.services.mapillary.thumb = null;
21082
21083         return mapillary;
21084     };
21085
21086
21087     if (!iD.services.mapillary.cache) {
21088         mapillary.reset();
21089     }
21090
21091     return d3.rebind(mapillary, dispatch, 'on');
21092 };
21093 iD.services.nominatim  = function() {
21094     var nominatim = {},
21095         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
21096
21097
21098     nominatim.countryCode = function(location, callback) {
21099         var cache = iD.services.nominatim.cache,
21100             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
21101
21102         if (countryCodes.length > 0)
21103             return callback(null, countryCodes[0][4]);
21104
21105         d3.json(endpoint +
21106             iD.util.qsString({
21107                 format: 'json',
21108                 addressdetails: 1,
21109                 lat: location[1],
21110                 lon: location[0]
21111             }), function(err, result) {
21112                 if (err)
21113                     return callback(err);
21114                 else if (result && result.error)
21115                     return callback(result.error);
21116
21117                 var extent = iD.geo.Extent(location).padByMeters(1000);
21118
21119                 cache.insert(extent.rectangle().concat(result.address.country_code));
21120
21121                 callback(null, result.address.country_code);
21122             });
21123     };
21124
21125     nominatim.reset = function() {
21126         iD.services.nominatim.cache = rbush();
21127         return nominatim;
21128     };
21129
21130
21131     if (!iD.services.nominatim.cache) {
21132         nominatim.reset();
21133     }
21134
21135     return nominatim;
21136 };
21137 iD.services.taginfo = function() {
21138     var taginfo = {},
21139         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
21140         tag_sorts = {
21141             point: 'count_nodes',
21142             vertex: 'count_nodes',
21143             area: 'count_ways',
21144             line: 'count_ways'
21145         },
21146         tag_filters = {
21147             point: 'nodes',
21148             vertex: 'nodes',
21149             area: 'ways',
21150             line: 'ways'
21151         };
21152
21153
21154     function sets(parameters, n, o) {
21155         if (parameters.geometry && o[parameters.geometry]) {
21156             parameters[n] = o[parameters.geometry];
21157         }
21158         return parameters;
21159     }
21160
21161     function setFilter(parameters) {
21162         return sets(parameters, 'filter', tag_filters);
21163     }
21164
21165     function setSort(parameters) {
21166         return sets(parameters, 'sortname', tag_sorts);
21167     }
21168
21169     function clean(parameters) {
21170         return _.omit(parameters, 'geometry', 'debounce');
21171     }
21172
21173     function filterKeys(type) {
21174         var count_type = type ? 'count_' + type : 'count_all';
21175         return function(d) {
21176             return parseFloat(d[count_type]) > 2500 || d.in_wiki;
21177         };
21178     }
21179
21180     function filterMultikeys() {
21181         return function(d) {
21182             return (d.key.match(/:/g) || []).length === 1;  // exactly one ':'
21183         };
21184     }
21185
21186     function filterValues() {
21187         return function(d) {
21188             if (d.value.match(/[A-Z*;,]/) !== null) return false;  // exclude some punctuation, uppercase letters
21189             return parseFloat(d.fraction) > 0.0 || d.in_wiki;
21190         };
21191     }
21192
21193     function valKey(d) {
21194         return {
21195             value: d.key,
21196             title: d.key
21197         };
21198     }
21199
21200     function valKeyDescription(d) {
21201         return {
21202             value: d.value,
21203             title: d.description || d.value
21204         };
21205     }
21206
21207     // sort keys with ':' lower than keys without ':'
21208     function sortKeys(a, b) {
21209         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
21210             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
21211             : 0;
21212     }
21213
21214     var debounced = _.debounce(d3.json, 100, true);
21215
21216     function request(url, debounce, callback) {
21217         var cache = iD.services.taginfo.cache;
21218
21219         if (cache[url]) {
21220             callback(null, cache[url]);
21221         } else if (debounce) {
21222             debounced(url, done);
21223         } else {
21224             d3.json(url, done);
21225         }
21226
21227         function done(err, data) {
21228             if (!err) cache[url] = data;
21229             callback(err, data);
21230         }
21231     }
21232
21233     taginfo.keys = function(parameters, callback) {
21234         var debounce = parameters.debounce;
21235         parameters = clean(setSort(parameters));
21236         request(endpoint + 'keys/all?' +
21237             iD.util.qsString(_.extend({
21238                 rp: 10,
21239                 sortname: 'count_all',
21240                 sortorder: 'desc',
21241                 page: 1
21242             }, parameters)), debounce, function(err, d) {
21243                 if (err) return callback(err);
21244                 var f = filterKeys(parameters.filter);
21245                 callback(null, d.data.filter(f).sort(sortKeys).map(valKey));
21246             });
21247     };
21248
21249     taginfo.multikeys = function(parameters, callback) {
21250         var debounce = parameters.debounce;
21251         parameters = clean(setSort(parameters));
21252         request(endpoint + 'keys/all?' +
21253             iD.util.qsString(_.extend({
21254                 rp: 25,
21255                 sortname: 'count_all',
21256                 sortorder: 'desc',
21257                 page: 1
21258             }, parameters)), debounce, function(err, d) {
21259                 if (err) return callback(err);
21260                 var f = filterMultikeys();
21261                 callback(null, d.data.filter(f).map(valKey));
21262             });
21263     };
21264
21265     taginfo.values = function(parameters, callback) {
21266         var debounce = parameters.debounce;
21267         parameters = clean(setSort(setFilter(parameters)));
21268         request(endpoint + 'key/values?' +
21269             iD.util.qsString(_.extend({
21270                 rp: 25,
21271                 sortname: 'count_all',
21272                 sortorder: 'desc',
21273                 page: 1
21274             }, parameters)), debounce, function(err, d) {
21275                 if (err) return callback(err);
21276                 var f = filterValues();
21277                 callback(null, d.data.filter(f).map(valKeyDescription));
21278             });
21279     };
21280
21281     taginfo.docs = function(parameters, callback) {
21282         var debounce = parameters.debounce;
21283         parameters = clean(setSort(parameters));
21284
21285         var path = 'key/wiki_pages?';
21286         if (parameters.value) path = 'tag/wiki_pages?';
21287         else if (parameters.rtype) path = 'relation/wiki_pages?';
21288
21289         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
21290             if (err) return callback(err);
21291             callback(null, d.data);
21292         });
21293     };
21294
21295     taginfo.endpoint = function(_) {
21296         if (!arguments.length) return endpoint;
21297         endpoint = _;
21298         return taginfo;
21299     };
21300
21301     taginfo.reset = function() {
21302         iD.services.taginfo.cache = {};
21303         return taginfo;
21304     };
21305
21306
21307     if (!iD.services.taginfo.cache) {
21308         taginfo.reset();
21309     }
21310
21311     return taginfo;
21312 };
21313 iD.services.wikipedia = function() {
21314     var wiki = {},
21315         endpoint = 'https://en.wikipedia.org/w/api.php?';
21316
21317     wiki.search = function(lang, query, callback) {
21318         lang = lang || 'en';
21319         d3.jsonp(endpoint.replace('en', lang) +
21320             iD.util.qsString({
21321                 action: 'query',
21322                 list: 'search',
21323                 srlimit: '10',
21324                 srinfo: 'suggestion',
21325                 format: 'json',
21326                 callback: '{callback}',
21327                 srsearch: query
21328             }), function(data) {
21329                 if (!data.query) return;
21330                 callback(query, data.query.search.map(function(d) {
21331                     return d.title;
21332                 }));
21333             });
21334     };
21335
21336     wiki.suggestions = function(lang, query, callback) {
21337         lang = lang || 'en';
21338         d3.jsonp(endpoint.replace('en', lang) +
21339             iD.util.qsString({
21340                 action: 'opensearch',
21341                 namespace: 0,
21342                 suggest: '',
21343                 format: 'json',
21344                 callback: '{callback}',
21345                 search: query
21346             }), function(d) {
21347                 callback(d[0], d[1]);
21348             });
21349     };
21350
21351     wiki.translations = function(lang, title, callback) {
21352         d3.jsonp(endpoint.replace('en', lang) +
21353             iD.util.qsString({
21354                 action: 'query',
21355                 prop: 'langlinks',
21356                 format: 'json',
21357                 callback: '{callback}',
21358                 lllimit: 500,
21359                 titles: title
21360             }), function(d) {
21361                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
21362                     translations = {};
21363                 if (list && list.langlinks) {
21364                     list.langlinks.forEach(function(d) {
21365                         translations[d.lang] = d['*'];
21366                     });
21367                     callback(translations);
21368                 }
21369             });
21370     };
21371
21372     return wiki;
21373 };
21374 iD.util = {};
21375
21376 iD.util.tagText = function(entity) {
21377     return d3.entries(entity.tags).map(function(e) {
21378         return e.key + '=' + e.value;
21379     }).join(', ');
21380 };
21381
21382 iD.util.entitySelector = function(ids) {
21383     return ids.length ? '.' + ids.join(',.') : 'nothing';
21384 };
21385
21386 iD.util.entityOrMemberSelector = function(ids, graph) {
21387     var s = iD.util.entitySelector(ids);
21388
21389     ids.forEach(function(id) {
21390         var entity = graph.hasEntity(id);
21391         if (entity && entity.type === 'relation') {
21392             entity.members.forEach(function(member) {
21393                 s += ',.' + member.id;
21394             });
21395         }
21396     });
21397
21398     return s;
21399 };
21400
21401 iD.util.displayName = function(entity) {
21402     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
21403     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
21404 };
21405
21406 iD.util.displayType = function(id) {
21407     return {
21408         n: t('inspector.node'),
21409         w: t('inspector.way'),
21410         r: t('inspector.relation')
21411     }[id.charAt(0)];
21412 };
21413
21414 iD.util.stringQs = function(str) {
21415     return str.split('&').reduce(function(obj, pair){
21416         var parts = pair.split('=');
21417         if (parts.length === 2) {
21418             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
21419         }
21420         return obj;
21421     }, {});
21422 };
21423
21424 iD.util.qsString = function(obj, noencode) {
21425     function softEncode(s) {
21426       // encode everything except special characters used in certain hash parameters:
21427       // "/" in map states, ":", ",", {" and "}" in background
21428       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
21429     }
21430     return Object.keys(obj).sort().map(function(key) {
21431         return encodeURIComponent(key) + '=' + (
21432             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
21433     }).join('&');
21434 };
21435
21436 iD.util.prefixDOMProperty = function(property) {
21437     var prefixes = ['webkit', 'ms', 'moz', 'o'],
21438         i = -1,
21439         n = prefixes.length,
21440         s = document.body;
21441
21442     if (property in s)
21443         return property;
21444
21445     property = property.substr(0, 1).toUpperCase() + property.substr(1);
21446
21447     while (++i < n)
21448         if (prefixes[i] + property in s)
21449             return prefixes[i] + property;
21450
21451     return false;
21452 };
21453
21454 iD.util.prefixCSSProperty = function(property) {
21455     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
21456         i = -1,
21457         n = prefixes.length,
21458         s = document.body.style;
21459
21460     if (property.toLowerCase() in s)
21461         return property.toLowerCase();
21462
21463     while (++i < n)
21464         if (prefixes[i] + property in s)
21465             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
21466
21467     return false;
21468 };
21469
21470
21471 iD.util.setTransform = function(el, x, y, scale) {
21472     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
21473         translate = iD.detect().opera ?
21474             'translate('   + x + 'px,' + y + 'px)' :
21475             'translate3d(' + x + 'px,' + y + 'px,0)';
21476     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
21477 };
21478
21479 iD.util.getStyle = function(selector) {
21480     for (var i = 0; i < document.styleSheets.length; i++) {
21481         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
21482         for (var k = 0; k < rules.length; k++) {
21483             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
21484             if (_.includes(selectorText, selector)) {
21485                 return rules[k];
21486             }
21487         }
21488     }
21489 };
21490
21491 iD.util.editDistance = function(a, b) {
21492     if (a.length === 0) return b.length;
21493     if (b.length === 0) return a.length;
21494     var matrix = [];
21495     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
21496     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
21497     for (i = 1; i <= b.length; i++) {
21498         for (j = 1; j <= a.length; j++) {
21499             if (b.charAt(i-1) === a.charAt(j-1)) {
21500                 matrix[i][j] = matrix[i-1][j-1];
21501             } else {
21502                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
21503                     Math.min(matrix[i][j-1] + 1, // insertion
21504                     matrix[i-1][j] + 1)); // deletion
21505             }
21506         }
21507     }
21508     return matrix[b.length][a.length];
21509 };
21510
21511 // a d3.mouse-alike which
21512 // 1. Only works on HTML elements, not SVG
21513 // 2. Does not cause style recalculation
21514 iD.util.fastMouse = function(container) {
21515     var rect = container.getBoundingClientRect(),
21516         rectLeft = rect.left,
21517         rectTop = rect.top,
21518         clientLeft = +container.clientLeft,
21519         clientTop = +container.clientTop;
21520     return function(e) {
21521         return [
21522             e.clientX - rectLeft - clientLeft,
21523             e.clientY - rectTop - clientTop];
21524     };
21525 };
21526
21527 /* eslint-disable no-proto */
21528 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
21529 /* eslint-enable no-proto */
21530
21531 iD.util.asyncMap = function(inputs, func, callback) {
21532     var remaining = inputs.length,
21533         results = [],
21534         errors = [];
21535
21536     inputs.forEach(function(d, i) {
21537         func(d, function done(err, data) {
21538             errors[i] = err;
21539             results[i] = data;
21540             remaining--;
21541             if (!remaining) callback(errors, results);
21542         });
21543     });
21544 };
21545
21546 // wraps an index to an interval [0..length-1]
21547 iD.util.wrap = function(index, length) {
21548     if (index < 0)
21549         index += Math.ceil(-index/length)*length;
21550     return index % length;
21551 };
21552 // A per-domain session mutex backed by a cookie and dead man's
21553 // switch. If the session crashes, the mutex will auto-release
21554 // after 5 seconds.
21555
21556 iD.util.SessionMutex = function(name) {
21557     var mutex = {},
21558         intervalID;
21559
21560     function renew() {
21561         var expires = new Date();
21562         expires.setSeconds(expires.getSeconds() + 5);
21563         document.cookie = name + '=1; expires=' + expires.toUTCString();
21564     }
21565
21566     mutex.lock = function() {
21567         if (intervalID) return true;
21568         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
21569         if (cookie) return false;
21570         renew();
21571         intervalID = window.setInterval(renew, 4000);
21572         return true;
21573     };
21574
21575     mutex.unlock = function() {
21576         if (!intervalID) return;
21577         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
21578         clearInterval(intervalID);
21579         intervalID = null;
21580     };
21581
21582     mutex.locked = function() {
21583         return !!intervalID;
21584     };
21585
21586     return mutex;
21587 };
21588 iD.util.SuggestNames = function(preset, suggestions) {
21589     preset = preset.id.split('/', 2);
21590     var k = preset[0],
21591         v = preset[1];
21592
21593     return function(value, callback) {
21594         var result = [];
21595         if (value && value.length > 2) {
21596             if (suggestions[k] && suggestions[k][v]) {
21597                 for (var sugg in suggestions[k][v]) {
21598                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
21599                     if (dist < 3) {
21600                         result.push({
21601                             title: sugg,
21602                             value: sugg,
21603                             dist: dist
21604                         });
21605                     }
21606                 }
21607             }
21608             result.sort(function(a, b) {
21609                 return a.dist - b.dist;
21610             });
21611         }
21612         result = result.slice(0,3);
21613         callback(result);
21614     };
21615 };
21616 iD.geo = {};
21617
21618 iD.geo.roundCoords = function(c) {
21619     return [Math.floor(c[0]), Math.floor(c[1])];
21620 };
21621
21622 iD.geo.interp = function(p1, p2, t) {
21623     return [p1[0] + (p2[0] - p1[0]) * t,
21624             p1[1] + (p2[1] - p1[1]) * t];
21625 };
21626
21627 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
21628 // Returns a positive value, if OAB makes a counter-clockwise turn,
21629 // negative for clockwise turn, and zero if the points are collinear.
21630 iD.geo.cross = function(o, a, b) {
21631     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
21632 };
21633
21634 // http://jsperf.com/id-dist-optimization
21635 iD.geo.euclideanDistance = function(a, b) {
21636     var x = a[0] - b[0], y = a[1] - b[1];
21637     return Math.sqrt((x * x) + (y * y));
21638 };
21639
21640 // using WGS84 polar radius (6356752.314245179 m)
21641 // const = 2 * PI * r / 360
21642 iD.geo.latToMeters = function(dLat) {
21643     return dLat * 110946.257617;
21644 };
21645
21646 // using WGS84 equatorial radius (6378137.0 m)
21647 // const = 2 * PI * r / 360
21648 iD.geo.lonToMeters = function(dLon, atLat) {
21649     return Math.abs(atLat) >= 90 ? 0 :
21650         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
21651 };
21652
21653 // using WGS84 polar radius (6356752.314245179 m)
21654 // const = 2 * PI * r / 360
21655 iD.geo.metersToLat = function(m) {
21656     return m / 110946.257617;
21657 };
21658
21659 // using WGS84 equatorial radius (6378137.0 m)
21660 // const = 2 * PI * r / 360
21661 iD.geo.metersToLon = function(m, atLat) {
21662     return Math.abs(atLat) >= 90 ? 0 :
21663         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
21664 };
21665
21666 iD.geo.offsetToMeters = function(offset) {
21667     var equatRadius = 6356752.314245179,
21668         polarRadius = 6378137.0,
21669         tileSize = 256;
21670
21671     return [
21672         offset[0] * 2 * Math.PI * equatRadius / tileSize,
21673         -offset[1] * 2 * Math.PI * polarRadius / tileSize
21674     ];
21675 };
21676
21677 iD.geo.metersToOffset = function(meters) {
21678     var equatRadius = 6356752.314245179,
21679         polarRadius = 6378137.0,
21680         tileSize = 256;
21681
21682     return [
21683         meters[0] * tileSize / (2 * Math.PI * equatRadius),
21684         -meters[1] * tileSize / (2 * Math.PI * polarRadius)
21685     ];
21686 };
21687
21688 // Equirectangular approximation of spherical distances on Earth
21689 iD.geo.sphericalDistance = function(a, b) {
21690     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
21691         y = iD.geo.latToMeters(a[1] - b[1]);
21692     return Math.sqrt((x * x) + (y * y));
21693 };
21694
21695 iD.geo.edgeEqual = function(a, b) {
21696     return (a[0] === b[0] && a[1] === b[1]) ||
21697         (a[0] === b[1] && a[1] === b[0]);
21698 };
21699
21700 // Return the counterclockwise angle in the range (-pi, pi)
21701 // between the positive X axis and the line intersecting a and b.
21702 iD.geo.angle = function(a, b, projection) {
21703     a = projection(a.loc);
21704     b = projection(b.loc);
21705     return Math.atan2(b[1] - a[1], b[0] - a[0]);
21706 };
21707
21708 // Choose the edge with the minimal distance from `point` to its orthogonal
21709 // projection onto that edge, if such a projection exists, or the distance to
21710 // the closest vertex on that edge. Returns an object with the `index` of the
21711 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
21712 iD.geo.chooseEdge = function(nodes, point, projection) {
21713     var dist = iD.geo.euclideanDistance,
21714         points = nodes.map(function(n) { return projection(n.loc); }),
21715         min = Infinity,
21716         idx, loc;
21717
21718     function dot(p, q) {
21719         return p[0] * q[0] + p[1] * q[1];
21720     }
21721
21722     for (var i = 0; i < points.length - 1; i++) {
21723         var o = points[i],
21724             s = [points[i + 1][0] - o[0],
21725                  points[i + 1][1] - o[1]],
21726             v = [point[0] - o[0],
21727                  point[1] - o[1]],
21728             proj = dot(v, s) / dot(s, s),
21729             p;
21730
21731         if (proj < 0) {
21732             p = o;
21733         } else if (proj > 1) {
21734             p = points[i + 1];
21735         } else {
21736             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
21737         }
21738
21739         var d = dist(p, point);
21740         if (d < min) {
21741             min = d;
21742             idx = i + 1;
21743             loc = projection.invert(p);
21744         }
21745     }
21746
21747     return {
21748         index: idx,
21749         distance: min,
21750         loc: loc
21751     };
21752 };
21753
21754 // Return the intersection point of 2 line segments.
21755 // From https://github.com/pgkelley4/line-segments-intersect
21756 // This uses the vector cross product approach described below:
21757 //  http://stackoverflow.com/a/565282/786339
21758 iD.geo.lineIntersection = function(a, b) {
21759     function subtractPoints(point1, point2) {
21760         return [point1[0] - point2[0], point1[1] - point2[1]];
21761     }
21762     function crossProduct(point1, point2) {
21763         return point1[0] * point2[1] - point1[1] * point2[0];
21764     }
21765
21766     var p = [a[0][0], a[0][1]],
21767         p2 = [a[1][0], a[1][1]],
21768         q = [b[0][0], b[0][1]],
21769         q2 = [b[1][0], b[1][1]],
21770         r = subtractPoints(p2, p),
21771         s = subtractPoints(q2, q),
21772         uNumerator = crossProduct(subtractPoints(q, p), r),
21773         denominator = crossProduct(r, s);
21774
21775     if (uNumerator && denominator) {
21776         var u = uNumerator / denominator,
21777             t = crossProduct(subtractPoints(q, p), s) / denominator;
21778
21779         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
21780             return iD.geo.interp(p, p2, t);
21781         }
21782     }
21783
21784     return null;
21785 };
21786
21787 iD.geo.pathIntersections = function(path1, path2) {
21788     var intersections = [];
21789     for (var i = 0; i < path1.length - 1; i++) {
21790         for (var j = 0; j < path2.length - 1; j++) {
21791             var a = [ path1[i], path1[i+1] ],
21792                 b = [ path2[j], path2[j+1] ],
21793                 hit = iD.geo.lineIntersection(a, b);
21794             if (hit) intersections.push(hit);
21795         }
21796     }
21797     return intersections;
21798 };
21799
21800 // Return whether point is contained in polygon.
21801 //
21802 // `point` should be a 2-item array of coordinates.
21803 // `polygon` should be an array of 2-item arrays of coordinates.
21804 //
21805 // From https://github.com/substack/point-in-polygon.
21806 // ray-casting algorithm based on
21807 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
21808 //
21809 iD.geo.pointInPolygon = function(point, polygon) {
21810     var x = point[0],
21811         y = point[1],
21812         inside = false;
21813
21814     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
21815         var xi = polygon[i][0], yi = polygon[i][1];
21816         var xj = polygon[j][0], yj = polygon[j][1];
21817
21818         var intersect = ((yi > y) !== (yj > y)) &&
21819             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
21820         if (intersect) inside = !inside;
21821     }
21822
21823     return inside;
21824 };
21825
21826 iD.geo.polygonContainsPolygon = function(outer, inner) {
21827     return _.every(inner, function(point) {
21828         return iD.geo.pointInPolygon(point, outer);
21829     });
21830 };
21831
21832 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
21833     function testSegments(outer, inner) {
21834         for (var i = 0; i < outer.length - 1; i++) {
21835             for (var j = 0; j < inner.length - 1; j++) {
21836                 var a = [ outer[i], outer[i+1] ],
21837                     b = [ inner[j], inner[j+1] ];
21838                 if (iD.geo.lineIntersection(a, b)) return true;
21839             }
21840         }
21841         return false;
21842     }
21843
21844     function testPoints(outer, inner) {
21845         return _.some(inner, function(point) {
21846             return iD.geo.pointInPolygon(point, outer);
21847         });
21848     }
21849
21850    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
21851 };
21852
21853 iD.geo.pathLength = function(path) {
21854     var length = 0,
21855         dx, dy;
21856     for (var i = 0; i < path.length - 1; i++) {
21857         dx = path[i][0] - path[i + 1][0];
21858         dy = path[i][1] - path[i + 1][1];
21859         length += Math.sqrt(dx * dx + dy * dy);
21860     }
21861     return length;
21862 };
21863 iD.geo.Extent = function geoExtent(min, max) {
21864     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
21865     if (min instanceof iD.geo.Extent) {
21866         return min;
21867     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
21868         this[0] = min[0];
21869         this[1] = min[1];
21870     } else {
21871         this[0] = min        || [ Infinity,  Infinity];
21872         this[1] = max || min || [-Infinity, -Infinity];
21873     }
21874 };
21875
21876 iD.geo.Extent.prototype = new Array(2);
21877
21878 _.extend(iD.geo.Extent.prototype, {
21879     equals: function (obj) {
21880         return this[0][0] === obj[0][0] &&
21881             this[0][1] === obj[0][1] &&
21882             this[1][0] === obj[1][0] &&
21883             this[1][1] === obj[1][1];
21884     },
21885
21886     extend: function(obj) {
21887         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
21888         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
21889                               Math.min(obj[0][1], this[0][1])],
21890                              [Math.max(obj[1][0], this[1][0]),
21891                               Math.max(obj[1][1], this[1][1])]);
21892     },
21893
21894     _extend: function(extent) {
21895         this[0][0] = Math.min(extent[0][0], this[0][0]);
21896         this[0][1] = Math.min(extent[0][1], this[0][1]);
21897         this[1][0] = Math.max(extent[1][0], this[1][0]);
21898         this[1][1] = Math.max(extent[1][1], this[1][1]);
21899     },
21900
21901     area: function() {
21902         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
21903     },
21904
21905     center: function() {
21906         return [(this[0][0] + this[1][0]) / 2,
21907                 (this[0][1] + this[1][1]) / 2];
21908     },
21909
21910     rectangle: function() {
21911         return [this[0][0], this[0][1], this[1][0], this[1][1]];
21912     },
21913
21914     polygon: function() {
21915         return [
21916             [this[0][0], this[0][1]],
21917             [this[0][0], this[1][1]],
21918             [this[1][0], this[1][1]],
21919             [this[1][0], this[0][1]],
21920             [this[0][0], this[0][1]]
21921         ];
21922     },
21923
21924     contains: function(obj) {
21925         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
21926         return obj[0][0] >= this[0][0] &&
21927                obj[0][1] >= this[0][1] &&
21928                obj[1][0] <= this[1][0] &&
21929                obj[1][1] <= this[1][1];
21930     },
21931
21932     intersects: function(obj) {
21933         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
21934         return obj[0][0] <= this[1][0] &&
21935                obj[0][1] <= this[1][1] &&
21936                obj[1][0] >= this[0][0] &&
21937                obj[1][1] >= this[0][1];
21938     },
21939
21940     intersection: function(obj) {
21941         if (!this.intersects(obj)) return new iD.geo.Extent();
21942         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
21943                                   Math.max(obj[0][1], this[0][1])],
21944                                  [Math.min(obj[1][0], this[1][0]),
21945                                   Math.min(obj[1][1], this[1][1])]);
21946     },
21947
21948     percentContainedIn: function(obj) {
21949         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
21950         var a1 = this.intersection(obj).area(),
21951             a2 = this.area();
21952
21953         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
21954             return 0;
21955         } else {
21956             return a1 / a2;
21957         }
21958     },
21959
21960     padByMeters: function(meters) {
21961         var dLat = iD.geo.metersToLat(meters),
21962             dLon = iD.geo.metersToLon(meters, this.center()[1]);
21963         return iD.geo.Extent(
21964                 [this[0][0] - dLon, this[0][1] - dLat],
21965                 [this[1][0] + dLon, this[1][1] + dLat]);
21966     },
21967
21968     toParam: function() {
21969         return this.rectangle().join(',');
21970     }
21971
21972 });
21973 iD.geo.Turn = function(turn) {
21974     if (!(this instanceof iD.geo.Turn))
21975         return new iD.geo.Turn(turn);
21976     _.extend(this, turn);
21977 };
21978
21979 iD.geo.Intersection = function(graph, vertexId) {
21980     var vertex = graph.entity(vertexId),
21981         parentWays = graph.parentWays(vertex),
21982         coincident = [],
21983         highways = {};
21984
21985     function addHighway(way, adjacentNodeId) {
21986         if (highways[adjacentNodeId]) {
21987             coincident.push(adjacentNodeId);
21988         } else {
21989             highways[adjacentNodeId] = way;
21990         }
21991     }
21992
21993     // Pre-split ways that would need to be split in
21994     // order to add a restriction. The real split will
21995     // happen when the restriction is added.
21996     parentWays.forEach(function(way) {
21997         if (!way.tags.highway || way.isArea() || way.isDegenerate())
21998             return;
21999
22000         var isFirst = (vertexId === way.first()),
22001             isLast = (vertexId === way.last()),
22002             isAffix = (isFirst || isLast),
22003             isClosingNode = (isFirst && isLast);
22004
22005         if (isAffix && !isClosingNode) {
22006             var index = (isFirst ? 1 : way.nodes.length - 2);
22007             addHighway(way, way.nodes[index]);
22008
22009         } else {
22010             var splitIndex, wayA, wayB, indexA, indexB;
22011             if (isClosingNode) {
22012                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
22013                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
22014                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
22015                 indexA = 1;
22016                 indexB = way.nodes.length - 2;
22017             } else {
22018                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
22019                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
22020                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
22021                 indexA = splitIndex - 1;
22022                 indexB = splitIndex + 1;
22023             }
22024             graph = graph.replace(wayA).replace(wayB);
22025             addHighway(wayA, way.nodes[indexA]);
22026             addHighway(wayB, way.nodes[indexB]);
22027         }
22028     });
22029
22030     // remove any ways from this intersection that are coincident
22031     // (i.e. any adjacent node used by more than one intersecting way)
22032     coincident.forEach(function (n) {
22033         delete highways[n];
22034     });
22035
22036
22037     var intersection = {
22038         highways: highways,
22039         ways: _.values(highways),
22040         graph: graph
22041     };
22042
22043     intersection.adjacentNodeId = function(fromWayId) {
22044         return _.find(_.keys(highways), function(k) {
22045             return highways[k].id === fromWayId;
22046         });
22047     };
22048
22049     intersection.turns = function(fromNodeId) {
22050         var start = highways[fromNodeId];
22051         if (!start)
22052             return [];
22053
22054         if (start.first() === vertex.id && start.tags.oneway === 'yes')
22055             return [];
22056         if (start.last() === vertex.id && start.tags.oneway === '-1')
22057             return [];
22058
22059         function withRestriction(turn) {
22060             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
22061                 if (relation.tags.type !== 'restriction')
22062                     return;
22063
22064                 var f = relation.memberByRole('from'),
22065                     t = relation.memberByRole('to'),
22066                     v = relation.memberByRole('via');
22067
22068                 if (f && f.id === turn.from.way &&
22069                     v && v.id === turn.via.node &&
22070                     t && t.id === turn.to.way) {
22071                     turn.restriction = relation.id;
22072                 } else if (/^only_/.test(relation.tags.restriction) &&
22073                     f && f.id === turn.from.way &&
22074                     v && v.id === turn.via.node &&
22075                     t && t.id !== turn.to.way) {
22076                     turn.restriction = relation.id;
22077                     turn.indirect_restriction = true;
22078                 }
22079             });
22080
22081             return iD.geo.Turn(turn);
22082         }
22083
22084         var from = {
22085                 node: fromNodeId,
22086                 way: start.id.split(/-(a|b)/)[0]
22087             },
22088             via = { node: vertex.id },
22089             turns = [];
22090
22091         _.each(highways, function(end, adjacentNodeId) {
22092             if (end === start)
22093                 return;
22094
22095             // backward
22096             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
22097                 turns.push(withRestriction({
22098                     from: from,
22099                     via: via,
22100                     to: {
22101                         node: adjacentNodeId,
22102                         way: end.id.split(/-(a|b)/)[0]
22103                     }
22104                 }));
22105             }
22106
22107             // forward
22108             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
22109                 turns.push(withRestriction({
22110                     from: from,
22111                     via: via,
22112                     to: {
22113                         node: adjacentNodeId,
22114                         way: end.id.split(/-(a|b)/)[0]
22115                     }
22116                 }));
22117             }
22118
22119         });
22120
22121         // U-turn
22122         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
22123             turns.push(withRestriction({
22124                 from: from,
22125                 via: via,
22126                 to: from,
22127                 u: true
22128             }));
22129         }
22130
22131         return turns;
22132     };
22133
22134     return intersection;
22135 };
22136
22137
22138 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
22139     var fromWay = graph.entity(from.way),
22140         fromNode = graph.entity(from.node),
22141         toWay = graph.entity(to.way),
22142         toNode = graph.entity(to.node),
22143         viaNode = graph.entity(via.node),
22144         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
22145             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
22146         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
22147             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
22148         angle = iD.geo.angle(viaNode, fromNode, projection) -
22149                 iD.geo.angle(viaNode, toNode, projection);
22150
22151     angle = angle * 180 / Math.PI;
22152
22153     while (angle < 0)
22154         angle += 360;
22155
22156     if (fromNode === toNode)
22157         return 'no_u_turn';
22158     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
22159         return 'no_u_turn';
22160     if (angle < 158)
22161         return 'no_right_turn';
22162     if (angle > 202)
22163         return 'no_left_turn';
22164
22165     return 'no_straight_on';
22166 };
22167 // For fixing up rendering of multipolygons with tags on the outer member.
22168 // https://github.com/openstreetmap/iD/issues/613
22169 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
22170     if (entity.type !== 'way')
22171         return false;
22172
22173     var parents = graph.parentRelations(entity);
22174     if (parents.length !== 1)
22175         return false;
22176
22177     var parent = parents[0];
22178     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
22179         return false;
22180
22181     var members = parent.members, member;
22182     for (var i = 0; i < members.length; i++) {
22183         member = members[i];
22184         if (member.id === entity.id && member.role && member.role !== 'outer')
22185             return false; // Not outer member
22186         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
22187             return false; // Not a simple multipolygon
22188     }
22189
22190     return parent;
22191 };
22192
22193 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
22194     if (entity.type !== 'way')
22195         return false;
22196
22197     var parents = graph.parentRelations(entity);
22198     if (parents.length !== 1)
22199         return false;
22200
22201     var parent = parents[0];
22202     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
22203         return false;
22204
22205     var members = parent.members, member, outerMember;
22206     for (var i = 0; i < members.length; i++) {
22207         member = members[i];
22208         if (!member.role || member.role === 'outer') {
22209             if (outerMember)
22210                 return false; // Not a simple multipolygon
22211             outerMember = member;
22212         }
22213     }
22214
22215     return outerMember && graph.hasEntity(outerMember.id);
22216 };
22217
22218 // Join `array` into sequences of connecting ways.
22219 //
22220 // Segments which share identical start/end nodes will, as much as possible,
22221 // be connected with each other.
22222 //
22223 // The return value is a nested array. Each constituent array contains elements
22224 // of `array` which have been determined to connect. Each consitituent array
22225 // also has a `nodes` property whose value is an ordered array of member nodes,
22226 // with appropriate order reversal and start/end coordinate de-duplication.
22227 //
22228 // Members of `array` must have, at minimum, `type` and `id` properties.
22229 // Thus either an array of `iD.Way`s or a relation member array may be
22230 // used.
22231 //
22232 // If an member has a `tags` property, its tags will be reversed via
22233 // `iD.actions.Reverse` in the output.
22234 //
22235 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
22236 // false) and non-way members are ignored.
22237 //
22238 iD.geo.joinWays = function(array, graph) {
22239     var joined = [], member, current, nodes, first, last, i, how, what;
22240
22241     array = array.filter(function(member) {
22242         return member.type === 'way' && graph.hasEntity(member.id);
22243     });
22244
22245     function resolve(member) {
22246         return graph.childNodes(graph.entity(member.id));
22247     }
22248
22249     function reverse(member) {
22250         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
22251     }
22252
22253     while (array.length) {
22254         member = array.shift();
22255         current = [member];
22256         current.nodes = nodes = resolve(member).slice();
22257         joined.push(current);
22258
22259         while (array.length && _.first(nodes) !== _.last(nodes)) {
22260             first = _.first(nodes);
22261             last  = _.last(nodes);
22262
22263             for (i = 0; i < array.length; i++) {
22264                 member = array[i];
22265                 what = resolve(member);
22266
22267                 if (last === _.first(what)) {
22268                     how  = nodes.push;
22269                     what = what.slice(1);
22270                     break;
22271                 } else if (last === _.last(what)) {
22272                     how  = nodes.push;
22273                     what = what.slice(0, -1).reverse();
22274                     member = reverse(member);
22275                     break;
22276                 } else if (first === _.last(what)) {
22277                     how  = nodes.unshift;
22278                     what = what.slice(0, -1);
22279                     break;
22280                 } else if (first === _.first(what)) {
22281                     how  = nodes.unshift;
22282                     what = what.slice(1).reverse();
22283                     member = reverse(member);
22284                     break;
22285                 } else {
22286                     what = how = null;
22287                 }
22288             }
22289
22290             if (!what)
22291                 break; // No more joinable ways.
22292
22293             how.apply(current, [member]);
22294             how.apply(nodes, what);
22295
22296             array.splice(i, 1);
22297         }
22298     }
22299
22300     return joined;
22301 };
22302 /*
22303     Bypasses features of D3's default projection stream pipeline that are unnecessary:
22304     * Antimeridian clipping
22305     * Spherical rotation
22306     * Resampling
22307 */
22308 iD.geo.RawMercator = function () {
22309     var project = d3.geo.mercator.raw,
22310         k = 512 / Math.PI, // scale
22311         x = 0, y = 0, // translate
22312         clipExtent = [[0, 0], [0, 0]];
22313
22314     function projection(point) {
22315         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
22316         return [point[0] * k + x, y - point[1] * k];
22317     }
22318
22319     projection.invert = function(point) {
22320         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
22321         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
22322     };
22323
22324     projection.scale = function(_) {
22325         if (!arguments.length) return k;
22326         k = +_;
22327         return projection;
22328     };
22329
22330     projection.translate = function(_) {
22331         if (!arguments.length) return [x, y];
22332         x = +_[0];
22333         y = +_[1];
22334         return projection;
22335     };
22336
22337     projection.clipExtent = function(_) {
22338         if (!arguments.length) return clipExtent;
22339         clipExtent = _;
22340         return projection;
22341     };
22342
22343     projection.stream = d3.geo.transform({
22344         point: function(x, y) {
22345             x = projection([x, y]);
22346             this.stream.point(x[0], x[1]);
22347         }
22348     }).stream;
22349
22350     return projection;
22351 };
22352 iD.actions = {};
22353 iD.actions.AddEntity = function(way) {
22354     return function(graph) {
22355         return graph.replace(way);
22356     };
22357 };
22358 iD.actions.AddMember = function(relationId, member, memberIndex) {
22359     return function(graph) {
22360         var relation = graph.entity(relationId);
22361
22362         if (isNaN(memberIndex) && member.type === 'way') {
22363             var members = relation.indexedMembers();
22364             members.push(member);
22365
22366             var joined = iD.geo.joinWays(members, graph);
22367             for (var i = 0; i < joined.length; i++) {
22368                 var segment = joined[i];
22369                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
22370                     if (segment[j] !== member)
22371                         continue;
22372
22373                     if (j === 0) {
22374                         memberIndex = segment[j + 1].index;
22375                     } else if (j === segment.length - 1) {
22376                         memberIndex = segment[j - 1].index + 1;
22377                     } else {
22378                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
22379                     }
22380                 }
22381             }
22382         }
22383
22384         return graph.replace(relation.addMember(member, memberIndex));
22385     };
22386 };
22387 iD.actions.AddMidpoint = function(midpoint, node) {
22388     return function(graph) {
22389         graph = graph.replace(node.move(midpoint.loc));
22390
22391         var parents = _.intersection(
22392             graph.parentWays(graph.entity(midpoint.edge[0])),
22393             graph.parentWays(graph.entity(midpoint.edge[1])));
22394
22395         parents.forEach(function(way) {
22396             for (var i = 0; i < way.nodes.length - 1; i++) {
22397                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
22398                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
22399
22400                     // Add only one midpoint on doubled-back segments,
22401                     // turning them into self-intersections.
22402                     return;
22403                 }
22404             }
22405         });
22406
22407         return graph;
22408     };
22409 };
22410 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
22411 iD.actions.AddVertex = function(wayId, nodeId, index) {
22412     return function(graph) {
22413         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
22414     };
22415 };
22416 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
22417     return function(graph) {
22418         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
22419     };
22420 };
22421 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
22422     return function(graph) {
22423         var entity = graph.entity(entityId),
22424             geometry = entity.geometry(graph),
22425             tags = entity.tags;
22426
22427         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
22428         if (newPreset) tags = newPreset.applyTags(tags, geometry);
22429
22430         return graph.replace(entity.update({tags: tags}));
22431     };
22432 };
22433 iD.actions.ChangeTags = function(entityId, tags) {
22434     return function(graph) {
22435         var entity = graph.entity(entityId);
22436         return graph.replace(entity.update({tags: tags}));
22437     };
22438 };
22439 iD.actions.Circularize = function(wayId, projection, maxAngle) {
22440     maxAngle = (maxAngle || 20) * Math.PI / 180;
22441
22442     var action = function(graph) {
22443         var way = graph.entity(wayId);
22444
22445         if (!way.isConvex(graph)) {
22446             graph = action.makeConvex(graph);
22447         }
22448
22449         var nodes = _.uniq(graph.childNodes(way)),
22450             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
22451             points = nodes.map(function(n) { return projection(n.loc); }),
22452             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
22453             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
22454             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
22455             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
22456             ids;
22457
22458         // we need atleast two key nodes for the algorithm to work
22459         if (!keyNodes.length) {
22460             keyNodes = [nodes[0]];
22461             keyPoints = [points[0]];
22462         }
22463
22464         if (keyNodes.length === 1) {
22465             var index = nodes.indexOf(keyNodes[0]),
22466                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
22467
22468             keyNodes.push(nodes[oppositeIndex]);
22469             keyPoints.push(points[oppositeIndex]);
22470         }
22471
22472         // key points and nodes are those connected to the ways,
22473         // they are projected onto the circle, inbetween nodes are moved
22474         // to constant intervals between key nodes, extra inbetween nodes are
22475         // added if necessary.
22476         for (var i = 0; i < keyPoints.length; i++) {
22477             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
22478                 startNode = keyNodes[i],
22479                 endNode = keyNodes[nextKeyNodeIndex],
22480                 startNodeIndex = nodes.indexOf(startNode),
22481                 endNodeIndex = nodes.indexOf(endNode),
22482                 numberNewPoints = -1,
22483                 indexRange = endNodeIndex - startNodeIndex,
22484                 distance, totalAngle, eachAngle, startAngle, endAngle,
22485                 angle, loc, node, j,
22486                 inBetweenNodes = [];
22487
22488             if (indexRange < 0) {
22489                 indexRange += nodes.length;
22490             }
22491
22492             // position this key node
22493             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
22494             if (distance === 0) { distance = 1e-4; }
22495             keyPoints[i] = [
22496                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
22497                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
22498             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
22499
22500             // figure out the between delta angle we want to match to
22501             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
22502             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
22503             totalAngle = endAngle - startAngle;
22504
22505             // detects looping around -pi/pi
22506             if (totalAngle * sign > 0) {
22507                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
22508             }
22509
22510             do {
22511                 numberNewPoints++;
22512                 eachAngle = totalAngle / (indexRange + numberNewPoints);
22513             } while (Math.abs(eachAngle) > maxAngle);
22514
22515             // move existing points
22516             for (j = 1; j < indexRange; j++) {
22517                 angle = startAngle + j * eachAngle;
22518                 loc = projection.invert([
22519                     centroid[0] + Math.cos(angle)*radius,
22520                     centroid[1] + Math.sin(angle)*radius]);
22521
22522                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
22523                 graph = graph.replace(node);
22524             }
22525
22526             // add new inbetween nodes if necessary
22527             for (j = 0; j < numberNewPoints; j++) {
22528                 angle = startAngle + (indexRange + j) * eachAngle;
22529                 loc = projection.invert([
22530                     centroid[0] + Math.cos(angle) * radius,
22531                     centroid[1] + Math.sin(angle) * radius]);
22532
22533                 node = iD.Node({loc: loc});
22534                 graph = graph.replace(node);
22535
22536                 nodes.splice(endNodeIndex + j, 0, node);
22537                 inBetweenNodes.push(node.id);
22538             }
22539
22540             // Check for other ways that share these keyNodes..
22541             // If keyNodes are adjacent in both ways,
22542             // we can add inBetween nodes to that shared way too..
22543             if (indexRange === 1 && inBetweenNodes.length) {
22544                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
22545                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
22546                     wayDirection1 = (endIndex1 - startIndex1);
22547                 if (wayDirection1 < -1) { wayDirection1 = 1; }
22548
22549                 /* eslint-disable no-loop-func */
22550                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
22551                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
22552                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
22553                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
22554                             wayDirection2 = (endIndex2 - startIndex2),
22555                             insertAt = endIndex2;
22556                         if (wayDirection2 < -1) { wayDirection2 = 1; }
22557
22558                         if (wayDirection1 !== wayDirection2) {
22559                             inBetweenNodes.reverse();
22560                             insertAt = startIndex2;
22561                         }
22562                         for (j = 0; j < inBetweenNodes.length; j++) {
22563                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
22564                         }
22565                         graph = graph.replace(sharedWay);
22566                     }
22567                 });
22568                 /* eslint-enable no-loop-func */
22569             }
22570
22571         }
22572
22573         // update the way to have all the new nodes
22574         ids = nodes.map(function(n) { return n.id; });
22575         ids.push(ids[0]);
22576
22577         way = way.update({nodes: ids});
22578         graph = graph.replace(way);
22579
22580         return graph;
22581     };
22582
22583     action.makeConvex = function(graph) {
22584         var way = graph.entity(wayId),
22585             nodes = _.uniq(graph.childNodes(way)),
22586             points = nodes.map(function(n) { return projection(n.loc); }),
22587             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
22588             hull = d3.geom.hull(points);
22589
22590         // D3 convex hulls go counterclockwise..
22591         if (sign === -1) {
22592             nodes.reverse();
22593             points.reverse();
22594         }
22595
22596         for (var i = 0; i < hull.length - 1; i++) {
22597             var startIndex = points.indexOf(hull[i]),
22598                 endIndex = points.indexOf(hull[i+1]),
22599                 indexRange = (endIndex - startIndex);
22600
22601             if (indexRange < 0) {
22602                 indexRange += nodes.length;
22603             }
22604
22605             // move interior nodes to the surface of the convex hull..
22606             for (var j = 1; j < indexRange; j++) {
22607                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
22608                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22609                 graph = graph.replace(node);
22610             }
22611         }
22612         return graph;
22613     };
22614
22615     action.disabled = function(graph) {
22616         if (!graph.entity(wayId).isClosed())
22617             return 'not_closed';
22618     };
22619
22620     return action;
22621 };
22622 // Connect the ways at the given nodes.
22623 //
22624 // The last node will survive. All other nodes will be replaced with
22625 // the surviving node in parent ways, and then removed.
22626 //
22627 // Tags and relation memberships of of non-surviving nodes are merged
22628 // to the survivor.
22629 //
22630 // This is the inverse of `iD.actions.Disconnect`.
22631 //
22632 // Reference:
22633 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22634 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22635 //
22636 iD.actions.Connect = function(nodeIds) {
22637     return function(graph) {
22638         var survivor = graph.entity(_.last(nodeIds));
22639
22640         for (var i = 0; i < nodeIds.length - 1; i++) {
22641             var node = graph.entity(nodeIds[i]);
22642
22643             /* eslint-disable no-loop-func */
22644             graph.parentWays(node).forEach(function(parent) {
22645                 if (!parent.areAdjacent(node.id, survivor.id)) {
22646                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
22647                 }
22648             });
22649
22650             graph.parentRelations(node).forEach(function(parent) {
22651                 graph = graph.replace(parent.replaceMember(node, survivor));
22652             });
22653             /* eslint-enable no-loop-func */
22654
22655             survivor = survivor.mergeTags(node.tags);
22656             graph = iD.actions.DeleteNode(node.id)(graph);
22657         }
22658
22659         graph = graph.replace(survivor);
22660
22661         return graph;
22662     };
22663 };
22664 iD.actions.CopyEntities = function(ids, fromGraph) {
22665     var copies = {};
22666
22667     var action = function(graph) {
22668         ids.forEach(function(id) {
22669             fromGraph.entity(id).copy(fromGraph, copies);
22670         });
22671
22672         for (var id in copies) {
22673             graph = graph.replace(copies[id]);
22674         }
22675
22676         return graph;
22677     };
22678
22679     action.copies = function() {
22680         return copies;
22681     };
22682
22683     return action;
22684 };
22685 iD.actions.DeleteMember = function(relationId, memberIndex) {
22686     return function(graph) {
22687         var relation = graph.entity(relationId)
22688             .removeMember(memberIndex);
22689
22690         graph = graph.replace(relation);
22691
22692         if (relation.isDegenerate())
22693             graph = iD.actions.DeleteRelation(relation.id)(graph);
22694
22695         return graph;
22696     };
22697 };
22698 iD.actions.DeleteMultiple = function(ids) {
22699     var actions = {
22700         way: iD.actions.DeleteWay,
22701         node: iD.actions.DeleteNode,
22702         relation: iD.actions.DeleteRelation
22703     };
22704
22705     var action = function(graph) {
22706         ids.forEach(function(id) {
22707             if (graph.hasEntity(id)) { // It may have been deleted aready.
22708                 graph = actions[graph.entity(id).type](id)(graph);
22709             }
22710         });
22711
22712         return graph;
22713     };
22714
22715     action.disabled = function(graph) {
22716         for (var i = 0; i < ids.length; i++) {
22717             var id = ids[i],
22718                 disabled = actions[graph.entity(id).type](id).disabled(graph);
22719             if (disabled) return disabled;
22720         }
22721     };
22722
22723     return action;
22724 };
22725 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22726 iD.actions.DeleteNode = function(nodeId) {
22727     var action = function(graph) {
22728         var node = graph.entity(nodeId);
22729
22730         graph.parentWays(node)
22731             .forEach(function(parent) {
22732                 parent = parent.removeNode(nodeId);
22733                 graph = graph.replace(parent);
22734
22735                 if (parent.isDegenerate()) {
22736                     graph = iD.actions.DeleteWay(parent.id)(graph);
22737                 }
22738             });
22739
22740         graph.parentRelations(node)
22741             .forEach(function(parent) {
22742                 parent = parent.removeMembersWithID(nodeId);
22743                 graph = graph.replace(parent);
22744
22745                 if (parent.isDegenerate()) {
22746                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22747                 }
22748             });
22749
22750         return graph.remove(node);
22751     };
22752
22753     action.disabled = function() {
22754         return false;
22755     };
22756
22757     return action;
22758 };
22759 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22760 iD.actions.DeleteRelation = function(relationId) {
22761     function deleteEntity(entity, graph) {
22762         return !graph.parentWays(entity).length &&
22763             !graph.parentRelations(entity).length &&
22764             !entity.hasInterestingTags();
22765     }
22766
22767     var action = function(graph) {
22768         var relation = graph.entity(relationId);
22769
22770         graph.parentRelations(relation)
22771             .forEach(function(parent) {
22772                 parent = parent.removeMembersWithID(relationId);
22773                 graph = graph.replace(parent);
22774
22775                 if (parent.isDegenerate()) {
22776                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22777                 }
22778             });
22779
22780         _.uniq(_.map(relation.members, 'id')).forEach(function(memberId) {
22781             graph = graph.replace(relation.removeMembersWithID(memberId));
22782
22783             var entity = graph.entity(memberId);
22784             if (deleteEntity(entity, graph)) {
22785                 graph = iD.actions.DeleteMultiple([memberId])(graph);
22786             }
22787         });
22788
22789         return graph.remove(relation);
22790     };
22791
22792     action.disabled = function(graph) {
22793         if (!graph.entity(relationId).isComplete(graph))
22794             return 'incomplete_relation';
22795     };
22796
22797     return action;
22798 };
22799 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22800 iD.actions.DeleteWay = function(wayId) {
22801     function deleteNode(node, graph) {
22802         return !graph.parentWays(node).length &&
22803             !graph.parentRelations(node).length &&
22804             !node.hasInterestingTags();
22805     }
22806
22807     var action = function(graph) {
22808         var way = graph.entity(wayId);
22809
22810         graph.parentRelations(way)
22811             .forEach(function(parent) {
22812                 parent = parent.removeMembersWithID(wayId);
22813                 graph = graph.replace(parent);
22814
22815                 if (parent.isDegenerate()) {
22816                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22817                 }
22818             });
22819
22820         _.uniq(way.nodes).forEach(function(nodeId) {
22821             graph = graph.replace(way.removeNode(nodeId));
22822
22823             var node = graph.entity(nodeId);
22824             if (deleteNode(node, graph)) {
22825                 graph = graph.remove(node);
22826             }
22827         });
22828
22829         return graph.remove(way);
22830     };
22831
22832     action.disabled = function(graph) {
22833         var disabled = false;
22834
22835         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
22836             var type = parent.tags.type,
22837                 role = parent.memberById(wayId).role || 'outer';
22838             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
22839                 disabled = 'part_of_relation';
22840             }
22841         });
22842
22843         return disabled;
22844     };
22845
22846     return action;
22847 };
22848 iD.actions.DeprecateTags = function(entityId) {
22849     return function(graph) {
22850         var entity = graph.entity(entityId),
22851             newtags = _.clone(entity.tags),
22852             change = false,
22853             rule;
22854
22855         // This handles deprecated tags with a single condition
22856         for (var i = 0; i < iD.data.deprecated.length; i++) {
22857
22858             rule = iD.data.deprecated[i];
22859             var match = _.toPairs(rule.old)[0],
22860                 replacements = rule.replace ? _.toPairs(rule.replace) : null;
22861
22862             if (entity.tags[match[0]] && match[1] === '*') {
22863
22864                 var value = entity.tags[match[0]];
22865                 if (replacements && !newtags[replacements[0][0]]) {
22866                     newtags[replacements[0][0]] = value;
22867                 }
22868                 delete newtags[match[0]];
22869                 change = true;
22870
22871             } else if (entity.tags[match[0]] === match[1]) {
22872                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
22873                 change = true;
22874             }
22875         }
22876
22877         if (change) {
22878             return graph.replace(entity.update({tags: newtags}));
22879         } else {
22880             return graph;
22881         }
22882     };
22883 };
22884 iD.actions.DiscardTags = function(difference) {
22885     return function(graph) {
22886         function discardTags(entity) {
22887             if (!_.isEmpty(entity.tags)) {
22888                 var tags = {};
22889                 _.each(entity.tags, function(v, k) {
22890                     if (v) tags[k] = v;
22891                 });
22892
22893                 graph = graph.replace(entity.update({
22894                     tags: _.omit(tags, iD.data.discarded)
22895                 }));
22896             }
22897         }
22898
22899         difference.modified().forEach(discardTags);
22900         difference.created().forEach(discardTags);
22901
22902         return graph;
22903     };
22904 };
22905 // Disconect the ways at the given node.
22906 //
22907 // Optionally, disconnect only the given ways.
22908 //
22909 // For testing convenience, accepts an ID to assign to the (first) new node.
22910 // Normally, this will be undefined and the way will automatically
22911 // be assigned a new ID.
22912 //
22913 // This is the inverse of `iD.actions.Connect`.
22914 //
22915 // Reference:
22916 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
22917 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
22918 //
22919 iD.actions.Disconnect = function(nodeId, newNodeId) {
22920     var wayIds;
22921
22922     var action = function(graph) {
22923         var node = graph.entity(nodeId),
22924             connections = action.connections(graph);
22925
22926         connections.forEach(function(connection) {
22927             var way = graph.entity(connection.wayID),
22928                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
22929
22930             graph = graph.replace(newNode);
22931             if (connection.index === 0 && way.isArea()) {
22932                 // replace shared node with shared node..
22933                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
22934             } else {
22935                 // replace shared node with multiple new nodes..
22936                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
22937             }
22938         });
22939
22940         return graph;
22941     };
22942
22943     action.connections = function(graph) {
22944         var candidates = [],
22945             keeping = false,
22946             parentWays = graph.parentWays(graph.entity(nodeId));
22947
22948         parentWays.forEach(function(way) {
22949             if (wayIds && wayIds.indexOf(way.id) === -1) {
22950                 keeping = true;
22951                 return;
22952             }
22953             if (way.isArea() && (way.nodes[0] === nodeId)) {
22954                 candidates.push({wayID: way.id, index: 0});
22955             } else {
22956                 way.nodes.forEach(function(waynode, index) {
22957                     if (waynode === nodeId) {
22958                         candidates.push({wayID: way.id, index: index});
22959                     }
22960                 });
22961             }
22962         });
22963
22964         return keeping ? candidates : candidates.slice(1);
22965     };
22966
22967     action.disabled = function(graph) {
22968         var connections = action.connections(graph);
22969         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
22970             return 'not_connected';
22971
22972         var parentWays = graph.parentWays(graph.entity(nodeId)),
22973             seenRelationIds = {},
22974             sharedRelation;
22975
22976         parentWays.forEach(function(way) {
22977             if (wayIds && wayIds.indexOf(way.id) === -1)
22978                 return;
22979
22980             var relations = graph.parentRelations(way);
22981             relations.forEach(function(relation) {
22982                 if (relation.id in seenRelationIds) {
22983                     sharedRelation = relation;
22984                 } else {
22985                     seenRelationIds[relation.id] = true;
22986                 }
22987             });
22988         });
22989
22990         if (sharedRelation)
22991             return 'relation';
22992     };
22993
22994     action.limitWays = function(_) {
22995         if (!arguments.length) return wayIds;
22996         wayIds = _;
22997         return action;
22998     };
22999
23000     return action;
23001 };
23002 // Join ways at the end node they share.
23003 //
23004 // This is the inverse of `iD.actions.Split`.
23005 //
23006 // Reference:
23007 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
23008 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
23009 //
23010 iD.actions.Join = function(ids) {
23011
23012     function groupEntitiesByGeometry(graph) {
23013         var entities = ids.map(function(id) { return graph.entity(id); });
23014         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
23015     }
23016
23017     var action = function(graph) {
23018         var ways = ids.map(graph.entity, graph),
23019             survivor = ways[0];
23020
23021         // Prefer to keep an existing way.
23022         for (var i = 0; i < ways.length; i++) {
23023             if (!ways[i].isNew()) {
23024                 survivor = ways[i];
23025                 break;
23026             }
23027         }
23028
23029         var joined = iD.geo.joinWays(ways, graph)[0];
23030
23031         survivor = survivor.update({nodes: _.map(joined.nodes, 'id')});
23032         graph = graph.replace(survivor);
23033
23034         joined.forEach(function(way) {
23035             if (way.id === survivor.id)
23036                 return;
23037
23038             graph.parentRelations(way).forEach(function(parent) {
23039                 graph = graph.replace(parent.replaceMember(way, survivor));
23040             });
23041
23042             survivor = survivor.mergeTags(way.tags);
23043
23044             graph = graph.replace(survivor);
23045             graph = iD.actions.DeleteWay(way.id)(graph);
23046         });
23047
23048         return graph;
23049     };
23050
23051     action.disabled = function(graph) {
23052         var geometries = groupEntitiesByGeometry(graph);
23053         if (ids.length < 2 || ids.length !== geometries.line.length)
23054             return 'not_eligible';
23055
23056         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
23057         if (joined.length > 1)
23058             return 'not_adjacent';
23059
23060         var nodeIds = _.map(joined[0].nodes, 'id').slice(1, -1),
23061             relation,
23062             tags = {},
23063             conflicting = false;
23064
23065         joined[0].forEach(function(way) {
23066             var parents = graph.parentRelations(way);
23067             parents.forEach(function(parent) {
23068                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
23069                     relation = parent;
23070             });
23071
23072             for (var k in way.tags) {
23073                 if (!(k in tags)) {
23074                     tags[k] = way.tags[k];
23075                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
23076                     conflicting = true;
23077                 }
23078             }
23079         });
23080
23081         if (relation)
23082             return 'restriction';
23083
23084         if (conflicting)
23085             return 'conflicting_tags';
23086     };
23087
23088     return action;
23089 };
23090 iD.actions.Merge = function(ids) {
23091     function groupEntitiesByGeometry(graph) {
23092         var entities = ids.map(function(id) { return graph.entity(id); });
23093         return _.extend({point: [], area: [], line: [], relation: []},
23094             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
23095     }
23096
23097     var action = function(graph) {
23098         var geometries = groupEntitiesByGeometry(graph),
23099             target = geometries.area[0] || geometries.line[0],
23100             points = geometries.point;
23101
23102         points.forEach(function(point) {
23103             target = target.mergeTags(point.tags);
23104
23105             graph.parentRelations(point).forEach(function(parent) {
23106                 graph = graph.replace(parent.replaceMember(point, target));
23107             });
23108
23109             graph = graph.remove(point);
23110         });
23111
23112         graph = graph.replace(target);
23113
23114         return graph;
23115     };
23116
23117     action.disabled = function(graph) {
23118         var geometries = groupEntitiesByGeometry(graph);
23119         if (geometries.point.length === 0 ||
23120             (geometries.area.length + geometries.line.length) !== 1 ||
23121             geometries.relation.length !== 0)
23122             return 'not_eligible';
23123     };
23124
23125     return action;
23126 };
23127 iD.actions.MergePolygon = function(ids, newRelationId) {
23128
23129     function groupEntities(graph) {
23130         var entities = ids.map(function (id) { return graph.entity(id); });
23131         return _.extend({
23132                 closedWay: [],
23133                 multipolygon: [],
23134                 other: []
23135             }, _.groupBy(entities, function(entity) {
23136                 if (entity.type === 'way' && entity.isClosed()) {
23137                     return 'closedWay';
23138                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
23139                     return 'multipolygon';
23140                 } else {
23141                     return 'other';
23142                 }
23143             }));
23144     }
23145
23146     var action = function(graph) {
23147         var entities = groupEntities(graph);
23148
23149         // An array representing all the polygons that are part of the multipolygon.
23150         //
23151         // Each element is itself an array of objects with an id property, and has a
23152         // locs property which is an array of the locations forming the polygon.
23153         var polygons = entities.multipolygon.reduce(function(polygons, m) {
23154             return polygons.concat(iD.geo.joinWays(m.members, graph));
23155         }, []).concat(entities.closedWay.map(function(d) {
23156             var member = [{id: d.id}];
23157             member.nodes = graph.childNodes(d);
23158             return member;
23159         }));
23160
23161         // contained is an array of arrays of boolean values,
23162         // where contained[j][k] is true iff the jth way is
23163         // contained by the kth way.
23164         var contained = polygons.map(function(w, i) {
23165             return polygons.map(function(d, n) {
23166                 if (i === n) return null;
23167                 return iD.geo.polygonContainsPolygon(
23168                     _.map(d.nodes, 'loc'),
23169                     _.map(w.nodes, 'loc'));
23170             });
23171         });
23172
23173         // Sort all polygons as either outer or inner ways
23174         var members = [],
23175             outer = true;
23176
23177         while (polygons.length) {
23178             extractUncontained(polygons);
23179             polygons = polygons.filter(isContained);
23180             contained = contained.filter(isContained).map(filterContained);
23181         }
23182
23183         function isContained(d, i) {
23184             return _.some(contained[i]);
23185         }
23186
23187         function filterContained(d) {
23188             return d.filter(isContained);
23189         }
23190
23191         function extractUncontained(polygons) {
23192             polygons.forEach(function(d, i) {
23193                 if (!isContained(d, i)) {
23194                     d.forEach(function(member) {
23195                         members.push({
23196                             type: 'way',
23197                             id: member.id,
23198                             role: outer ? 'outer' : 'inner'
23199                         });
23200                     });
23201                 }
23202             });
23203             outer = !outer;
23204         }
23205
23206         // Move all tags to one relation
23207         var relation = entities.multipolygon[0] ||
23208             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
23209
23210         entities.multipolygon.slice(1).forEach(function(m) {
23211             relation = relation.mergeTags(m.tags);
23212             graph = graph.remove(m);
23213         });
23214
23215         entities.closedWay.forEach(function(way) {
23216             function isThisOuter(m) {
23217                 return m.id === way.id && m.role !== 'inner';
23218             }
23219             if (members.some(isThisOuter)) {
23220                 relation = relation.mergeTags(way.tags);
23221                 graph = graph.replace(way.update({ tags: {} }));
23222             }
23223         });
23224
23225         return graph.replace(relation.update({
23226             members: members,
23227             tags: _.omit(relation.tags, 'area')
23228         }));
23229     };
23230
23231     action.disabled = function(graph) {
23232         var entities = groupEntities(graph);
23233         if (entities.other.length > 0 ||
23234             entities.closedWay.length + entities.multipolygon.length < 2)
23235             return 'not_eligible';
23236         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
23237             return 'incomplete_relation';
23238     };
23239
23240     return action;
23241 };
23242 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
23243     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
23244         conflicts = [];
23245
23246     function user(d) {
23247         return _.isFunction(formatUser) ? formatUser(d) : d;
23248     }
23249
23250
23251     function mergeLocation(remote, target) {
23252         function pointEqual(a, b) {
23253             var epsilon = 1e-6;
23254             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
23255         }
23256
23257         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
23258             return target;
23259         }
23260         if (option === 'force_remote') {
23261             return target.update({loc: remote.loc});
23262         }
23263
23264         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
23265         return target;
23266     }
23267
23268
23269     function mergeNodes(base, remote, target) {
23270         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
23271             return target;
23272         }
23273         if (option === 'force_remote') {
23274             return target.update({nodes: remote.nodes});
23275         }
23276
23277         var ccount = conflicts.length,
23278             o = base.nodes || [],
23279             a = target.nodes || [],
23280             b = remote.nodes || [],
23281             nodes = [],
23282             hunks = Diff3.diff3_merge(a, o, b, true);
23283
23284         for (var i = 0; i < hunks.length; i++) {
23285             var hunk = hunks[i];
23286             if (hunk.ok) {
23287                 nodes.push.apply(nodes, hunk.ok);
23288             } else {
23289                 // for all conflicts, we can assume c.a !== c.b
23290                 // because `diff3_merge` called with `true` option to exclude false conflicts..
23291                 var c = hunk.conflict;
23292                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
23293                     nodes.push.apply(nodes, c.b);
23294                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
23295                     nodes.push.apply(nodes, c.a);
23296                 } else {       // changed both locally and remotely
23297                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
23298                     break;
23299                 }
23300             }
23301         }
23302
23303         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
23304     }
23305
23306
23307     function mergeChildren(targetWay, children, updates, graph) {
23308         function isUsed(node, targetWay) {
23309             var parentWays = _.map(graph.parentWays(node), 'id');
23310             return node.hasInterestingTags() ||
23311                 _.without(parentWays, targetWay.id).length > 0 ||
23312                 graph.parentRelations(node).length > 0;
23313         }
23314
23315         var ccount = conflicts.length;
23316
23317         for (var i = 0; i < children.length; i++) {
23318             var id = children[i],
23319                 node = graph.hasEntity(id);
23320
23321             // remove unused childNodes..
23322             if (targetWay.nodes.indexOf(id) === -1) {
23323                 if (node && !isUsed(node, targetWay)) {
23324                     updates.removeIds.push(id);
23325                 }
23326                 continue;
23327             }
23328
23329             // restore used childNodes..
23330             var local = localGraph.hasEntity(id),
23331                 remote = remoteGraph.hasEntity(id),
23332                 target;
23333
23334             if (option === 'force_remote' && remote && remote.visible) {
23335                 updates.replacements.push(remote);
23336
23337             } else if (option === 'force_local' && local) {
23338                 target = iD.Entity(local);
23339                 if (remote) {
23340                     target = target.update({ version: remote.version });
23341                 }
23342                 updates.replacements.push(target);
23343
23344             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
23345                 target = iD.Entity(local, { version: remote.version });
23346                 if (remote.visible) {
23347                     target = mergeLocation(remote, target);
23348                 } else {
23349                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
23350                 }
23351
23352                 if (conflicts.length !== ccount) break;
23353                 updates.replacements.push(target);
23354             }
23355         }
23356
23357         return targetWay;
23358     }
23359
23360
23361     function updateChildren(updates, graph) {
23362         for (var i = 0; i < updates.replacements.length; i++) {
23363             graph = graph.replace(updates.replacements[i]);
23364         }
23365         if (updates.removeIds.length) {
23366             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
23367         }
23368         return graph;
23369     }
23370
23371
23372     function mergeMembers(remote, target) {
23373         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
23374             return target;
23375         }
23376         if (option === 'force_remote') {
23377             return target.update({members: remote.members});
23378         }
23379
23380         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
23381         return target;
23382     }
23383
23384
23385     function mergeTags(base, remote, target) {
23386         function ignoreKey(k) {
23387             return _.includes(iD.data.discarded, k);
23388         }
23389
23390         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
23391             return target;
23392         }
23393         if (option === 'force_remote') {
23394             return target.update({tags: remote.tags});
23395         }
23396
23397         var ccount = conflicts.length,
23398             o = base.tags || {},
23399             a = target.tags || {},
23400             b = remote.tags || {},
23401             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
23402             tags = _.clone(a),
23403             changed = false;
23404
23405         for (var i = 0; i < keys.length; i++) {
23406             var k = keys[i];
23407
23408             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
23409                 if (o[k] !== a[k]) {      // changed locally..
23410                     conflicts.push(t('merge_remote_changes.conflict.tags',
23411                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
23412
23413                 } else {                  // unchanged locally, accept remote change..
23414                     if (b.hasOwnProperty(k)) {
23415                         tags[k] = b[k];
23416                     } else {
23417                         delete tags[k];
23418                     }
23419                     changed = true;
23420                 }
23421             }
23422         }
23423
23424         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
23425     }
23426
23427
23428     //  `graph.base()` is the common ancestor of the two graphs.
23429     //  `localGraph` contains user's edits up to saving
23430     //  `remoteGraph` contains remote edits to modified nodes
23431     //  `graph` must be a descendent of `localGraph` and may include
23432     //      some conflict resolution actions performed on it.
23433     //
23434     //                  --- ... --- `localGraph` -- ... -- `graph`
23435     //                 /
23436     //  `graph.base()` --- ... --- `remoteGraph`
23437     //
23438     var action = function(graph) {
23439         var updates = { replacements: [], removeIds: [] },
23440             base = graph.base().entities[id],
23441             local = localGraph.entity(id),
23442             remote = remoteGraph.entity(id),
23443             target = iD.Entity(local, { version: remote.version });
23444
23445         // delete/undelete
23446         if (!remote.visible) {
23447             if (option === 'force_remote') {
23448                 return iD.actions.DeleteMultiple([id])(graph);
23449
23450             } else if (option === 'force_local') {
23451                 if (target.type === 'way') {
23452                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
23453                     graph = updateChildren(updates, graph);
23454                 }
23455                 return graph.replace(target);
23456
23457             } else {
23458                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
23459                 return graph;  // do nothing
23460             }
23461         }
23462
23463         // merge
23464         if (target.type === 'node') {
23465             target = mergeLocation(remote, target);
23466
23467         } else if (target.type === 'way') {
23468             // pull in any child nodes that may not be present locally..
23469             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
23470             target = mergeNodes(base, remote, target);
23471             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
23472
23473         } else if (target.type === 'relation') {
23474             target = mergeMembers(remote, target);
23475         }
23476
23477         target = mergeTags(base, remote, target);
23478
23479         if (!conflicts.length) {
23480             graph = updateChildren(updates, graph).replace(target);
23481         }
23482
23483         return graph;
23484     };
23485
23486     action.withOption = function(opt) {
23487         option = opt;
23488         return action;
23489     };
23490
23491     action.conflicts = function() {
23492         return conflicts;
23493     };
23494
23495     return action;
23496 };
23497 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
23498 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
23499 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
23500     var delta = tryDelta;
23501
23502     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
23503     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
23504
23505     function setupCache(graph) {
23506         function canMove(nodeId) {
23507             var parents = _.map(graph.parentWays(graph.entity(nodeId)), 'id');
23508             if (parents.length < 3) return true;
23509
23510             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
23511             var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
23512             if (!parentsMoving) delete cache.moving[nodeId];
23513
23514             return parentsMoving;
23515         }
23516
23517         function cacheEntities(ids) {
23518             _.each(ids, function(id) {
23519                 if (cache.moving[id]) return;
23520                 cache.moving[id] = true;
23521
23522                 var entity = graph.hasEntity(id);
23523                 if (!entity) return;
23524
23525                 if (entity.type === 'node') {
23526                     cache.nodes.push(id);
23527                     cache.startLoc[id] = entity.loc;
23528                 } else if (entity.type === 'way') {
23529                     cache.ways.push(id);
23530                     cacheEntities(entity.nodes);
23531                 } else {
23532                     cacheEntities(_.map(entity.members, 'id'));
23533                 }
23534             });
23535         }
23536
23537         function cacheIntersections(ids) {
23538             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
23539
23540             _.each(ids, function(id) {
23541                 // consider only intersections with 1 moved and 1 unmoved way.
23542                 _.each(graph.childNodes(graph.entity(id)), function(node) {
23543                     var parents = graph.parentWays(node);
23544                     if (parents.length !== 2) return;
23545
23546                     var moved = graph.entity(id),
23547                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
23548                     if (!unmoved) return;
23549
23550                     // exclude ways that are overly connected..
23551                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
23552
23553                     if (moved.isArea() || unmoved.isArea()) return;
23554
23555                     cache.intersection[node.id] = {
23556                         nodeId: node.id,
23557                         movedId: moved.id,
23558                         unmovedId: unmoved.id,
23559                         movedIsEP: isEndpoint(moved, node.id),
23560                         unmovedIsEP: isEndpoint(unmoved, node.id)
23561                     };
23562                 });
23563             });
23564         }
23565
23566
23567         if (!cache) {
23568             cache = {};
23569         }
23570         if (!cache.ok) {
23571             cache.moving = {};
23572             cache.intersection = {};
23573             cache.replacedVertex = {};
23574             cache.startLoc = {};
23575             cache.nodes = [];
23576             cache.ways = [];
23577
23578             cacheEntities(moveIds);
23579             cacheIntersections(cache.ways);
23580             cache.nodes = _.filter(cache.nodes, canMove);
23581
23582             cache.ok = true;
23583         }
23584     }
23585
23586
23587     // Place a vertex where the moved vertex used to be, to preserve way shape..
23588     function replaceMovedVertex(nodeId, wayId, graph, delta) {
23589         var way = graph.entity(wayId),
23590             moved = graph.entity(nodeId),
23591             movedIndex = way.nodes.indexOf(nodeId),
23592             len, prevIndex, nextIndex;
23593
23594         if (way.isClosed()) {
23595             len = way.nodes.length - 1;
23596             prevIndex = (movedIndex + len - 1) % len;
23597             nextIndex = (movedIndex + len + 1) % len;
23598         } else {
23599             len = way.nodes.length;
23600             prevIndex = movedIndex - 1;
23601             nextIndex = movedIndex + 1;
23602         }
23603
23604         var prev = graph.hasEntity(way.nodes[prevIndex]),
23605             next = graph.hasEntity(way.nodes[nextIndex]);
23606
23607         // Don't add orig vertex at endpoint..
23608         if (!prev || !next) return graph;
23609
23610         var key = wayId + '_' + nodeId,
23611             orig = cache.replacedVertex[key];
23612         if (!orig) {
23613             orig = iD.Node();
23614             cache.replacedVertex[key] = orig;
23615             cache.startLoc[orig.id] = cache.startLoc[nodeId];
23616         }
23617
23618         var start, end;
23619         if (delta) {
23620             start = projection(cache.startLoc[nodeId]);
23621             end = projection.invert(vecAdd(start, delta));
23622         } else {
23623             end = cache.startLoc[nodeId];
23624         }
23625         orig = orig.move(end);
23626
23627         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
23628                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
23629
23630         // Don't add orig vertex if it would just make a straight line..
23631         if (angle > 175 && angle < 185) return graph;
23632
23633         // Don't add orig vertex if another point is already nearby (within 10m)
23634         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
23635             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
23636
23637         // moving forward or backward along way?
23638         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
23639             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
23640             d1 = iD.geo.pathLength(p1),
23641             d2 = iD.geo.pathLength(p2),
23642             insertAt = (d1 < d2) ? movedIndex : nextIndex;
23643
23644         // moving around closed loop?
23645         if (way.isClosed() && insertAt === 0) insertAt = len;
23646
23647         way = way.addNode(orig.id, insertAt);
23648         return graph.replace(orig).replace(way);
23649     }
23650
23651     // Reorder nodes around intersections that have moved..
23652     function unZorroIntersection(intersection, graph) {
23653         var vertex = graph.entity(intersection.nodeId),
23654             way1 = graph.entity(intersection.movedId),
23655             way2 = graph.entity(intersection.unmovedId),
23656             isEP1 = intersection.movedIsEP,
23657             isEP2 = intersection.unmovedIsEP;
23658
23659         // don't move the vertex if it is the endpoint of both ways.
23660         if (isEP1 && isEP2) return graph;
23661
23662         var nodes1 = _.without(graph.childNodes(way1), vertex),
23663             nodes2 = _.without(graph.childNodes(way2), vertex);
23664
23665         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
23666         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
23667
23668         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
23669             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
23670             loc;
23671
23672         // snap vertex to nearest edge (or some point between them)..
23673         if (!isEP1 && !isEP2) {
23674             var epsilon = 1e-4, maxIter = 10;
23675             for (var i = 0; i < maxIter; i++) {
23676                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
23677                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
23678                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
23679                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
23680             }
23681         } else if (!isEP1) {
23682             loc = edge1.loc;
23683         } else {
23684             loc = edge2.loc;
23685         }
23686
23687         graph = graph.replace(vertex.move(loc));
23688
23689         // if zorro happened, reorder nodes..
23690         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
23691             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
23692             graph = graph.replace(way1);
23693         }
23694         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
23695             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
23696             graph = graph.replace(way2);
23697         }
23698
23699         return graph;
23700     }
23701
23702
23703     function cleanupIntersections(graph) {
23704         _.each(cache.intersection, function(obj) {
23705             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
23706             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
23707             graph = unZorroIntersection(obj, graph);
23708         });
23709
23710         return graph;
23711     }
23712
23713     // check if moving way endpoint can cross an unmoved way, if so limit delta..
23714     function limitDelta(graph) {
23715         _.each(cache.intersection, function(obj) {
23716             if (!obj.movedIsEP) return;
23717
23718             var node = graph.entity(obj.nodeId),
23719                 start = projection(node.loc),
23720                 end = vecAdd(start, delta),
23721                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
23722                 movedPath = _.map(_.map(movedNodes, 'loc'),
23723                     function(loc) { return vecAdd(projection(loc), delta); }),
23724                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
23725                 unmovedPath = _.map(_.map(unmovedNodes, 'loc'), projection),
23726                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
23727
23728             for (var i = 0; i < hits.length; i++) {
23729                 if (_.isEqual(hits[i], end)) continue;
23730                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
23731                 delta = vecSub(projection(edge.loc), start);
23732             }
23733         });
23734     }
23735
23736
23737     var action = function(graph) {
23738         if (delta[0] === 0 && delta[1] === 0) return graph;
23739
23740         setupCache(graph);
23741
23742         if (!_.isEmpty(cache.intersection)) {
23743             limitDelta(graph);
23744         }
23745
23746         _.each(cache.nodes, function(id) {
23747             var node = graph.entity(id),
23748                 start = projection(node.loc),
23749                 end = vecAdd(start, delta);
23750             graph = graph.replace(node.move(projection.invert(end)));
23751         });
23752
23753         if (!_.isEmpty(cache.intersection)) {
23754             graph = cleanupIntersections(graph);
23755         }
23756
23757         return graph;
23758     };
23759
23760     action.disabled = function(graph) {
23761         function incompleteRelation(id) {
23762             var entity = graph.entity(id);
23763             return entity.type === 'relation' && !entity.isComplete(graph);
23764         }
23765
23766         if (_.some(moveIds, incompleteRelation))
23767             return 'incomplete_relation';
23768     };
23769
23770     action.delta = function() {
23771         return delta;
23772     };
23773
23774     return action;
23775 };
23776 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
23777 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
23778 iD.actions.MoveNode = function(nodeId, loc) {
23779     return function(graph) {
23780         return graph.replace(graph.entity(nodeId).move(loc));
23781     };
23782 };
23783 iD.actions.Noop = function() {
23784     return function(graph) {
23785         return graph;
23786     };
23787 };
23788 /*
23789  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
23790  */
23791
23792 iD.actions.Orthogonalize = function(wayId, projection) {
23793     var threshold = 12, // degrees within right or straight to alter
23794         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
23795         upperThreshold = Math.cos(threshold * Math.PI / 180);
23796
23797     var action = function(graph) {
23798         var way = graph.entity(wayId),
23799             nodes = graph.childNodes(way),
23800             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
23801             corner = {i: 0, dotp: 1},
23802             epsilon = 1e-4,
23803             i, j, score, motions;
23804
23805         if (nodes.length === 4) {
23806             for (i = 0; i < 1000; i++) {
23807                 motions = points.map(calcMotion);
23808                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
23809                 score = corner.dotp;
23810                 if (score < epsilon) {
23811                     break;
23812                 }
23813             }
23814
23815             graph = graph.replace(graph.entity(nodes[corner.i].id)
23816                 .move(projection.invert(points[corner.i])));
23817         } else {
23818             var best,
23819                 originalPoints = _.clone(points);
23820             score = Infinity;
23821
23822             for (i = 0; i < 1000; i++) {
23823                 motions = points.map(calcMotion);
23824                 for (j = 0; j < motions.length; j++) {
23825                     points[j] = addPoints(points[j],motions[j]);
23826                 }
23827                 var newScore = squareness(points);
23828                 if (newScore < score) {
23829                     best = _.clone(points);
23830                     score = newScore;
23831                 }
23832                 if (score < epsilon) {
23833                     break;
23834                 }
23835             }
23836
23837             points = best;
23838
23839             for (i = 0; i < points.length; i++) {
23840                 // only move the points that actually moved
23841                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
23842                     graph = graph.replace(graph.entity(nodes[i].id)
23843                         .move(projection.invert(points[i])));
23844                 }
23845             }
23846
23847             // remove empty nodes on straight sections
23848             for (i = 0; i < points.length; i++) {
23849                 var node = nodes[i];
23850
23851                 if (graph.parentWays(node).length > 1 ||
23852                     graph.parentRelations(node).length ||
23853                     node.hasInterestingTags()) {
23854
23855                     continue;
23856                 }
23857
23858                 var dotp = normalizedDotProduct(i, points);
23859                 if (dotp < -1 + epsilon) {
23860                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
23861                 }
23862             }
23863         }
23864
23865         return graph;
23866
23867         function calcMotion(b, i, array) {
23868             var a = array[(i - 1 + array.length) % array.length],
23869                 c = array[(i + 1) % array.length],
23870                 p = subtractPoints(a, b),
23871                 q = subtractPoints(c, b),
23872                 scale, dotp;
23873
23874             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
23875             p = normalizePoint(p, 1.0);
23876             q = normalizePoint(q, 1.0);
23877
23878             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
23879
23880             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
23881             if (array.length > 3) {
23882                 if (dotp < -0.707106781186547) {
23883                     dotp += 1.0;
23884                 }
23885             } else if (dotp && Math.abs(dotp) < corner.dotp) {
23886                 corner.i = i;
23887                 corner.dotp = Math.abs(dotp);
23888             }
23889
23890             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
23891         }
23892     };
23893
23894     function squareness(points) {
23895         return points.reduce(function(sum, val, i, array) {
23896             var dotp = normalizedDotProduct(i, array);
23897
23898             dotp = filterDotProduct(dotp);
23899             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
23900         }, 0);
23901     }
23902
23903     function normalizedDotProduct(i, points) {
23904         var a = points[(i - 1 + points.length) % points.length],
23905             b = points[i],
23906             c = points[(i + 1) % points.length],
23907             p = subtractPoints(a, b),
23908             q = subtractPoints(c, b);
23909
23910         p = normalizePoint(p, 1.0);
23911         q = normalizePoint(q, 1.0);
23912
23913         return p[0] * q[0] + p[1] * q[1];
23914     }
23915
23916     function subtractPoints(a, b) {
23917         return [a[0] - b[0], a[1] - b[1]];
23918     }
23919
23920     function addPoints(a, b) {
23921         return [a[0] + b[0], a[1] + b[1]];
23922     }
23923
23924     function normalizePoint(point, scale) {
23925         var vector = [0, 0];
23926         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
23927         if (length !== 0) {
23928             vector[0] = point[0] / length;
23929             vector[1] = point[1] / length;
23930         }
23931
23932         vector[0] *= scale;
23933         vector[1] *= scale;
23934
23935         return vector;
23936     }
23937
23938     function filterDotProduct(dotp) {
23939         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
23940             return dotp;
23941         }
23942
23943         return 0;
23944     }
23945
23946     action.disabled = function(graph) {
23947         var way = graph.entity(wayId),
23948             nodes = graph.childNodes(way),
23949             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
23950
23951         if (squareness(points)) {
23952             return false;
23953         }
23954
23955         return 'not_squarish';
23956     };
23957
23958     return action;
23959 };
23960 // Create a restriction relation for `turn`, which must have the following structure:
23961 //
23962 //     {
23963 //         from: { node: <node ID>, way: <way ID> },
23964 //         via:  { node: <node ID> },
23965 //         to:   { node: <node ID>, way: <way ID> },
23966 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
23967 //     }
23968 //
23969 // This specifies a restriction of type `restriction` when traveling from
23970 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
23971 // (The action does not check that these entities form a valid intersection.)
23972 //
23973 // If `restriction` is not provided, it is automatically determined by
23974 // iD.geo.inferRestriction.
23975 //
23976 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
23977 // and `to.node` are used to determine which portion of the split ways become
23978 // members of the restriction.
23979 //
23980 // For testing convenience, accepts an ID to assign to the new relation.
23981 // Normally, this will be undefined and the relation will automatically
23982 // be assigned a new ID.
23983 //
23984 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
23985     return function(graph) {
23986         var from = graph.entity(turn.from.way),
23987             via  = graph.entity(turn.via.node),
23988             to   = graph.entity(turn.to.way);
23989
23990         function isClosingNode(way, nodeId) {
23991             return nodeId === way.first() && nodeId === way.last();
23992         }
23993
23994         function split(toOrFrom) {
23995             var newID = toOrFrom.newID || iD.Way().id;
23996             graph = iD.actions.Split(via.id, [newID])
23997                 .limitWays([toOrFrom.way])(graph);
23998
23999             var a = graph.entity(newID),
24000                 b = graph.entity(toOrFrom.way);
24001
24002             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
24003                 return [a, b];
24004             } else {
24005                 return [b, a];
24006             }
24007         }
24008
24009         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
24010             if (turn.from.node === turn.to.node) {
24011                 // U-turn
24012                 from = to = split(turn.from)[0];
24013             } else if (turn.from.way === turn.to.way) {
24014                 // Straight-on or circular
24015                 var s = split(turn.from);
24016                 from = s[0];
24017                 to   = s[1];
24018             } else {
24019                 // Other
24020                 from = split(turn.from)[0];
24021             }
24022         }
24023
24024         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
24025             to = split(turn.to)[0];
24026         }
24027
24028         return graph.replace(iD.Relation({
24029             id: restrictionId,
24030             tags: {
24031                 type: 'restriction',
24032                 restriction: turn.restriction ||
24033                     iD.geo.inferRestriction(
24034                         graph,
24035                         turn.from,
24036                         turn.via,
24037                         turn.to,
24038                         projection)
24039             },
24040             members: [
24041                 {id: from.id, type: 'way',  role: 'from'},
24042                 {id: via.id,  type: 'node', role: 'via'},
24043                 {id: to.id,   type: 'way',  role: 'to'}
24044             ]
24045         }));
24046     };
24047 };
24048 /*
24049   Order the nodes of a way in reverse order and reverse any direction dependent tags
24050   other than `oneway`. (We assume that correcting a backwards oneway is the primary
24051   reason for reversing a way.)
24052
24053   The following transforms are performed:
24054
24055     Keys:
24056           *:right=* ⟺ *:left=*
24057         *:forward=* ⟺ *:backward=*
24058        direction=up ⟺ direction=down
24059          incline=up ⟺ incline=down
24060             *=right ⟺ *=left
24061
24062     Relation members:
24063        role=forward ⟺ role=backward
24064          role=north ⟺ role=south
24065           role=east ⟺ role=west
24066
24067    In addition, numeric-valued `incline` tags are negated.
24068
24069    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
24070    or adjusted tags that don't seem to be used in practice were omitted.
24071
24072    References:
24073       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
24074       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
24075       http://wiki.openstreetmap.org/wiki/Key:incline
24076       http://wiki.openstreetmap.org/wiki/Route#Members
24077       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
24078  */
24079 iD.actions.Reverse = function(wayId, options) {
24080     var replacements = [
24081             [/:right$/, ':left'], [/:left$/, ':right'],
24082             [/:forward$/, ':backward'], [/:backward$/, ':forward']
24083         ],
24084         numeric = /^([+\-]?)(?=[\d.])/,
24085         roleReversals = {
24086             forward: 'backward',
24087             backward: 'forward',
24088             north: 'south',
24089             south: 'north',
24090             east: 'west',
24091             west: 'east'
24092         };
24093
24094     function reverseKey(key) {
24095         for (var i = 0; i < replacements.length; ++i) {
24096             var replacement = replacements[i];
24097             if (replacement[0].test(key)) {
24098                 return key.replace(replacement[0], replacement[1]);
24099             }
24100         }
24101         return key;
24102     }
24103
24104     function reverseValue(key, value) {
24105         if (key === 'incline' && numeric.test(value)) {
24106             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
24107         } else if (key === 'incline' || key === 'direction') {
24108             return {up: 'down', down: 'up'}[value] || value;
24109         } else if (options && options.reverseOneway && key === 'oneway') {
24110             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
24111         } else {
24112             return {left: 'right', right: 'left'}[value] || value;
24113         }
24114     }
24115
24116     return function(graph) {
24117         var way = graph.entity(wayId),
24118             nodes = way.nodes.slice().reverse(),
24119             tags = {}, key, role;
24120
24121         for (key in way.tags) {
24122             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
24123         }
24124
24125         graph.parentRelations(way).forEach(function(relation) {
24126             relation.members.forEach(function(member, index) {
24127                 if (member.id === way.id && (role = roleReversals[member.role])) {
24128                     relation = relation.updateMember({role: role}, index);
24129                     graph = graph.replace(relation);
24130                 }
24131             });
24132         });
24133
24134         return graph.replace(way.update({nodes: nodes, tags: tags}));
24135     };
24136 };
24137 iD.actions.Revert = function(id) {
24138
24139     var action = function(graph) {
24140         var entity = graph.hasEntity(id),
24141             base = graph.base().entities[id];
24142
24143         if (entity && !base) {    // entity will be removed..
24144             if (entity.type === 'node') {
24145                 graph.parentWays(entity)
24146                     .forEach(function(parent) {
24147                         parent = parent.removeNode(id);
24148                         graph = graph.replace(parent);
24149
24150                         if (parent.isDegenerate()) {
24151                             graph = iD.actions.DeleteWay(parent.id)(graph);
24152                         }
24153                     });
24154             }
24155
24156             graph.parentRelations(entity)
24157                 .forEach(function(parent) {
24158                     parent = parent.removeMembersWithID(id);
24159                     graph = graph.replace(parent);
24160
24161                     if (parent.isDegenerate()) {
24162                         graph = iD.actions.DeleteRelation(parent.id)(graph);
24163                     }
24164                 });
24165         }
24166
24167         return graph.revert(id);
24168     };
24169
24170     return action;
24171 };
24172 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
24173     return function(graph) {
24174         return graph.update(function(graph) {
24175             var way = graph.entity(wayId);
24176
24177             _.uniq(way.nodes).forEach(function(id) {
24178
24179                 var node = graph.entity(id),
24180                     point = projection(node.loc),
24181                     radial = [0,0];
24182
24183                 radial[0] = point[0] - pivot[0];
24184                 radial[1] = point[1] - pivot[1];
24185
24186                 point = [
24187                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
24188                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
24189                 ];
24190
24191                 graph = graph.replace(node.move(projection.invert(point)));
24192
24193             });
24194
24195         });
24196     };
24197 };
24198 // Split a way at the given node.
24199 //
24200 // Optionally, split only the given ways, if multiple ways share
24201 // the given node.
24202 //
24203 // This is the inverse of `iD.actions.Join`.
24204 //
24205 // For testing convenience, accepts an ID to assign to the new way.
24206 // Normally, this will be undefined and the way will automatically
24207 // be assigned a new ID.
24208 //
24209 // Reference:
24210 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
24211 //
24212 iD.actions.Split = function(nodeId, newWayIds) {
24213     var wayIds;
24214
24215     // if the way is closed, we need to search for a partner node
24216     // to split the way at.
24217     //
24218     // The following looks for a node that is both far away from
24219     // the initial node in terms of way segment length and nearby
24220     // in terms of beeline-distance. This assures that areas get
24221     // split on the most "natural" points (independent of the number
24222     // of nodes).
24223     // For example: bone-shaped areas get split across their waist
24224     // line, circles across the diameter.
24225     function splitArea(nodes, idxA, graph) {
24226         var lengths = new Array(nodes.length),
24227             length,
24228             i,
24229             best = 0,
24230             idxB;
24231
24232         function wrap(index) {
24233             return iD.util.wrap(index, nodes.length);
24234         }
24235
24236         function dist(nA, nB) {
24237             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
24238         }
24239
24240         // calculate lengths
24241         length = 0;
24242         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
24243             length += dist(nodes[i], nodes[wrap(i-1)]);
24244             lengths[i] = length;
24245         }
24246
24247         length = 0;
24248         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
24249             length += dist(nodes[i], nodes[wrap(i+1)]);
24250             if (length < lengths[i])
24251                 lengths[i] = length;
24252         }
24253
24254         // determine best opposite node to split
24255         for (i = 0; i < nodes.length; i++) {
24256             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
24257             if (cost > best) {
24258                 idxB = i;
24259                 best = cost;
24260             }
24261         }
24262
24263         return idxB;
24264     }
24265
24266     function split(graph, wayA, newWayId) {
24267         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
24268             nodesA,
24269             nodesB,
24270             isArea = wayA.isArea(),
24271             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
24272
24273         if (wayA.isClosed()) {
24274             var nodes = wayA.nodes.slice(0, -1),
24275                 idxA = _.indexOf(nodes, nodeId),
24276                 idxB = splitArea(nodes, idxA, graph);
24277
24278             if (idxB < idxA) {
24279                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
24280                 nodesB = nodes.slice(idxB, idxA + 1);
24281             } else {
24282                 nodesA = nodes.slice(idxA, idxB + 1);
24283                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
24284             }
24285         } else {
24286             var idx = _.indexOf(wayA.nodes, nodeId, 1);
24287             nodesA = wayA.nodes.slice(0, idx + 1);
24288             nodesB = wayA.nodes.slice(idx);
24289         }
24290
24291         wayA = wayA.update({nodes: nodesA});
24292         wayB = wayB.update({nodes: nodesB});
24293
24294         graph = graph.replace(wayA);
24295         graph = graph.replace(wayB);
24296
24297         graph.parentRelations(wayA).forEach(function(relation) {
24298             if (relation.isRestriction()) {
24299                 var via = relation.memberByRole('via');
24300                 if (via && wayB.contains(via.id)) {
24301                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
24302                     graph = graph.replace(relation);
24303                 }
24304             } else {
24305                 if (relation === isOuter) {
24306                     graph = graph.replace(relation.mergeTags(wayA.tags));
24307                     graph = graph.replace(wayA.update({tags: {}}));
24308                     graph = graph.replace(wayB.update({tags: {}}));
24309                 }
24310
24311                 var member = {
24312                     id: wayB.id,
24313                     type: 'way',
24314                     role: relation.memberById(wayA.id).role
24315                 };
24316
24317                 graph = iD.actions.AddMember(relation.id, member)(graph);
24318             }
24319         });
24320
24321         if (!isOuter && isArea) {
24322             var multipolygon = iD.Relation({
24323                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
24324                 members: [
24325                     {id: wayA.id, role: 'outer', type: 'way'},
24326                     {id: wayB.id, role: 'outer', type: 'way'}
24327                 ]});
24328
24329             graph = graph.replace(multipolygon);
24330             graph = graph.replace(wayA.update({tags: {}}));
24331             graph = graph.replace(wayB.update({tags: {}}));
24332         }
24333
24334         return graph;
24335     }
24336
24337     var action = function(graph) {
24338         var candidates = action.ways(graph);
24339         for (var i = 0; i < candidates.length; i++) {
24340             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
24341         }
24342         return graph;
24343     };
24344
24345     action.ways = function(graph) {
24346         var node = graph.entity(nodeId),
24347             parents = graph.parentWays(node),
24348             hasLines = _.some(parents, function(parent) { return parent.geometry(graph) === 'line'; });
24349
24350         return parents.filter(function(parent) {
24351             if (wayIds && wayIds.indexOf(parent.id) === -1)
24352                 return false;
24353
24354             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
24355                 return false;
24356
24357             if (parent.isClosed()) {
24358                 return true;
24359             }
24360
24361             for (var i = 1; i < parent.nodes.length - 1; i++) {
24362                 if (parent.nodes[i] === nodeId) {
24363                     return true;
24364                 }
24365             }
24366
24367             return false;
24368         });
24369     };
24370
24371     action.disabled = function(graph) {
24372         var candidates = action.ways(graph);
24373         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
24374             return 'not_eligible';
24375     };
24376
24377     action.limitWays = function(_) {
24378         if (!arguments.length) return wayIds;
24379         wayIds = _;
24380         return action;
24381     };
24382
24383     return action;
24384 };
24385 /*
24386  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
24387  */
24388
24389 iD.actions.Straighten = function(wayId, projection) {
24390     function positionAlongWay(n, s, e) {
24391         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
24392                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
24393     }
24394
24395     var action = function(graph) {
24396         var way = graph.entity(wayId),
24397             nodes = graph.childNodes(way),
24398             points = nodes.map(function(n) { return projection(n.loc); }),
24399             startPoint = points[0],
24400             endPoint = points[points.length-1],
24401             toDelete = [],
24402             i;
24403
24404         for (i = 1; i < points.length-1; i++) {
24405             var node = nodes[i],
24406                 point = points[i];
24407
24408             if (graph.parentWays(node).length > 1 ||
24409                 graph.parentRelations(node).length ||
24410                 node.hasInterestingTags()) {
24411
24412                 var u = positionAlongWay(point, startPoint, endPoint),
24413                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
24414                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
24415
24416                 graph = graph.replace(graph.entity(node.id)
24417                     .move(projection.invert([p0, p1])));
24418             } else {
24419                 // safe to delete
24420                 if (toDelete.indexOf(node) === -1) {
24421                     toDelete.push(node);
24422                 }
24423             }
24424         }
24425
24426         for (i = 0; i < toDelete.length; i++) {
24427             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
24428         }
24429
24430         return graph;
24431     };
24432
24433     action.disabled = function(graph) {
24434         // check way isn't too bendy
24435         var way = graph.entity(wayId),
24436             nodes = graph.childNodes(way),
24437             points = nodes.map(function(n) { return projection(n.loc); }),
24438             startPoint = points[0],
24439             endPoint = points[points.length-1],
24440             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
24441             i;
24442
24443         if (threshold === 0) {
24444             return 'too_bendy';
24445         }
24446
24447         for (i = 1; i < points.length-1; i++) {
24448             var point = points[i],
24449                 u = positionAlongWay(point, startPoint, endPoint),
24450                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
24451                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
24452                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
24453
24454             // to bendy if point is off by 20% of total start/end distance in projected space
24455             if (isNaN(dist) || dist > threshold) {
24456                 return 'too_bendy';
24457             }
24458         }
24459     };
24460
24461     return action;
24462 };
24463 // Remove the effects of `turn.restriction` on `turn`, which must have the
24464 // following structure:
24465 //
24466 //     {
24467 //         from: { node: <node ID>, way: <way ID> },
24468 //         via:  { node: <node ID> },
24469 //         to:   { node: <node ID>, way: <way ID> },
24470 //         restriction: <relation ID>
24471 //     }
24472 //
24473 // In the simple case, `restriction` is a reference to a `no_*` restriction
24474 // on the turn itself. In this case, it is simply deleted.
24475 //
24476 // The more complex case is where `restriction` references an `only_*`
24477 // restriction on a different turn in the same intersection. In that case,
24478 // that restriction is also deleted, but at the same time restrictions on
24479 // the turns other than the first two are created.
24480 //
24481 iD.actions.UnrestrictTurn = function(turn) {
24482     return function(graph) {
24483         return iD.actions.DeleteRelation(turn.restriction)(graph);
24484     };
24485 };
24486 iD.behavior = {};
24487 iD.behavior.AddWay = function(context) {
24488     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
24489         draw = iD.behavior.Draw(context);
24490
24491     var addWay = function(surface) {
24492         draw.on('click', event.start)
24493             .on('clickWay', event.startFromWay)
24494             .on('clickNode', event.startFromNode)
24495             .on('cancel', addWay.cancel)
24496             .on('finish', addWay.cancel);
24497
24498         context.map()
24499             .dblclickEnable(false);
24500
24501         surface.call(draw);
24502     };
24503
24504     addWay.off = function(surface) {
24505         surface.call(draw.off);
24506     };
24507
24508     addWay.cancel = function() {
24509         window.setTimeout(function() {
24510             context.map().dblclickEnable(true);
24511         }, 1000);
24512
24513         context.enter(iD.modes.Browse(context));
24514     };
24515
24516     addWay.tail = function(text) {
24517         draw.tail(text);
24518         return addWay;
24519     };
24520
24521     return d3.rebind(addWay, event, 'on');
24522 };
24523 iD.behavior.Breathe = function() {
24524     var duration = 800,
24525         selector = '.selected.shadow, .selected .shadow',
24526         selected = d3.select(null),
24527         classed = '',
24528         params = {},
24529         done;
24530
24531     function reset(selection) {
24532         selection
24533             .style('stroke-opacity', null)
24534             .style('stroke-width', null)
24535             .style('fill-opacity', null)
24536             .style('r', null);
24537     }
24538
24539     function setAnimationParams(transition, fromTo) {
24540         transition
24541             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
24542             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
24543             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
24544             .style('r', function(d) { return params[d.id][fromTo].width; });
24545     }
24546
24547     function calcAnimationParams(selection) {
24548         selection
24549             .call(reset)
24550             .each(function(d) {
24551                 var s = d3.select(this),
24552                     tag = s.node().tagName,
24553                     p = {'from': {}, 'to': {}},
24554                     opacity, width;
24555
24556                 // determine base opacity and width
24557                 if (tag === 'circle') {
24558                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
24559                     width = parseFloat(s.style('r') || 15.5);
24560                 } else {
24561                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
24562                     width = parseFloat(s.style('stroke-width') || 10);
24563                 }
24564
24565                 // calculate from/to interpolation params..
24566                 p.tag = tag;
24567                 p.from.opacity = opacity * 0.6;
24568                 p.to.opacity = opacity * 1.25;
24569                 p.from.width = width * 0.9;
24570                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
24571                 params[d.id] = p;
24572             });
24573     }
24574
24575     function run(surface, fromTo) {
24576         var toFrom = (fromTo === 'from' ? 'to': 'from'),
24577             currSelected = surface.selectAll(selector),
24578             currClassed = surface.attr('class'),
24579             n = 0;
24580
24581         if (done || currSelected.empty()) {
24582             selected.call(reset);
24583             return;
24584         }
24585
24586         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
24587             selected.call(reset);
24588             classed = currClassed;
24589             selected = currSelected.call(calcAnimationParams);
24590         }
24591
24592         selected
24593             .transition()
24594             .call(setAnimationParams, fromTo)
24595             .duration(duration)
24596             .each(function() { ++n; })
24597             .each('end', function() {
24598                 if (!--n) {  // call once
24599                     surface.call(run, toFrom);
24600                 }
24601             });
24602     }
24603
24604     var breathe = function(surface) {
24605         done = false;
24606         d3.timer(function() {
24607             if (done) return true;
24608
24609             var currSelected = surface.selectAll(selector);
24610             if (currSelected.empty()) return false;
24611
24612             surface.call(run, 'from');
24613             return true;
24614         }, 200);
24615     };
24616
24617     breathe.off = function() {
24618         done = true;
24619         d3.timer.flush();
24620         selected
24621             .transition()
24622             .call(reset)
24623             .duration(0);
24624     };
24625
24626     return breathe;
24627 };
24628 iD.behavior.Copy = function(context) {
24629     var keybinding = d3.keybinding('copy');
24630
24631     function groupEntities(ids, graph) {
24632         var entities = ids.map(function (id) { return graph.entity(id); });
24633         return _.extend({relation: [], way: [], node: []},
24634             _.groupBy(entities, function(entity) { return entity.type; }));
24635     }
24636
24637     function getDescendants(id, graph, descendants) {
24638         var entity = graph.entity(id),
24639             i, children;
24640
24641         descendants = descendants || {};
24642
24643         if (entity.type === 'relation') {
24644             children = _.map(entity.members, 'id');
24645         } else if (entity.type === 'way') {
24646             children = entity.nodes;
24647         } else {
24648             children = [];
24649         }
24650
24651         for (i = 0; i < children.length; i++) {
24652             if (!descendants[children[i]]) {
24653                 descendants[children[i]] = true;
24654                 descendants = getDescendants(children[i], graph, descendants);
24655             }
24656         }
24657
24658         return descendants;
24659     }
24660
24661     function doCopy() {
24662         d3.event.preventDefault();
24663         if (context.inIntro()) return;
24664
24665         var graph = context.graph(),
24666             selected = groupEntities(context.selectedIDs(), graph),
24667             canCopy = [],
24668             skip = {},
24669             i, entity;
24670
24671         for (i = 0; i < selected.relation.length; i++) {
24672             entity = selected.relation[i];
24673             if (!skip[entity.id] && entity.isComplete(graph)) {
24674                 canCopy.push(entity.id);
24675                 skip = getDescendants(entity.id, graph, skip);
24676             }
24677         }
24678         for (i = 0; i < selected.way.length; i++) {
24679             entity = selected.way[i];
24680             if (!skip[entity.id]) {
24681                 canCopy.push(entity.id);
24682                 skip = getDescendants(entity.id, graph, skip);
24683             }
24684         }
24685         for (i = 0; i < selected.node.length; i++) {
24686             entity = selected.node[i];
24687             if (!skip[entity.id]) {
24688                 canCopy.push(entity.id);
24689             }
24690         }
24691
24692         context.copyIDs(canCopy);
24693     }
24694
24695     function copy() {
24696         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
24697         d3.select(document).call(keybinding);
24698         return copy;
24699     }
24700
24701     copy.off = function() {
24702         d3.select(document).call(keybinding.off);
24703     };
24704
24705     return copy;
24706 };
24707 /*
24708     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
24709
24710     * The `origin` function is expected to return an [x, y] tuple rather than an
24711       {x, y} object.
24712     * The events are `start`, `move`, and `end`.
24713       (https://github.com/mbostock/d3/issues/563)
24714     * The `start` event is not dispatched until the first cursor movement occurs.
24715       (https://github.com/mbostock/d3/pull/368)
24716     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
24717       than `x`, `y`, `dx`, and `dy` properties.
24718     * The `end` event is not dispatched if no movement occurs.
24719     * An `off` function is available that unbinds the drag's internal event handlers.
24720     * Delegation is supported via the `delegate` function.
24721
24722  */
24723 iD.behavior.drag = function() {
24724     function d3_eventCancel() {
24725       d3.event.stopPropagation();
24726       d3.event.preventDefault();
24727     }
24728
24729     var event = d3.dispatch('start', 'move', 'end'),
24730         origin = null,
24731         selector = '',
24732         filter = null,
24733         event_, target, surface;
24734
24735     event.of = function(thiz, argumentz) {
24736       return function(e1) {
24737         var e0 = e1.sourceEvent = d3.event;
24738         e1.target = drag;
24739         d3.event = e1;
24740         try {
24741           event[e1.type].apply(thiz, argumentz);
24742         } finally {
24743           d3.event = e0;
24744         }
24745       };
24746     };
24747
24748     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
24749         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
24750             function () {
24751                 var selection = d3.selection(),
24752                     select = selection.style(d3_event_userSelectProperty);
24753                 selection.style(d3_event_userSelectProperty, 'none');
24754                 return function () {
24755                     selection.style(d3_event_userSelectProperty, select);
24756                 };
24757             } :
24758             function (type) {
24759                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
24760                 return function () {
24761                     w.on('selectstart.' + type, null);
24762                 };
24763             };
24764
24765     function mousedown() {
24766         target = this;
24767         event_ = event.of(target, arguments);
24768         var eventTarget = d3.event.target,
24769             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
24770             offset,
24771             origin_ = point(),
24772             started = false,
24773             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
24774
24775         var w = d3.select(window)
24776             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
24777             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
24778
24779         if (origin) {
24780             offset = origin.apply(target, arguments);
24781             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
24782         } else {
24783             offset = [0, 0];
24784         }
24785
24786         if (touchId === null) d3.event.stopPropagation();
24787
24788         function point() {
24789             var p = target.parentNode || surface;
24790             return touchId !== null ? d3.touches(p).filter(function(p) {
24791                 return p.identifier === touchId;
24792             })[0] : d3.mouse(p);
24793         }
24794
24795         function dragmove() {
24796
24797             var p = point(),
24798                 dx = p[0] - origin_[0],
24799                 dy = p[1] - origin_[1];
24800             
24801             if (dx === 0 && dy === 0)
24802                 return;
24803
24804             if (!started) {
24805                 started = true;
24806                 event_({
24807                     type: 'start'
24808                 });
24809             }
24810
24811             origin_ = p;
24812             d3_eventCancel();
24813
24814             event_({
24815                 type: 'move',
24816                 point: [p[0] + offset[0],  p[1] + offset[1]],
24817                 delta: [dx, dy]
24818             });
24819         }
24820
24821         function dragend() {
24822             if (started) {
24823                 event_({
24824                     type: 'end'
24825                 });
24826
24827                 d3_eventCancel();
24828                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
24829             }
24830
24831             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
24832                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
24833             selectEnable();
24834         }
24835
24836         function click() {
24837             d3_eventCancel();
24838             w.on('click.drag', null);
24839         }
24840     }
24841
24842     function drag(selection) {
24843         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
24844             delegate = mousedown;
24845
24846         if (selector) {
24847             delegate = function() {
24848                 var root = this,
24849                     target = d3.event.target;
24850                 for (; target && target !== root; target = target.parentNode) {
24851                     if (target[matchesSelector](selector) &&
24852                             (!filter || filter(target.__data__))) {
24853                         return mousedown.call(target, target.__data__);
24854                     }
24855                 }
24856             };
24857         }
24858
24859         selection.on('mousedown.drag' + selector, delegate)
24860             .on('touchstart.drag' + selector, delegate);
24861     }
24862
24863     drag.off = function(selection) {
24864         selection.on('mousedown.drag' + selector, null)
24865             .on('touchstart.drag' + selector, null);
24866     };
24867
24868     drag.delegate = function(_) {
24869         if (!arguments.length) return selector;
24870         selector = _;
24871         return drag;
24872     };
24873
24874     drag.filter = function(_) {
24875         if (!arguments.length) return origin;
24876         filter = _;
24877         return drag;
24878     };
24879
24880     drag.origin = function (_) {
24881         if (!arguments.length) return origin;
24882         origin = _;
24883         return drag;
24884     };
24885
24886     drag.cancel = function() {
24887         d3.select(window)
24888             .on('mousemove.drag', null)
24889             .on('mouseup.drag', null);
24890         return drag;
24891     };
24892
24893     drag.target = function() {
24894         if (!arguments.length) return target;
24895         target = arguments[0];
24896         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
24897         return drag;
24898     };
24899
24900     drag.surface = function() {
24901         if (!arguments.length) return surface;
24902         surface = arguments[0];
24903         return drag;
24904     };
24905
24906     return d3.rebind(drag, event, 'on');
24907 };
24908 iD.behavior.Draw = function(context) {
24909     var event = d3.dispatch('move', 'click', 'clickWay',
24910             'clickNode', 'undo', 'cancel', 'finish'),
24911         keybinding = d3.keybinding('draw'),
24912         hover = iD.behavior.Hover(context)
24913             .altDisables(true)
24914             .on('hover', context.ui().sidebar.hover),
24915         tail = iD.behavior.Tail(),
24916         edit = iD.behavior.Edit(context),
24917         closeTolerance = 4,
24918         tolerance = 12,
24919         mouseLeave = false,
24920         lastMouse = null,
24921         cached = iD.behavior.Draw;
24922
24923     function datum() {
24924         if (d3.event.altKey) return {};
24925
24926         if (d3.event.type === 'keydown') {
24927             return (lastMouse && lastMouse.target.__data__) || {};
24928         } else {
24929             return d3.event.target.__data__ || {};
24930         }
24931     }
24932
24933     function mousedown() {
24934
24935         function point() {
24936             var p = context.container().node();
24937             return touchId !== null ? d3.touches(p).filter(function(p) {
24938                 return p.identifier === touchId;
24939             })[0] : d3.mouse(p);
24940         }
24941
24942         var element = d3.select(this),
24943             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
24944             t1 = +new Date(),
24945             p1 = point();
24946
24947         element.on('mousemove.draw', null);
24948
24949         d3.select(window).on('mouseup.draw', function() {
24950             var t2 = +new Date(),
24951                 p2 = point(),
24952                 dist = iD.geo.euclideanDistance(p1, p2);
24953
24954             element.on('mousemove.draw', mousemove);
24955             d3.select(window).on('mouseup.draw', null);
24956
24957             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
24958                 // Prevent a quick second click
24959                 d3.select(window).on('click.draw-block', function() {
24960                     d3.event.stopPropagation();
24961                 }, true);
24962
24963                 context.map().dblclickEnable(false);
24964
24965                 window.setTimeout(function() {
24966                     context.map().dblclickEnable(true);
24967                     d3.select(window).on('click.draw-block', null);
24968                 }, 500);
24969
24970                 click();
24971             }
24972         });
24973     }
24974
24975     function mousemove() {
24976         lastMouse = d3.event;
24977         event.move(datum());
24978     }
24979
24980     function mouseenter() {
24981         mouseLeave = false;
24982     }
24983
24984     function mouseleave() {
24985         mouseLeave = true;
24986     }
24987
24988     function click() {
24989         var d = datum();
24990         if (d.type === 'way') {
24991             var dims = context.map().dimensions(),
24992                 mouse = context.mouse(),
24993                 pad = 5,
24994                 trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
24995                     mouse[1] > pad && mouse[1] < dims[1] - pad;
24996
24997             if (trySnap) {
24998                 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
24999                     edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
25000                 event.clickWay(choice.loc, edge);
25001             } else {
25002                 event.click(context.map().mouseCoordinates());
25003             }
25004
25005         } else if (d.type === 'node') {
25006             event.clickNode(d);
25007
25008         } else {
25009             event.click(context.map().mouseCoordinates());
25010         }
25011     }
25012
25013     function space() {
25014         var currSpace = context.mouse();
25015         if (cached.disableSpace && cached.lastSpace) {
25016             var dist = iD.geo.euclideanDistance(cached.lastSpace, currSpace);
25017             if (dist > tolerance) {
25018                 cached.disableSpace = false;
25019             }
25020         }
25021
25022         if (cached.disableSpace || mouseLeave || !lastMouse) return;
25023
25024         // user must move mouse or release space bar to allow another click
25025         cached.lastSpace = currSpace;
25026         cached.disableSpace = true;
25027
25028         d3.select(window).on('keyup.space-block', function() {
25029             cached.disableSpace = false;
25030             d3.select(window).on('keyup.space-block', null);
25031         });
25032
25033         d3.event.preventDefault();
25034         click();
25035     }
25036
25037     function backspace() {
25038         d3.event.preventDefault();
25039         event.undo();
25040     }
25041
25042     function del() {
25043         d3.event.preventDefault();
25044         event.cancel();
25045     }
25046
25047     function ret() {
25048         d3.event.preventDefault();
25049         event.finish();
25050     }
25051
25052     function draw(selection) {
25053         context.install(hover);
25054         context.install(edit);
25055
25056         if (!context.inIntro() && !cached.usedTails[tail.text()]) {
25057             context.install(tail);
25058         }
25059
25060         keybinding
25061             .on('⌫', backspace)
25062             .on('⌦', del)
25063             .on('⎋', ret)
25064             .on('↩', ret)
25065             .on('space', space)
25066             .on('⌥space', space);
25067
25068         selection
25069             .on('mouseenter.draw', mouseenter)
25070             .on('mouseleave.draw', mouseleave)
25071             .on('mousedown.draw', mousedown)
25072             .on('mousemove.draw', mousemove);
25073
25074         d3.select(document)
25075             .call(keybinding);
25076
25077         return draw;
25078     }
25079
25080     draw.off = function(selection) {
25081         context.ui().sidebar.hover.cancel();
25082         context.uninstall(hover);
25083         context.uninstall(edit);
25084
25085         if (!context.inIntro() && !cached.usedTails[tail.text()]) {
25086             context.uninstall(tail);
25087             cached.usedTails[tail.text()] = true;
25088         }
25089
25090         selection
25091             .on('mouseenter.draw', null)
25092             .on('mouseleave.draw', null)
25093             .on('mousedown.draw', null)
25094             .on('mousemove.draw', null);
25095
25096         d3.select(window)
25097             .on('mouseup.draw', null);
25098             // note: keyup.space-block, click.draw-block should remain
25099
25100         d3.select(document)
25101             .call(keybinding.off);
25102     };
25103
25104     draw.tail = function(_) {
25105         tail.text(_);
25106         return draw;
25107     };
25108
25109     return d3.rebind(draw, event, 'on');
25110 };
25111
25112 iD.behavior.Draw.usedTails = {};
25113 iD.behavior.Draw.disableSpace = false;
25114 iD.behavior.Draw.lastSpace = null;
25115 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
25116     var way = context.entity(wayId),
25117         isArea = context.geometry(wayId) === 'area',
25118         finished = false,
25119         annotation = t((way.isDegenerate() ?
25120             'operations.start.annotation.' :
25121             'operations.continue.annotation.') + context.geometry(wayId)),
25122         draw = iD.behavior.Draw(context);
25123
25124     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
25125         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
25126         end = iD.Node({loc: context.map().mouseCoordinates()}),
25127         segment = iD.Way({
25128             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
25129             tags: _.clone(way.tags)
25130         });
25131
25132     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
25133     if (isArea) {
25134         f(iD.actions.AddEntity(end),
25135             iD.actions.AddVertex(wayId, end.id, index));
25136     } else {
25137         f(iD.actions.AddEntity(start),
25138             iD.actions.AddEntity(end),
25139             iD.actions.AddEntity(segment));
25140     }
25141
25142     function move(datum) {
25143         var loc;
25144
25145         if (datum.type === 'node' && datum.id !== end.id) {
25146             loc = datum.loc;
25147
25148         } else if (datum.type === 'way' && datum.id !== segment.id) {
25149             var dims = context.map().dimensions(),
25150                 mouse = context.mouse(),
25151                 pad = 5,
25152                 trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
25153                     mouse[1] > pad && mouse[1] < dims[1] - pad;
25154
25155             if (trySnap) {
25156                 loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
25157             }
25158         }
25159
25160         if (!loc) {
25161             loc = context.map().mouseCoordinates();
25162         }
25163
25164         context.replace(iD.actions.MoveNode(end.id, loc));
25165     }
25166
25167     function undone() {
25168         finished = true;
25169         context.enter(iD.modes.Browse(context));
25170     }
25171
25172     function setActiveElements() {
25173         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
25174         context.surface().selectAll(iD.util.entitySelector(active))
25175             .classed('active', true);
25176     }
25177
25178     var drawWay = function(surface) {
25179         draw.on('move', move)
25180             .on('click', drawWay.add)
25181             .on('clickWay', drawWay.addWay)
25182             .on('clickNode', drawWay.addNode)
25183             .on('undo', context.undo)
25184             .on('cancel', drawWay.cancel)
25185             .on('finish', drawWay.finish);
25186
25187         context.map()
25188             .dblclickEnable(false)
25189             .on('drawn.draw', setActiveElements);
25190
25191         setActiveElements();
25192
25193         surface.call(draw);
25194
25195         context.history()
25196             .on('undone.draw', undone);
25197     };
25198
25199     drawWay.off = function(surface) {
25200         if (!finished)
25201             context.pop();
25202
25203         context.map()
25204             .on('drawn.draw', null);
25205
25206         surface.call(draw.off)
25207             .selectAll('.active')
25208             .classed('active', false);
25209
25210         context.history()
25211             .on('undone.draw', null);
25212     };
25213
25214     function ReplaceTemporaryNode(newNode) {
25215         return function(graph) {
25216             if (isArea) {
25217                 return graph
25218                     .replace(way.addNode(newNode.id, index))
25219                     .remove(end);
25220
25221             } else {
25222                 return graph
25223                     .replace(graph.entity(wayId).addNode(newNode.id, index))
25224                     .remove(end)
25225                     .remove(segment)
25226                     .remove(start);
25227             }
25228         };
25229     }
25230
25231     // Accept the current position of the temporary node and continue drawing.
25232     drawWay.add = function(loc) {
25233
25234         // prevent duplicate nodes
25235         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
25236         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
25237
25238         var newNode = iD.Node({loc: loc});
25239
25240         context.replace(
25241             iD.actions.AddEntity(newNode),
25242             ReplaceTemporaryNode(newNode),
25243             annotation);
25244
25245         finished = true;
25246         context.enter(mode);
25247     };
25248
25249     // Connect the way to an existing way.
25250     drawWay.addWay = function(loc, edge) {
25251         var previousEdge = startIndex ?
25252             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
25253             [way.nodes[0], way.nodes[1]];
25254
25255         // Avoid creating duplicate segments
25256         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
25257             return;
25258
25259         var newNode = iD.Node({ loc: loc });
25260
25261         context.perform(
25262             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
25263             ReplaceTemporaryNode(newNode),
25264             annotation);
25265
25266         finished = true;
25267         context.enter(mode);
25268     };
25269
25270     // Connect the way to an existing node and continue drawing.
25271     drawWay.addNode = function(node) {
25272
25273         // Avoid creating duplicate segments
25274         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
25275
25276         context.perform(
25277             ReplaceTemporaryNode(node),
25278             annotation);
25279
25280         finished = true;
25281         context.enter(mode);
25282     };
25283
25284     // Finish the draw operation, removing the temporary node. If the way has enough
25285     // nodes to be valid, it's selected. Otherwise, return to browse mode.
25286     drawWay.finish = function() {
25287         context.pop();
25288         finished = true;
25289
25290         window.setTimeout(function() {
25291             context.map().dblclickEnable(true);
25292         }, 1000);
25293
25294         if (context.hasEntity(wayId)) {
25295             context.enter(
25296                 iD.modes.Select(context, [wayId])
25297                     .suppressMenu(true)
25298                     .newFeature(true));
25299         } else {
25300             context.enter(iD.modes.Browse(context));
25301         }
25302     };
25303
25304     // Cancel the draw operation and return to browse, deleting everything drawn.
25305     drawWay.cancel = function() {
25306         context.perform(
25307             d3.functor(baseGraph),
25308             t('operations.cancel_draw.annotation'));
25309
25310         window.setTimeout(function() {
25311             context.map().dblclickEnable(true);
25312         }, 1000);
25313
25314         finished = true;
25315         context.enter(iD.modes.Browse(context));
25316     };
25317
25318     drawWay.tail = function(text) {
25319         draw.tail(text);
25320         return drawWay;
25321     };
25322
25323     return drawWay;
25324 };
25325 iD.behavior.Edit = function(context) {
25326     function edit() {
25327         context.map()
25328             .minzoom(context.minEditableZoom());
25329     }
25330
25331     edit.off = function() {
25332         context.map()
25333             .minzoom(0);
25334     };
25335
25336     return edit;
25337 };
25338 iD.behavior.Hash = function(context) {
25339     var s0 = null, // cached location.hash
25340         lat = 90 - 1e-8; // allowable latitude range
25341
25342     var parser = function(map, s) {
25343         var q = iD.util.stringQs(s);
25344         var args = (q.map || '').split('/').map(Number);
25345         if (args.length < 3 || args.some(isNaN)) {
25346             return true; // replace bogus hash
25347         } else if (s !== formatter(map).slice(1)) {
25348             map.centerZoom([args[1],
25349                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
25350         }
25351     };
25352
25353     var formatter = function(map) {
25354         var mode = context.mode(),
25355             center = map.center(),
25356             zoom = map.zoom(),
25357             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
25358             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
25359             newParams = {};
25360
25361         if (mode && mode.id === 'browse') {
25362             delete q.id;
25363         } else {
25364             var selected = context.selectedIDs().filter(function(id) {
25365                 return !context.entity(id).isNew();
25366             });
25367             if (selected.length) {
25368                 newParams.id = selected.join(',');
25369             }
25370         }
25371
25372         newParams.map = zoom.toFixed(2) +
25373                 '/' + center[0].toFixed(precision) +
25374                 '/' + center[1].toFixed(precision);
25375
25376         return '#' + iD.util.qsString(_.assign(q, newParams), true);
25377     };
25378
25379     function update() {
25380         if (context.inIntro()) return;
25381         var s1 = formatter(context.map());
25382         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
25383     }
25384
25385     var throttledUpdate = _.throttle(update, 500);
25386
25387     function hashchange() {
25388         if (location.hash === s0) return; // ignore spurious hashchange events
25389         if (parser(context.map(), (s0 = location.hash).substring(1))) {
25390             update(); // replace bogus hash
25391         }
25392     }
25393
25394     function hash() {
25395         context.map()
25396             .on('move.hash', throttledUpdate);
25397
25398         context
25399             .on('enter.hash', throttledUpdate);
25400
25401         d3.select(window)
25402             .on('hashchange.hash', hashchange);
25403
25404         if (location.hash) {
25405             var q = iD.util.stringQs(location.hash.substring(1));
25406             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
25407             if (q.comment) context.storage('comment', q.comment);
25408             hashchange();
25409             if (q.map) hash.hadHash = true;
25410         }
25411     }
25412
25413     hash.off = function() {
25414         throttledUpdate.cancel();
25415
25416         context.map()
25417             .on('move.hash', null);
25418
25419         context
25420             .on('enter.hash', null);
25421
25422         d3.select(window)
25423             .on('hashchange.hash', null);
25424
25425         location.hash = '';
25426     };
25427
25428     return hash;
25429 };
25430 /*
25431    The hover behavior adds the `.hover` class on mouseover to all elements to which
25432    the identical datum is bound, and removes it on mouseout.
25433
25434    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
25435    representation may consist of several elements scattered throughout the DOM hierarchy.
25436    Only one of these elements can have the :hover pseudo-class, but all of them will
25437    have the .hover class.
25438  */
25439 iD.behavior.Hover = function() {
25440     var dispatch = d3.dispatch('hover'),
25441         selection,
25442         altDisables,
25443         target;
25444
25445     function keydown() {
25446         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
25447             dispatch.hover(null);
25448             selection.selectAll('.hover')
25449                 .classed('hover-suppressed', true)
25450                 .classed('hover', false);
25451         }
25452     }
25453
25454     function keyup() {
25455         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
25456             dispatch.hover(target ? target.id : null);
25457             selection.selectAll('.hover-suppressed')
25458                 .classed('hover-suppressed', false)
25459                 .classed('hover', true);
25460         }
25461     }
25462
25463     var hover = function(__) {
25464         selection = __;
25465
25466         function enter(d) {
25467             if (d === target) return;
25468
25469             target = d;
25470
25471             selection.selectAll('.hover')
25472                 .classed('hover', false);
25473             selection.selectAll('.hover-suppressed')
25474                 .classed('hover-suppressed', false);
25475
25476             if (target instanceof iD.Entity) {
25477                 var selector = '.' + target.id;
25478
25479                 if (target.type === 'relation') {
25480                     target.members.forEach(function(member) {
25481                         selector += ', .' + member.id;
25482                     });
25483                 }
25484
25485                 var suppressed = altDisables && d3.event && d3.event.altKey;
25486
25487                 selection.selectAll(selector)
25488                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
25489
25490                 dispatch.hover(target.id);
25491             } else {
25492                 dispatch.hover(null);
25493             }
25494         }
25495
25496         var down;
25497
25498         function mouseover() {
25499             if (down) return;
25500             var target = d3.event.target;
25501             enter(target ? target.__data__ : null);
25502         }
25503
25504         function mouseout() {
25505             if (down) return;
25506             var target = d3.event.relatedTarget;
25507             enter(target ? target.__data__ : null);
25508         }
25509
25510         function mousedown() {
25511             down = true;
25512             d3.select(window)
25513                 .on('mouseup.hover', mouseup);
25514         }
25515
25516         function mouseup() {
25517             down = false;
25518         }
25519
25520         selection
25521             .on('mouseover.hover', mouseover)
25522             .on('mouseout.hover', mouseout)
25523             .on('mousedown.hover', mousedown)
25524             .on('mouseup.hover', mouseup);
25525
25526         d3.select(window)
25527             .on('keydown.hover', keydown)
25528             .on('keyup.hover', keyup);
25529     };
25530
25531     hover.off = function(selection) {
25532         selection.selectAll('.hover')
25533             .classed('hover', false);
25534         selection.selectAll('.hover-suppressed')
25535             .classed('hover-suppressed', false);
25536
25537         selection
25538             .on('mouseover.hover', null)
25539             .on('mouseout.hover', null)
25540             .on('mousedown.hover', null)
25541             .on('mouseup.hover', null);
25542
25543         d3.select(window)
25544             .on('keydown.hover', null)
25545             .on('keyup.hover', null)
25546             .on('mouseup.hover', null);
25547     };
25548
25549     hover.altDisables = function(_) {
25550         if (!arguments.length) return altDisables;
25551         altDisables = _;
25552         return hover;
25553     };
25554
25555     return d3.rebind(hover, dispatch, 'on');
25556 };
25557 iD.behavior.Lasso = function(context) {
25558
25559     var behavior = function(selection) {
25560         var lasso;
25561
25562         function mousedown() {
25563             var button = 0;  // left
25564             if (d3.event.button === button && d3.event.shiftKey === true) {
25565                 lasso = null;
25566
25567                 selection
25568                     .on('mousemove.lasso', mousemove)
25569                     .on('mouseup.lasso', mouseup);
25570
25571                 d3.event.stopPropagation();
25572             }
25573         }
25574
25575         function mousemove() {
25576             if (!lasso) {
25577                 lasso = iD.ui.Lasso(context);
25578                 context.surface().call(lasso);
25579             }
25580
25581             lasso.p(context.mouse());
25582         }
25583
25584         function normalize(a, b) {
25585             return [
25586                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
25587                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
25588         }
25589
25590         function lassoed() {
25591             if (!lasso) return [];
25592
25593             var graph = context.graph(),
25594                 bounds = lasso.extent().map(context.projection.invert),
25595                 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
25596
25597             return _.map(context.intersects(extent).filter(function(entity) {
25598                 return entity.type === 'node' &&
25599                     iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
25600                     !context.features().isHidden(entity, graph, entity.geometry(graph));
25601             }), 'id');
25602         }
25603
25604         function mouseup() {
25605             selection
25606                 .on('mousemove.lasso', null)
25607                 .on('mouseup.lasso', null);
25608
25609             if (!lasso) return;
25610
25611             var ids = lassoed();
25612             lasso.close();
25613
25614             if (ids.length) {
25615                 context.enter(iD.modes.Select(context, ids));
25616             }
25617         }
25618
25619         selection
25620             .on('mousedown.lasso', mousedown);
25621     };
25622
25623     behavior.off = function(selection) {
25624         selection.on('mousedown.lasso', null);
25625     };
25626
25627     return behavior;
25628 };
25629 iD.behavior.Paste = function(context) {
25630     var keybinding = d3.keybinding('paste');
25631
25632     function omitTag(v, k) {
25633         return (
25634             k === 'phone' ||
25635             k === 'fax' ||
25636             k === 'email' ||
25637             k === 'website' ||
25638             k === 'url' ||
25639             k === 'note' ||
25640             k === 'description' ||
25641             k.indexOf('name') !== -1 ||
25642             k.indexOf('wiki') === 0 ||
25643             k.indexOf('addr:') === 0 ||
25644             k.indexOf('contact:') === 0
25645         );
25646     }
25647
25648     function doPaste() {
25649         d3.event.preventDefault();
25650         if (context.inIntro()) return;
25651
25652         var baseGraph = context.graph(),
25653             mouse = context.mouse(),
25654             projection = context.projection,
25655             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
25656
25657         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
25658
25659         var extent = iD.geo.Extent(),
25660             oldIDs = context.copyIDs(),
25661             oldGraph = context.copyGraph(),
25662             newIDs = [];
25663
25664         if (!oldIDs.length) return;
25665
25666         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
25667         context.perform(action);
25668
25669         var copies = action.copies();
25670         for (var id in copies) {
25671             var oldEntity = oldGraph.entity(id),
25672                 newEntity = copies[id];
25673
25674             extent._extend(oldEntity.extent(oldGraph));
25675             newIDs.push(newEntity.id);
25676             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
25677         }
25678
25679         // Put pasted objects where mouse pointer is..
25680         var center = projection(extent.center()),
25681             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
25682
25683         context.perform(iD.actions.Move(newIDs, delta, projection));
25684         context.enter(iD.modes.Move(context, newIDs, baseGraph));
25685     }
25686
25687     function paste() {
25688         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
25689         d3.select(document).call(keybinding);
25690         return paste;
25691     }
25692
25693     paste.off = function() {
25694         d3.select(document).call(keybinding.off);
25695     };
25696
25697     return paste;
25698 };
25699 iD.behavior.Select = function(context) {
25700     function keydown() {
25701         if (d3.event && d3.event.shiftKey) {
25702             context.surface()
25703                 .classed('behavior-multiselect', true);
25704         }
25705     }
25706
25707     function keyup() {
25708         if (!d3.event || !d3.event.shiftKey) {
25709             context.surface()
25710                 .classed('behavior-multiselect', false);
25711         }
25712     }
25713
25714     function click() {
25715         var datum = d3.event.target.__data__,
25716             lasso = d3.select('#surface .lasso').node(),
25717             mode = context.mode();
25718
25719         if (!(datum instanceof iD.Entity)) {
25720             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
25721                 context.enter(iD.modes.Browse(context));
25722
25723         } else if (!d3.event.shiftKey && !lasso) {
25724             // Avoid re-entering Select mode with same entity.
25725             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
25726                 context.enter(iD.modes.Select(context, [datum.id]));
25727             } else {
25728                 mode.suppressMenu(false).reselect();
25729             }
25730         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
25731             var selectedIDs = _.without(context.selectedIDs(), datum.id);
25732             context.enter(selectedIDs.length ?
25733                 iD.modes.Select(context, selectedIDs) :
25734                 iD.modes.Browse(context));
25735
25736         } else {
25737             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
25738         }
25739     }
25740
25741     var behavior = function(selection) {
25742         d3.select(window)
25743             .on('keydown.select', keydown)
25744             .on('keyup.select', keyup);
25745
25746         selection.on('click.select', click);
25747
25748         keydown();
25749     };
25750
25751     behavior.off = function(selection) {
25752         d3.select(window)
25753             .on('keydown.select', null)
25754             .on('keyup.select', null);
25755
25756         selection.on('click.select', null);
25757
25758         keyup();
25759     };
25760
25761     return behavior;
25762 };
25763 iD.behavior.Tail = function() {
25764     var text,
25765         container,
25766         xmargin = 25,
25767         tooltipSize = [0, 0],
25768         selectionSize = [0, 0];
25769
25770     function tail(selection) {
25771         if (!text) return;
25772
25773         d3.select(window)
25774             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
25775
25776         function show() {
25777             container.style('display', 'block');
25778             tooltipSize = container.dimensions();
25779         }
25780
25781         function mousemove() {
25782             if (container.style('display') === 'none') show();
25783             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
25784                 -tooltipSize[0] - xmargin : xmargin;
25785             container.classed('left', xoffset > 0);
25786             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
25787         }
25788
25789         function mouseleave() {
25790             if (d3.event.relatedTarget !== container.node()) {
25791                 container.style('display', 'none');
25792             }
25793         }
25794
25795         function mouseenter() {
25796             if (d3.event.relatedTarget !== container.node()) {
25797                 show();
25798             }
25799         }
25800
25801         container = d3.select(document.body)
25802             .append('div')
25803             .style('display', 'none')
25804             .attr('class', 'tail tooltip-inner');
25805
25806         container.append('div')
25807             .text(text);
25808
25809         selection
25810             .on('mousemove.tail', mousemove)
25811             .on('mouseenter.tail', mouseenter)
25812             .on('mouseleave.tail', mouseleave);
25813
25814         container
25815             .on('mousemove.tail', mousemove);
25816
25817         tooltipSize = container.dimensions();
25818         selectionSize = selection.dimensions();
25819     }
25820
25821     tail.off = function(selection) {
25822         if (!text) return;
25823
25824         container
25825             .on('mousemove.tail', null)
25826             .remove();
25827
25828         selection
25829             .on('mousemove.tail', null)
25830             .on('mouseenter.tail', null)
25831             .on('mouseleave.tail', null);
25832
25833         d3.select(window)
25834             .on('resize.tail', null);
25835     };
25836
25837     tail.text = function(_) {
25838         if (!arguments.length) return text;
25839         text = _;
25840         return tail;
25841     };
25842
25843     return tail;
25844 };
25845 iD.modes = {};
25846 iD.modes.AddArea = function(context) {
25847     var mode = {
25848         id: 'add-area',
25849         button: 'area',
25850         title: t('modes.add_area.title'),
25851         description: t('modes.add_area.description'),
25852         key: '3'
25853     };
25854
25855     var behavior = iD.behavior.AddWay(context)
25856             .tail(t('modes.add_area.tail'))
25857             .on('start', start)
25858             .on('startFromWay', startFromWay)
25859             .on('startFromNode', startFromNode),
25860         defaultTags = {area: 'yes'};
25861
25862     function start(loc) {
25863         var graph = context.graph(),
25864             node = iD.Node({loc: loc}),
25865             way = iD.Way({tags: defaultTags});
25866
25867         context.perform(
25868             iD.actions.AddEntity(node),
25869             iD.actions.AddEntity(way),
25870             iD.actions.AddVertex(way.id, node.id),
25871             iD.actions.AddVertex(way.id, node.id));
25872
25873         context.enter(iD.modes.DrawArea(context, way.id, graph));
25874     }
25875
25876     function startFromWay(loc, edge) {
25877         var graph = context.graph(),
25878             node = iD.Node({loc: loc}),
25879             way = iD.Way({tags: defaultTags});
25880
25881         context.perform(
25882             iD.actions.AddEntity(node),
25883             iD.actions.AddEntity(way),
25884             iD.actions.AddVertex(way.id, node.id),
25885             iD.actions.AddVertex(way.id, node.id),
25886             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
25887
25888         context.enter(iD.modes.DrawArea(context, way.id, graph));
25889     }
25890
25891     function startFromNode(node) {
25892         var graph = context.graph(),
25893             way = iD.Way({tags: defaultTags});
25894
25895         context.perform(
25896             iD.actions.AddEntity(way),
25897             iD.actions.AddVertex(way.id, node.id),
25898             iD.actions.AddVertex(way.id, node.id));
25899
25900         context.enter(iD.modes.DrawArea(context, way.id, graph));
25901     }
25902
25903     mode.enter = function() {
25904         context.install(behavior);
25905     };
25906
25907     mode.exit = function() {
25908         context.uninstall(behavior);
25909     };
25910
25911     return mode;
25912 };
25913 iD.modes.AddLine = function(context) {
25914     var mode = {
25915         id: 'add-line',
25916         button: 'line',
25917         title: t('modes.add_line.title'),
25918         description: t('modes.add_line.description'),
25919         key: '2'
25920     };
25921
25922     var behavior = iD.behavior.AddWay(context)
25923         .tail(t('modes.add_line.tail'))
25924         .on('start', start)
25925         .on('startFromWay', startFromWay)
25926         .on('startFromNode', startFromNode);
25927
25928     function start(loc) {
25929         var baseGraph = context.graph(),
25930             node = iD.Node({loc: loc}),
25931             way = iD.Way();
25932
25933         context.perform(
25934             iD.actions.AddEntity(node),
25935             iD.actions.AddEntity(way),
25936             iD.actions.AddVertex(way.id, node.id));
25937
25938         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
25939     }
25940
25941     function startFromWay(loc, edge) {
25942         var baseGraph = context.graph(),
25943             node = iD.Node({loc: loc}),
25944             way = iD.Way();
25945
25946         context.perform(
25947             iD.actions.AddEntity(node),
25948             iD.actions.AddEntity(way),
25949             iD.actions.AddVertex(way.id, node.id),
25950             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
25951
25952         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
25953     }
25954
25955     function startFromNode(node) {
25956         var baseGraph = context.graph(),
25957             way = iD.Way();
25958
25959         context.perform(
25960             iD.actions.AddEntity(way),
25961             iD.actions.AddVertex(way.id, node.id));
25962
25963         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
25964     }
25965
25966     mode.enter = function() {
25967         context.install(behavior);
25968     };
25969
25970     mode.exit = function() {
25971         context.uninstall(behavior);
25972     };
25973
25974     return mode;
25975 };
25976 iD.modes.AddPoint = function(context) {
25977     var mode = {
25978         id: 'add-point',
25979         button: 'point',
25980         title: t('modes.add_point.title'),
25981         description: t('modes.add_point.description'),
25982         key: '1'
25983     };
25984
25985     var behavior = iD.behavior.Draw(context)
25986         .tail(t('modes.add_point.tail'))
25987         .on('click', add)
25988         .on('clickWay', addWay)
25989         .on('clickNode', addNode)
25990         .on('cancel', cancel)
25991         .on('finish', cancel);
25992
25993     function add(loc) {
25994         var node = iD.Node({loc: loc});
25995
25996         context.perform(
25997             iD.actions.AddEntity(node),
25998             t('operations.add.annotation.point'));
25999
26000         context.enter(
26001             iD.modes.Select(context, [node.id])
26002                 .suppressMenu(true)
26003                 .newFeature(true));
26004     }
26005
26006     function addWay(loc) {
26007         add(loc);
26008     }
26009
26010     function addNode(node) {
26011         add(node.loc);
26012     }
26013
26014     function cancel() {
26015         context.enter(iD.modes.Browse(context));
26016     }
26017
26018     mode.enter = function() {
26019         context.install(behavior);
26020     };
26021
26022     mode.exit = function() {
26023         context.uninstall(behavior);
26024     };
26025
26026     return mode;
26027 };
26028 iD.modes.Browse = function(context) {
26029     var mode = {
26030         button: 'browse',
26031         id: 'browse',
26032         title: t('modes.browse.title'),
26033         description: t('modes.browse.description')
26034     }, sidebar;
26035
26036     var behaviors = [
26037         iD.behavior.Paste(context),
26038         iD.behavior.Hover(context)
26039             .on('hover', context.ui().sidebar.hover),
26040         iD.behavior.Select(context),
26041         iD.behavior.Lasso(context),
26042         iD.modes.DragNode(context).behavior];
26043
26044     mode.enter = function() {
26045         behaviors.forEach(function(behavior) {
26046             context.install(behavior);
26047         });
26048
26049         // Get focus on the body.
26050         if (document.activeElement && document.activeElement.blur) {
26051             document.activeElement.blur();
26052         }
26053
26054         if (sidebar) {
26055             context.ui().sidebar.show(sidebar);
26056         } else {
26057             context.ui().sidebar.select(null);
26058         }
26059     };
26060
26061     mode.exit = function() {
26062         context.ui().sidebar.hover.cancel();
26063         behaviors.forEach(function(behavior) {
26064             context.uninstall(behavior);
26065         });
26066
26067         if (sidebar) {
26068             context.ui().sidebar.hide();
26069         }
26070     };
26071
26072     mode.sidebar = function(_) {
26073         if (!arguments.length) return sidebar;
26074         sidebar = _;
26075         return mode;
26076     };
26077
26078     return mode;
26079 };
26080 iD.modes.DragNode = function(context) {
26081     var mode = {
26082         id: 'drag-node',
26083         button: 'browse'
26084     };
26085
26086     var nudgeInterval,
26087         activeIDs,
26088         wasMidpoint,
26089         cancelled,
26090         selectedIDs = [],
26091         hover = iD.behavior.Hover(context)
26092             .altDisables(true)
26093             .on('hover', context.ui().sidebar.hover),
26094         edit = iD.behavior.Edit(context);
26095
26096     function edge(point, size) {
26097         var pad = [30, 100, 30, 100];
26098         if (point[0] > size[0] - pad[0]) return [-10, 0];
26099         else if (point[0] < pad[2]) return [10, 0];
26100         else if (point[1] > size[1] - pad[1]) return [0, -10];
26101         else if (point[1] < pad[3]) return [0, 10];
26102         return null;
26103     }
26104
26105     function startNudge(nudge) {
26106         if (nudgeInterval) window.clearInterval(nudgeInterval);
26107         nudgeInterval = window.setInterval(function() {
26108             context.pan(nudge);
26109         }, 50);
26110     }
26111
26112     function stopNudge() {
26113         if (nudgeInterval) window.clearInterval(nudgeInterval);
26114         nudgeInterval = null;
26115     }
26116
26117     function moveAnnotation(entity) {
26118         return t('operations.move.annotation.' + entity.geometry(context.graph()));
26119     }
26120
26121     function connectAnnotation(entity) {
26122         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
26123     }
26124
26125     function origin(entity) {
26126         return context.projection(entity.loc);
26127     }
26128
26129     function start(entity) {
26130         cancelled = d3.event.sourceEvent.shiftKey ||
26131             context.features().hasHiddenConnections(entity, context.graph());
26132
26133         if (cancelled) return behavior.cancel();
26134
26135         wasMidpoint = entity.type === 'midpoint';
26136         if (wasMidpoint) {
26137             var midpoint = entity;
26138             entity = iD.Node();
26139             context.perform(iD.actions.AddMidpoint(midpoint, entity));
26140
26141              var vertex = context.surface()
26142                 .selectAll('.' + entity.id);
26143              behavior.target(vertex.node(), entity);
26144
26145         } else {
26146             context.perform(
26147                 iD.actions.Noop());
26148         }
26149
26150         activeIDs = _.map(context.graph().parentWays(entity), 'id');
26151         activeIDs.push(entity.id);
26152
26153         context.enter(mode);
26154     }
26155
26156     function datum() {
26157         if (d3.event.sourceEvent.altKey) {
26158             return {};
26159         }
26160
26161         return d3.event.sourceEvent.target.__data__ || {};
26162     }
26163
26164     // via https://gist.github.com/shawnbot/4166283
26165     function childOf(p, c) {
26166         if (p === c) return false;
26167         while (c && c !== p) c = c.parentNode;
26168         return c === p;
26169     }
26170
26171     function move(entity) {
26172         if (cancelled) return;
26173         d3.event.sourceEvent.stopPropagation();
26174
26175         var nudge = childOf(context.container().node(),
26176             d3.event.sourceEvent.toElement) &&
26177             edge(d3.event.point, context.map().dimensions());
26178
26179         if (nudge) startNudge(nudge);
26180         else stopNudge();
26181
26182         var loc = context.projection.invert(d3.event.point);
26183
26184         var d = datum();
26185         if (d.type === 'node' && d.id !== entity.id) {
26186             loc = d.loc;
26187         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
26188             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
26189         }
26190
26191         context.replace(
26192             iD.actions.MoveNode(entity.id, loc),
26193             moveAnnotation(entity));
26194     }
26195
26196     function end(entity) {
26197         if (cancelled) return;
26198
26199         var d = datum();
26200
26201         if (d.type === 'way') {
26202             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
26203             context.replace(
26204                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
26205                 connectAnnotation(d));
26206
26207         } else if (d.type === 'node' && d.id !== entity.id) {
26208             context.replace(
26209                 iD.actions.Connect([d.id, entity.id]),
26210                 connectAnnotation(d));
26211
26212         } else if (wasMidpoint) {
26213             context.replace(
26214                 iD.actions.Noop(),
26215                 t('operations.add.annotation.vertex'));
26216
26217         } else {
26218             context.replace(
26219                 iD.actions.Noop(),
26220                 moveAnnotation(entity));
26221         }
26222
26223         var reselection = selectedIDs.filter(function(id) {
26224             return context.graph().hasEntity(id);
26225         });
26226
26227         if (reselection.length) {
26228             context.enter(
26229                 iD.modes.Select(context, reselection)
26230                     .suppressMenu(true));
26231         } else {
26232             context.enter(iD.modes.Browse(context));
26233         }
26234     }
26235
26236     function cancel() {
26237         behavior.cancel();
26238         context.enter(iD.modes.Browse(context));
26239     }
26240
26241     function setActiveElements() {
26242         context.surface().selectAll(iD.util.entitySelector(activeIDs))
26243             .classed('active', true);
26244     }
26245
26246     var behavior = iD.behavior.drag()
26247         .delegate('g.node, g.point, g.midpoint')
26248         .surface(context.surface().node())
26249         .origin(origin)
26250         .on('start', start)
26251         .on('move', move)
26252         .on('end', end);
26253
26254     mode.enter = function() {
26255         context.install(hover);
26256         context.install(edit);
26257
26258         context.history()
26259             .on('undone.drag-node', cancel);
26260
26261         context.map()
26262             .on('drawn.drag-node', setActiveElements);
26263
26264         setActiveElements();
26265     };
26266
26267     mode.exit = function() {
26268         context.ui().sidebar.hover.cancel();
26269         context.uninstall(hover);
26270         context.uninstall(edit);
26271
26272         context.history()
26273             .on('undone.drag-node', null);
26274
26275         context.map()
26276             .on('drawn.drag-node', null);
26277
26278         context.surface()
26279             .selectAll('.active')
26280             .classed('active', false);
26281
26282         stopNudge();
26283     };
26284
26285     mode.selectedIDs = function(_) {
26286         if (!arguments.length) return selectedIDs;
26287         selectedIDs = _;
26288         return mode;
26289     };
26290
26291     mode.behavior = behavior;
26292
26293     return mode;
26294 };
26295 iD.modes.DrawArea = function(context, wayId, baseGraph) {
26296     var mode = {
26297         button: 'area',
26298         id: 'draw-area'
26299     };
26300
26301     var behavior;
26302
26303     mode.enter = function() {
26304         var way = context.entity(wayId),
26305             headId = way.nodes[way.nodes.length - 2],
26306             tailId = way.first();
26307
26308         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
26309             .tail(t('modes.draw_area.tail'));
26310
26311         var addNode = behavior.addNode;
26312
26313         behavior.addNode = function(node) {
26314             if (node.id === headId || node.id === tailId) {
26315                 behavior.finish();
26316             } else {
26317                 addNode(node);
26318             }
26319         };
26320
26321         context.install(behavior);
26322     };
26323
26324     mode.exit = function() {
26325         context.uninstall(behavior);
26326     };
26327
26328     mode.selectedIDs = function() {
26329         return [wayId];
26330     };
26331
26332     return mode;
26333 };
26334 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
26335     var mode = {
26336         button: 'line',
26337         id: 'draw-line'
26338     };
26339
26340     var behavior;
26341
26342     mode.enter = function() {
26343         var way = context.entity(wayId),
26344             index = (affix === 'prefix') ? 0 : undefined,
26345             headId = (affix === 'prefix') ? way.first() : way.last();
26346
26347         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
26348             .tail(t('modes.draw_line.tail'));
26349
26350         var addNode = behavior.addNode;
26351
26352         behavior.addNode = function(node) {
26353             if (node.id === headId) {
26354                 behavior.finish();
26355             } else {
26356                 addNode(node);
26357             }
26358         };
26359
26360         context.install(behavior);
26361     };
26362
26363     mode.exit = function() {
26364         context.uninstall(behavior);
26365     };
26366
26367     mode.selectedIDs = function() {
26368         return [wayId];
26369     };
26370
26371     return mode;
26372 };
26373 iD.modes.Move = function(context, entityIDs, baseGraph) {
26374     var mode = {
26375         id: 'move',
26376         button: 'browse'
26377     };
26378
26379     var keybinding = d3.keybinding('move'),
26380         edit = iD.behavior.Edit(context),
26381         annotation = entityIDs.length === 1 ?
26382             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
26383             t('operations.move.annotation.multiple'),
26384         cache,
26385         origin,
26386         nudgeInterval;
26387
26388     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
26389
26390     function edge(point, size) {
26391         var pad = [30, 100, 30, 100];
26392         if (point[0] > size[0] - pad[0]) return [-10, 0];
26393         else if (point[0] < pad[2]) return [10, 0];
26394         else if (point[1] > size[1] - pad[1]) return [0, -10];
26395         else if (point[1] < pad[3]) return [0, 10];
26396         return null;
26397     }
26398
26399     function startNudge(nudge) {
26400         if (nudgeInterval) window.clearInterval(nudgeInterval);
26401         nudgeInterval = window.setInterval(function() {
26402             context.pan(nudge);
26403
26404             var currMouse = context.mouse(),
26405                 origMouse = context.projection(origin),
26406                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
26407                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
26408
26409             context.overwrite(action, annotation);
26410
26411         }, 50);
26412     }
26413
26414     function stopNudge() {
26415         if (nudgeInterval) window.clearInterval(nudgeInterval);
26416         nudgeInterval = null;
26417     }
26418
26419     function move() {
26420         var currMouse = context.mouse(),
26421             origMouse = context.projection(origin),
26422             delta = vecSub(currMouse, origMouse),
26423             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
26424
26425         context.overwrite(action, annotation);
26426
26427         var nudge = edge(currMouse, context.map().dimensions());
26428         if (nudge) startNudge(nudge);
26429         else stopNudge();
26430     }
26431
26432     function finish() {
26433         d3.event.stopPropagation();
26434         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
26435         stopNudge();
26436     }
26437
26438     function cancel() {
26439         if (baseGraph) {
26440             while (context.graph() !== baseGraph) context.pop();
26441             context.enter(iD.modes.Browse(context));
26442         } else {
26443             context.pop();
26444             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
26445         }
26446         stopNudge();
26447     }
26448
26449     function undone() {
26450         context.enter(iD.modes.Browse(context));
26451     }
26452
26453     mode.enter = function() {
26454         origin = context.map().mouseCoordinates();
26455         cache = {};
26456
26457         context.install(edit);
26458
26459         context.perform(
26460             iD.actions.Noop(),
26461             annotation);
26462
26463         context.surface()
26464             .on('mousemove.move', move)
26465             .on('click.move', finish);
26466
26467         context.history()
26468             .on('undone.move', undone);
26469
26470         keybinding
26471             .on('⎋', cancel)
26472             .on('↩', finish);
26473
26474         d3.select(document)
26475             .call(keybinding);
26476     };
26477
26478     mode.exit = function() {
26479         stopNudge();
26480
26481         context.uninstall(edit);
26482
26483         context.surface()
26484             .on('mousemove.move', null)
26485             .on('click.move', null);
26486
26487         context.history()
26488             .on('undone.move', null);
26489
26490         keybinding.off();
26491     };
26492
26493     return mode;
26494 };
26495 iD.modes.RotateWay = function(context, wayId) {
26496     var mode = {
26497         id: 'rotate-way',
26498         button: 'browse'
26499     };
26500
26501     var keybinding = d3.keybinding('rotate-way'),
26502         edit = iD.behavior.Edit(context);
26503
26504     mode.enter = function() {
26505         context.install(edit);
26506
26507         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
26508             way = context.graph().entity(wayId),
26509             nodes = _.uniq(context.graph().childNodes(way)),
26510             points = nodes.map(function(n) { return context.projection(n.loc); }),
26511             pivot = d3.geom.polygon(points).centroid(),
26512             angle;
26513
26514         context.perform(
26515             iD.actions.Noop(),
26516             annotation);
26517
26518         function rotate() {
26519
26520             var mousePoint = context.mouse(),
26521                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
26522
26523             if (typeof angle === 'undefined') angle = newAngle;
26524
26525             context.replace(
26526                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
26527                 annotation);
26528
26529             angle = newAngle;
26530         }
26531
26532         function finish() {
26533             d3.event.stopPropagation();
26534             context.enter(iD.modes.Select(context, [wayId])
26535                 .suppressMenu(true));
26536         }
26537
26538         function cancel() {
26539             context.pop();
26540             context.enter(iD.modes.Select(context, [wayId])
26541                 .suppressMenu(true));
26542         }
26543
26544         function undone() {
26545             context.enter(iD.modes.Browse(context));
26546         }
26547
26548         context.surface()
26549             .on('mousemove.rotate-way', rotate)
26550             .on('click.rotate-way', finish);
26551
26552         context.history()
26553             .on('undone.rotate-way', undone);
26554
26555         keybinding
26556             .on('⎋', cancel)
26557             .on('↩', finish);
26558
26559         d3.select(document)
26560             .call(keybinding);
26561     };
26562
26563     mode.exit = function() {
26564         context.uninstall(edit);
26565
26566         context.surface()
26567             .on('mousemove.rotate-way', null)
26568             .on('click.rotate-way', null);
26569
26570         context.history()
26571             .on('undone.rotate-way', null);
26572
26573         keybinding.off();
26574     };
26575
26576     return mode;
26577 };
26578 iD.modes.Save = function(context) {
26579     var ui = iD.ui.Commit(context)
26580             .on('cancel', cancel)
26581             .on('save', save);
26582
26583     function cancel() {
26584         context.enter(iD.modes.Browse(context));
26585     }
26586
26587     function save(e, tryAgain) {
26588         function withChildNodes(ids, graph) {
26589             return _.uniq(_.reduce(ids, function(result, id) {
26590                 var e = graph.entity(id);
26591                 if (e.type === 'way') {
26592                     try {
26593                         var cn = graph.childNodes(e);
26594                         result.push.apply(result, _.map(_.filter(cn, 'version'), 'id'));
26595                     } catch(err) {
26596                         /* eslint-disable no-console */
26597                         if (typeof console !== 'undefined') console.error(err);
26598                         /* eslint-enable no-console */
26599                     }
26600                 }
26601                 return result;
26602             }, _.clone(ids)));
26603         }
26604
26605         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
26606             history = context.history(),
26607             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
26608             localGraph = context.graph(),
26609             remoteGraph = iD.Graph(history.base(), true),
26610             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
26611             toCheck = _.map(_.map(modified, 'entity'), 'id'),
26612             toLoad = withChildNodes(toCheck, localGraph),
26613             conflicts = [],
26614             errors = [];
26615
26616         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
26617         context.container().call(loading);
26618
26619         if (toCheck.length) {
26620             context.connection().loadMultiple(toLoad, loaded);
26621         } else {
26622             finalize();
26623         }
26624
26625
26626         // Reload modified entities into an alternate graph and check for conflicts..
26627         function loaded(err, result) {
26628             if (errors.length) return;
26629
26630             if (err) {
26631                 errors.push({
26632                     msg: err.responseText,
26633                     details: [ t('save.status_code', { code: err.status }) ]
26634                 });
26635                 showErrors();
26636
26637             } else {
26638                 var loadMore = [];
26639                 _.each(result.data, function(entity) {
26640                     remoteGraph.replace(entity);
26641                     toLoad = _.without(toLoad, entity.id);
26642
26643                     // Because loadMultiple doesn't download /full like loadEntity,
26644                     // need to also load children that aren't already being checked..
26645                     if (!entity.visible) return;
26646                     if (entity.type === 'way') {
26647                         loadMore.push.apply(loadMore,
26648                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
26649                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
26650                         loadMore.push.apply(loadMore,
26651                             _.difference(_.map(entity.members, 'id'), toCheck, toLoad, loadMore));
26652                     }
26653                 });
26654
26655                 if (loadMore.length) {
26656                     toLoad.push.apply(toLoad, loadMore);
26657                     context.connection().loadMultiple(loadMore, loaded);
26658                 }
26659
26660                 if (!toLoad.length) {
26661                     checkConflicts();
26662                 }
26663             }
26664         }
26665
26666
26667         function checkConflicts() {
26668             function choice(id, text, action) {
26669                 return { id: id, text: text, action: function() { history.replace(action); } };
26670             }
26671             function formatUser(d) {
26672                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
26673             }
26674             function entityName(entity) {
26675                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
26676             }
26677
26678             function compareVersions(local, remote) {
26679                 if (local.version !== remote.version) return false;
26680
26681                 if (local.type === 'way') {
26682                     var children = _.union(local.nodes, remote.nodes);
26683
26684                     for (var i = 0; i < children.length; i++) {
26685                         var a = localGraph.hasEntity(children[i]),
26686                             b = remoteGraph.hasEntity(children[i]);
26687
26688                         if (a && b && a.version !== b.version) return false;
26689                     }
26690                 }
26691
26692                 return true;
26693             }
26694
26695             _.each(toCheck, function(id) {
26696                 var local = localGraph.entity(id),
26697                     remote = remoteGraph.entity(id);
26698
26699                 if (compareVersions(local, remote)) return;
26700
26701                 var action = iD.actions.MergeRemoteChanges,
26702                     merge = action(id, localGraph, remoteGraph, formatUser);
26703
26704                 history.replace(merge);
26705
26706                 var mergeConflicts = merge.conflicts();
26707                 if (!mergeConflicts.length) return;  // merged safely
26708
26709                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
26710                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
26711                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
26712                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
26713
26714                 conflicts.push({
26715                     id: id,
26716                     name: entityName(local),
26717                     details: mergeConflicts,
26718                     chosen: 1,
26719                     choices: [
26720                         choice(id, keepMine, forceLocal),
26721                         choice(id, keepTheirs, forceRemote)
26722                     ]
26723                 });
26724             });
26725
26726             finalize();
26727         }
26728
26729
26730         function finalize() {
26731             if (conflicts.length) {
26732                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
26733                 showConflicts();
26734             } else if (errors.length) {
26735                 showErrors();
26736             } else {
26737                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
26738                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
26739                     context.connection().putChangeset(
26740                         changes,
26741                         e.comment,
26742                         history.imageryUsed(),
26743                         function(err, changeset_id) {
26744                             if (err) {
26745                                 errors.push({
26746                                     msg: err.responseText,
26747                                     details: [ t('save.status_code', { code: err.status }) ]
26748                                 });
26749                                 showErrors();
26750                             } else {
26751                                 history.clearSaved();
26752                                 success(e, changeset_id);
26753                                 // Add delay to allow for postgres replication #1646 #2678
26754                                 window.setTimeout(function() {
26755                                     loading.close();
26756                                     context.flush();
26757                                 }, 2500);
26758                             }
26759                         });
26760                 } else {        // changes were insignificant or reverted by user
26761                     loading.close();
26762                     context.flush();
26763                     cancel();
26764                 }
26765             }
26766         }
26767
26768
26769         function showConflicts() {
26770             var selection = context.container()
26771                 .select('#sidebar')
26772                 .append('div')
26773                 .attr('class','sidebar-component');
26774
26775             loading.close();
26776
26777             selection.call(iD.ui.Conflicts(context)
26778                 .list(conflicts)
26779                 .on('download', function() {
26780                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
26781                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
26782                     win.focus();
26783                 })
26784                 .on('cancel', function() {
26785                     history.pop();
26786                     selection.remove();
26787                 })
26788                 .on('save', function() {
26789                     for (var i = 0; i < conflicts.length; i++) {
26790                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
26791                             var entity = context.hasEntity(conflicts[i].id);
26792                             if (entity && entity.type === 'way') {
26793                                 var children = _.uniq(entity.nodes);
26794                                 for (var j = 0; j < children.length; j++) {
26795                                     history.replace(iD.actions.Revert(children[j]));
26796                                 }
26797                             }
26798                             history.replace(iD.actions.Revert(conflicts[i].id));
26799                         }
26800                     }
26801
26802                     selection.remove();
26803                     save(e, true);
26804                 })
26805             );
26806         }
26807
26808
26809         function showErrors() {
26810             var selection = iD.ui.confirm(context.container());
26811
26812             history.pop();
26813             loading.close();
26814
26815             selection
26816                 .select('.modal-section.header')
26817                 .append('h3')
26818                 .text(t('save.error'));
26819
26820             addErrors(selection, errors);
26821             selection.okButton();
26822         }
26823
26824
26825         function addErrors(selection, data) {
26826             var message = selection
26827                 .select('.modal-section.message-text');
26828
26829             var items = message
26830                 .selectAll('.error-container')
26831                 .data(data);
26832
26833             var enter = items.enter()
26834                 .append('div')
26835                 .attr('class', 'error-container');
26836
26837             enter
26838                 .append('a')
26839                 .attr('class', 'error-description')
26840                 .attr('href', '#')
26841                 .classed('hide-toggle', true)
26842                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
26843                 .on('click', function() {
26844                     var error = d3.select(this),
26845                         detail = d3.select(this.nextElementSibling),
26846                         exp = error.classed('expanded');
26847
26848                     detail.style('display', exp ? 'none' : 'block');
26849                     error.classed('expanded', !exp);
26850
26851                     d3.event.preventDefault();
26852                 });
26853
26854             var details = enter
26855                 .append('div')
26856                 .attr('class', 'error-detail-container')
26857                 .style('display', 'none');
26858
26859             details
26860                 .append('ul')
26861                 .attr('class', 'error-detail-list')
26862                 .selectAll('li')
26863                 .data(function(d) { return d.details || []; })
26864                 .enter()
26865                 .append('li')
26866                 .attr('class', 'error-detail-item')
26867                 .text(function(d) { return d; });
26868
26869             items.exit()
26870                 .remove();
26871         }
26872
26873     }
26874
26875
26876     function success(e, changeset_id) {
26877         context.enter(iD.modes.Browse(context)
26878             .sidebar(iD.ui.Success(context)
26879                 .changeset({
26880                     id: changeset_id,
26881                     comment: e.comment
26882                 })
26883                 .on('cancel', function() {
26884                     context.ui().sidebar.hide();
26885                 })));
26886     }
26887
26888     var mode = {
26889         id: 'save'
26890     };
26891
26892     mode.enter = function() {
26893         context.connection().authenticate(function(err) {
26894             if (err) {
26895                 cancel();
26896             } else {
26897                 context.ui().sidebar.show(ui);
26898             }
26899         });
26900     };
26901
26902     mode.exit = function() {
26903         context.ui().sidebar.hide();
26904     };
26905
26906     return mode;
26907 };
26908 iD.modes.Select = function(context, selectedIDs) {
26909     var mode = {
26910         id: 'select',
26911         button: 'browse'
26912     };
26913
26914     var keybinding = d3.keybinding('select'),
26915         timeout = null,
26916         behaviors = [
26917             iD.behavior.Copy(context),
26918             iD.behavior.Paste(context),
26919             iD.behavior.Breathe(context),
26920             iD.behavior.Hover(context),
26921             iD.behavior.Select(context),
26922             iD.behavior.Lasso(context),
26923             iD.modes.DragNode(context)
26924                 .selectedIDs(selectedIDs)
26925                 .behavior],
26926         inspector,
26927         radialMenu,
26928         newFeature = false,
26929         suppressMenu = false;
26930
26931     var wrap = context.container()
26932         .select('.inspector-wrap');
26933
26934
26935     function singular() {
26936         if (selectedIDs.length === 1) {
26937             return context.hasEntity(selectedIDs[0]);
26938         }
26939     }
26940
26941     function closeMenu() {
26942         if (radialMenu) {
26943             context.surface().call(radialMenu.close);
26944         }
26945     }
26946
26947     function positionMenu() {
26948         if (suppressMenu || !radialMenu) { return; }
26949
26950         var entity = singular();
26951         if (entity && context.geometry(entity.id) === 'relation') {
26952             suppressMenu = true;
26953         } else if (entity && entity.type === 'node') {
26954             radialMenu.center(context.projection(entity.loc));
26955         } else {
26956             var point = context.mouse(),
26957                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
26958             if (iD.geo.pointInPolygon(point, viewport)) {
26959                 radialMenu.center(point);
26960             } else {
26961                 suppressMenu = true;
26962             }
26963         }
26964     }
26965
26966     function showMenu() {
26967         closeMenu();
26968         if (!suppressMenu && radialMenu) {
26969             context.surface().call(radialMenu);
26970         }
26971     }
26972
26973     function toggleMenu() {
26974         if (d3.select('.radial-menu').empty()) {
26975             showMenu();
26976         } else {
26977             closeMenu();
26978         }
26979     }
26980
26981     mode.selectedIDs = function() {
26982         return selectedIDs;
26983     };
26984
26985     mode.reselect = function() {
26986         var surfaceNode = context.surface().node();
26987         if (surfaceNode.focus) { // FF doesn't support it
26988             surfaceNode.focus();
26989         }
26990
26991         positionMenu();
26992         showMenu();
26993     };
26994
26995     mode.newFeature = function(_) {
26996         if (!arguments.length) return newFeature;
26997         newFeature = _;
26998         return mode;
26999     };
27000
27001     mode.suppressMenu = function(_) {
27002         if (!arguments.length) return suppressMenu;
27003         suppressMenu = _;
27004         return mode;
27005     };
27006
27007     mode.enter = function() {
27008         function update() {
27009             closeMenu();
27010             if (_.some(selectedIDs, function(id) { return !context.hasEntity(id); })) {
27011                 // Exit mode if selected entity gets undone
27012                 context.enter(iD.modes.Browse(context));
27013             }
27014         }
27015
27016         function dblclick() {
27017             var target = d3.select(d3.event.target),
27018                 datum = target.datum();
27019
27020             if (datum instanceof iD.Way && !target.classed('fill')) {
27021                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
27022                     node = iD.Node();
27023
27024                 var prev = datum.nodes[choice.index - 1],
27025                     next = datum.nodes[choice.index];
27026
27027                 context.perform(
27028                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
27029                     t('operations.add.annotation.vertex'));
27030
27031                 d3.event.preventDefault();
27032                 d3.event.stopPropagation();
27033             }
27034         }
27035
27036         function selectElements(drawn) {
27037             var entity = singular();
27038             if (entity && context.geometry(entity.id) === 'relation') {
27039                 suppressMenu = true;
27040                 return;
27041             }
27042
27043             var selection = context.surface()
27044                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
27045
27046             if (selection.empty()) {
27047                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
27048                     context.enter(iD.modes.Browse(context));
27049                 }
27050             } else {
27051                 selection
27052                     .classed('selected', true);
27053             }
27054         }
27055
27056         function esc() {
27057             if (!context.inIntro()) {
27058                 context.enter(iD.modes.Browse(context));
27059             }
27060         }
27061
27062
27063         behaviors.forEach(function(behavior) {
27064             context.install(behavior);
27065         });
27066
27067         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
27068                 .map(function(o) { return o(selectedIDs, context); })
27069                 .filter(function(o) { return o.available(); });
27070
27071         operations.unshift(iD.operations.Delete(selectedIDs, context));
27072
27073         keybinding
27074             .on('⎋', esc, true)
27075             .on('space', toggleMenu);
27076
27077         operations.forEach(function(operation) {
27078             operation.keys.forEach(function(key) {
27079                 keybinding.on(key, function() {
27080                     if (!(context.inIntro() || operation.disabled())) {
27081                         operation();
27082                     }
27083                 });
27084             });
27085         });
27086
27087         d3.select(document)
27088             .call(keybinding);
27089
27090         radialMenu = iD.ui.RadialMenu(context, operations);
27091
27092         context.ui().sidebar
27093             .select(singular() ? singular().id : null, newFeature);
27094
27095         context.history()
27096             .on('undone.select', update)
27097             .on('redone.select', update);
27098
27099         context.map()
27100             .on('move.select', closeMenu)
27101             .on('drawn.select', selectElements);
27102
27103         selectElements();
27104
27105         var show = d3.event && !suppressMenu;
27106
27107         if (show) {
27108             positionMenu();
27109         }
27110
27111         timeout = window.setTimeout(function() {
27112             if (show) {
27113                 showMenu();
27114             }
27115
27116             context.surface()
27117                 .on('dblclick.select', dblclick);
27118         }, 200);
27119
27120         if (selectedIDs.length > 1) {
27121             var entities = iD.ui.SelectionList(context, selectedIDs);
27122             context.ui().sidebar.show(entities);
27123         }
27124     };
27125
27126     mode.exit = function() {
27127         if (timeout) window.clearTimeout(timeout);
27128
27129         if (inspector) wrap.call(inspector.close);
27130
27131         behaviors.forEach(function(behavior) {
27132             context.uninstall(behavior);
27133         });
27134
27135         keybinding.off();
27136         closeMenu();
27137         radialMenu = undefined;
27138
27139         context.history()
27140             .on('undone.select', null)
27141             .on('redone.select', null);
27142
27143         context.surface()
27144             .on('dblclick.select', null)
27145             .selectAll('.selected')
27146             .classed('selected', false);
27147
27148         context.map().on('drawn.select', null);
27149         context.ui().sidebar.hide();
27150     };
27151
27152     return mode;
27153 };
27154 iD.operations = {};
27155 iD.operations.Circularize = function(selectedIDs, context) {
27156     var entityId = selectedIDs[0],
27157         entity = context.entity(entityId),
27158         extent = entity.extent(context.graph()),
27159         geometry = context.geometry(entityId),
27160         action = iD.actions.Circularize(entityId, context.projection);
27161
27162     var operation = function() {
27163         var annotation = t('operations.circularize.annotation.' + geometry);
27164         context.perform(action, annotation);
27165     };
27166
27167     operation.available = function() {
27168         return selectedIDs.length === 1 &&
27169             entity.type === 'way' &&
27170             _.uniq(entity.nodes).length > 1;
27171     };
27172
27173     operation.disabled = function() {
27174         var reason;
27175         if (extent.percentContainedIn(context.extent()) < 0.8) {
27176             reason = 'too_large';
27177         } else if (context.hasHiddenConnections(entityId)) {
27178             reason = 'connected_to_hidden';
27179         }
27180         return action.disabled(context.graph()) || reason;
27181     };
27182
27183     operation.tooltip = function() {
27184         var disable = operation.disabled();
27185         return disable ?
27186             t('operations.circularize.' + disable) :
27187             t('operations.circularize.description.' + geometry);
27188     };
27189
27190     operation.id = 'circularize';
27191     operation.keys = [t('operations.circularize.key')];
27192     operation.title = t('operations.circularize.title');
27193
27194     return operation;
27195 };
27196 iD.operations.Continue = function(selectedIDs, context) {
27197     var graph = context.graph(),
27198         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
27199         geometries = _.extend({line: [], vertex: []},
27200             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
27201         vertex = geometries.vertex[0];
27202
27203     function candidateWays() {
27204         return graph.parentWays(vertex).filter(function(parent) {
27205             return parent.geometry(graph) === 'line' &&
27206                 parent.affix(vertex.id) &&
27207                 (geometries.line.length === 0 || geometries.line[0] === parent);
27208         });
27209     }
27210
27211     var operation = function() {
27212         var candidate = candidateWays()[0];
27213         context.enter(iD.modes.DrawLine(
27214             context,
27215             candidate.id,
27216             context.graph(),
27217             candidate.affix(vertex.id)));
27218     };
27219
27220     operation.available = function() {
27221         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
27222             !context.features().hasHiddenConnections(vertex, context.graph());
27223     };
27224
27225     operation.disabled = function() {
27226         var candidates = candidateWays();
27227         if (candidates.length === 0)
27228             return 'not_eligible';
27229         if (candidates.length > 1)
27230             return 'multiple';
27231     };
27232
27233     operation.tooltip = function() {
27234         var disable = operation.disabled();
27235         return disable ?
27236             t('operations.continue.' + disable) :
27237             t('operations.continue.description');
27238     };
27239
27240     operation.id = 'continue';
27241     operation.keys = [t('operations.continue.key')];
27242     operation.title = t('operations.continue.title');
27243
27244     return operation;
27245 };
27246 iD.operations.Delete = function(selectedIDs, context) {
27247     var action = iD.actions.DeleteMultiple(selectedIDs);
27248
27249     var operation = function() {
27250         var annotation,
27251             nextSelectedID;
27252
27253         if (selectedIDs.length > 1) {
27254             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
27255
27256         } else {
27257             var id = selectedIDs[0],
27258                 entity = context.entity(id),
27259                 geometry = context.geometry(id),
27260                 parents = context.graph().parentWays(entity),
27261                 parent = parents[0];
27262
27263             annotation = t('operations.delete.annotation.' + geometry);
27264
27265             // Select the next closest node in the way.
27266             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
27267                 var nodes = parent.nodes,
27268                     i = nodes.indexOf(id);
27269
27270                 if (i === 0) {
27271                     i++;
27272                 } else if (i === nodes.length - 1) {
27273                     i--;
27274                 } else {
27275                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
27276                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
27277                     i = a < b ? i - 1 : i + 1;
27278                 }
27279
27280                 nextSelectedID = nodes[i];
27281             }
27282         }
27283
27284         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
27285             context.enter(iD.modes.Select(context, [nextSelectedID]));
27286         } else {
27287             context.enter(iD.modes.Browse(context));
27288         }
27289
27290         context.perform(
27291             action,
27292             annotation);
27293     };
27294
27295     operation.available = function() {
27296         return true;
27297     };
27298
27299     operation.disabled = function() {
27300         var reason;
27301         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27302             reason = 'connected_to_hidden';
27303         }
27304         return action.disabled(context.graph()) || reason;
27305     };
27306
27307     operation.tooltip = function() {
27308         var disable = operation.disabled();
27309         return disable ?
27310             t('operations.delete.' + disable) :
27311             t('operations.delete.description');
27312     };
27313
27314     operation.id = 'delete';
27315     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
27316     operation.title = t('operations.delete.title');
27317
27318     return operation;
27319 };
27320 iD.operations.Disconnect = function(selectedIDs, context) {
27321     var vertices = _.filter(selectedIDs, function vertex(entityId) {
27322         return context.geometry(entityId) === 'vertex';
27323     });
27324
27325     var entityId = vertices[0],
27326         action = iD.actions.Disconnect(entityId);
27327
27328     if (selectedIDs.length > 1) {
27329         action.limitWays(_.without(selectedIDs, entityId));
27330     }
27331
27332     var operation = function() {
27333         context.perform(action, t('operations.disconnect.annotation'));
27334     };
27335
27336     operation.available = function() {
27337         return vertices.length === 1;
27338     };
27339
27340     operation.disabled = function() {
27341         var reason;
27342         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27343             reason = 'connected_to_hidden';
27344         }
27345         return action.disabled(context.graph()) || reason;
27346     };
27347
27348     operation.tooltip = function() {
27349         var disable = operation.disabled();
27350         return disable ?
27351             t('operations.disconnect.' + disable) :
27352             t('operations.disconnect.description');
27353     };
27354
27355     operation.id = 'disconnect';
27356     operation.keys = [t('operations.disconnect.key')];
27357     operation.title = t('operations.disconnect.title');
27358
27359     return operation;
27360 };
27361 iD.operations.Merge = function(selectedIDs, context) {
27362     var join = iD.actions.Join(selectedIDs),
27363         merge = iD.actions.Merge(selectedIDs),
27364         mergePolygon = iD.actions.MergePolygon(selectedIDs);
27365
27366     var operation = function() {
27367         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
27368             action;
27369
27370         if (!join.disabled(context.graph())) {
27371             action = join;
27372         } else if (!merge.disabled(context.graph())) {
27373             action = merge;
27374         } else {
27375             action = mergePolygon;
27376         }
27377
27378         context.perform(action, annotation);
27379         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
27380             .suppressMenu(true));
27381     };
27382
27383     operation.available = function() {
27384         return selectedIDs.length >= 2;
27385     };
27386
27387     operation.disabled = function() {
27388         return join.disabled(context.graph()) &&
27389             merge.disabled(context.graph()) &&
27390             mergePolygon.disabled(context.graph());
27391     };
27392
27393     operation.tooltip = function() {
27394         var j = join.disabled(context.graph()),
27395             m = merge.disabled(context.graph()),
27396             p = mergePolygon.disabled(context.graph());
27397
27398         if (j === 'restriction' && m && p)
27399             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
27400
27401         if (p === 'incomplete_relation' && j && m)
27402             return t('operations.merge.incomplete_relation');
27403
27404         if (j && m && p)
27405             return t('operations.merge.' + j);
27406
27407         return t('operations.merge.description');
27408     };
27409
27410     operation.id = 'merge';
27411     operation.keys = [t('operations.merge.key')];
27412     operation.title = t('operations.merge.title');
27413
27414     return operation;
27415 };
27416 iD.operations.Move = function(selectedIDs, context) {
27417     var extent = selectedIDs.reduce(function(extent, id) {
27418             return extent.extend(context.entity(id).extent(context.graph()));
27419         }, iD.geo.Extent());
27420
27421     var operation = function() {
27422         context.enter(iD.modes.Move(context, selectedIDs));
27423     };
27424
27425     operation.available = function() {
27426         return selectedIDs.length > 1 ||
27427             context.entity(selectedIDs[0]).type !== 'node';
27428     };
27429
27430     operation.disabled = function() {
27431         var reason;
27432         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
27433             reason = 'too_large';
27434         } else if (_.some(selectedIDs, context.hasHiddenConnections)) {
27435             reason = 'connected_to_hidden';
27436         }
27437         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
27438     };
27439
27440     operation.tooltip = function() {
27441         var disable = operation.disabled();
27442         return disable ?
27443             t('operations.move.' + disable) :
27444             t('operations.move.description');
27445     };
27446
27447     operation.id = 'move';
27448     operation.keys = [t('operations.move.key')];
27449     operation.title = t('operations.move.title');
27450
27451     return operation;
27452 };
27453 iD.operations.Orthogonalize = function(selectedIDs, context) {
27454     var entityId = selectedIDs[0],
27455         entity = context.entity(entityId),
27456         extent = entity.extent(context.graph()),
27457         geometry = context.geometry(entityId),
27458         action = iD.actions.Orthogonalize(entityId, context.projection);
27459
27460     var operation = function() {
27461         var annotation = t('operations.orthogonalize.annotation.' + geometry);
27462         context.perform(action, annotation);
27463     };
27464
27465     operation.available = function() {
27466         return selectedIDs.length === 1 &&
27467             entity.type === 'way' &&
27468             entity.isClosed() &&
27469             _.uniq(entity.nodes).length > 2;
27470     };
27471
27472     operation.disabled = function() {
27473         var reason;
27474         if (extent.percentContainedIn(context.extent()) < 0.8) {
27475             reason = 'too_large';
27476         } else if (context.hasHiddenConnections(entityId)) {
27477             reason = 'connected_to_hidden';
27478         }
27479         return action.disabled(context.graph()) || reason;
27480     };
27481
27482     operation.tooltip = function() {
27483         var disable = operation.disabled();
27484         return disable ?
27485             t('operations.orthogonalize.' + disable) :
27486             t('operations.orthogonalize.description.' + geometry);
27487     };
27488
27489     operation.id = 'orthogonalize';
27490     operation.keys = [t('operations.orthogonalize.key')];
27491     operation.title = t('operations.orthogonalize.title');
27492
27493     return operation;
27494 };
27495 iD.operations.Reverse = function(selectedIDs, context) {
27496     var entityId = selectedIDs[0];
27497
27498     var operation = function() {
27499         context.perform(
27500             iD.actions.Reverse(entityId),
27501             t('operations.reverse.annotation'));
27502     };
27503
27504     operation.available = function() {
27505         return selectedIDs.length === 1 &&
27506             context.geometry(entityId) === 'line';
27507     };
27508
27509     operation.disabled = function() {
27510         return false;
27511     };
27512
27513     operation.tooltip = function() {
27514         return t('operations.reverse.description');
27515     };
27516
27517     operation.id = 'reverse';
27518     operation.keys = [t('operations.reverse.key')];
27519     operation.title = t('operations.reverse.title');
27520
27521     return operation;
27522 };
27523 iD.operations.Rotate = function(selectedIDs, context) {
27524     var entityId = selectedIDs[0],
27525         entity = context.entity(entityId),
27526         extent = entity.extent(context.graph()),
27527         geometry = context.geometry(entityId);
27528
27529     var operation = function() {
27530         context.enter(iD.modes.RotateWay(context, entityId));
27531     };
27532
27533     operation.available = function() {
27534         if (selectedIDs.length !== 1 || entity.type !== 'way')
27535             return false;
27536         if (geometry === 'area')
27537             return true;
27538         if (entity.isClosed() &&
27539             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
27540             return true;
27541         return false;
27542     };
27543
27544     operation.disabled = function() {
27545         if (extent.percentContainedIn(context.extent()) < 0.8) {
27546             return 'too_large';
27547         } else if (context.hasHiddenConnections(entityId)) {
27548             return 'connected_to_hidden';
27549         } else {
27550             return false;
27551         }
27552     };
27553
27554     operation.tooltip = function() {
27555         var disable = operation.disabled();
27556         return disable ?
27557             t('operations.rotate.' + disable) :
27558             t('operations.rotate.description');
27559     };
27560
27561     operation.id = 'rotate';
27562     operation.keys = [t('operations.rotate.key')];
27563     operation.title = t('operations.rotate.title');
27564
27565     return operation;
27566 };
27567 iD.operations.Split = function(selectedIDs, context) {
27568     var vertices = _.filter(selectedIDs, function vertex(entityId) {
27569         return context.geometry(entityId) === 'vertex';
27570     });
27571
27572     var entityId = vertices[0],
27573         action = iD.actions.Split(entityId);
27574
27575     if (selectedIDs.length > 1) {
27576         action.limitWays(_.without(selectedIDs, entityId));
27577     }
27578
27579     var operation = function() {
27580         var annotation;
27581
27582         var ways = action.ways(context.graph());
27583         if (ways.length === 1) {
27584             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
27585         } else {
27586             annotation = t('operations.split.annotation.multiple', {n: ways.length});
27587         }
27588
27589         var difference = context.perform(action, annotation);
27590         context.enter(iD.modes.Select(context, difference.extantIDs()));
27591     };
27592
27593     operation.available = function() {
27594         return vertices.length === 1;
27595     };
27596
27597     operation.disabled = function() {
27598         var reason;
27599         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27600             reason = 'connected_to_hidden';
27601         }
27602         return action.disabled(context.graph()) || reason;
27603     };
27604
27605     operation.tooltip = function() {
27606         var disable = operation.disabled();
27607         if (disable) {
27608             return t('operations.split.' + disable);
27609         }
27610
27611         var ways = action.ways(context.graph());
27612         if (ways.length === 1) {
27613             return t('operations.split.description.' + context.geometry(ways[0].id));
27614         } else {
27615             return t('operations.split.description.multiple');
27616         }
27617     };
27618
27619     operation.id = 'split';
27620     operation.keys = [t('operations.split.key')];
27621     operation.title = t('operations.split.title');
27622
27623     return operation;
27624 };
27625 iD.operations.Straighten = function(selectedIDs, context) {
27626     var entityId = selectedIDs[0],
27627         action = iD.actions.Straighten(entityId, context.projection);
27628
27629     function operation() {
27630         var annotation = t('operations.straighten.annotation');
27631         context.perform(action, annotation);
27632     }
27633
27634     operation.available = function() {
27635         var entity = context.entity(entityId);
27636         return selectedIDs.length === 1 &&
27637             entity.type === 'way' &&
27638             !entity.isClosed() &&
27639             _.uniq(entity.nodes).length > 2;
27640     };
27641
27642     operation.disabled = function() {
27643         var reason;
27644         if (context.hasHiddenConnections(entityId)) {
27645             reason = 'connected_to_hidden';
27646         }
27647         return action.disabled(context.graph()) || reason;
27648     };
27649
27650     operation.tooltip = function() {
27651         var disable = operation.disabled();
27652         return disable ?
27653             t('operations.straighten.' + disable) :
27654             t('operations.straighten.description');
27655     };
27656
27657     operation.id = 'straighten';
27658     operation.keys = [t('operations.straighten.key')];
27659     operation.title = t('operations.straighten.title');
27660
27661     return operation;
27662 };
27663 iD.Connection = function(useHttps) {
27664     if (typeof useHttps !== 'boolean') {
27665       useHttps = window.location.protocol === 'https:';
27666     }
27667
27668     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
27669         protocol = useHttps ? 'https:' : 'http:',
27670         url = protocol + '//www.openstreetmap.org',
27671         connection = {},
27672         inflight = {},
27673         loadedTiles = {},
27674         tileZoom = 16,
27675         oauth = osmAuth({
27676             url: protocol + '//www.openstreetmap.org',
27677             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
27678             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
27679             loading: authenticating,
27680             done: authenticated
27681         }),
27682         ndStr = 'nd',
27683         tagStr = 'tag',
27684         memberStr = 'member',
27685         nodeStr = 'node',
27686         wayStr = 'way',
27687         relationStr = 'relation',
27688         userDetails,
27689         off;
27690
27691
27692     connection.changesetURL = function(changesetId) {
27693         return url + '/changeset/' + changesetId;
27694     };
27695
27696     connection.changesetsURL = function(center, zoom) {
27697         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
27698         return url + '/history#map=' +
27699             Math.floor(zoom) + '/' +
27700             center[1].toFixed(precision) + '/' +
27701             center[0].toFixed(precision);
27702     };
27703
27704     connection.entityURL = function(entity) {
27705         return url + '/' + entity.type + '/' + entity.osmId();
27706     };
27707
27708     connection.userURL = function(username) {
27709         return url + '/user/' + username;
27710     };
27711
27712     connection.loadFromURL = function(url, callback) {
27713         function done(err, dom) {
27714             return callback(err, parse(dom));
27715         }
27716         return d3.xml(url).get(done);
27717     };
27718
27719     connection.loadEntity = function(id, callback) {
27720         var type = iD.Entity.id.type(id),
27721             osmID = iD.Entity.id.toOSM(id);
27722
27723         connection.loadFromURL(
27724             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
27725             function(err, entities) {
27726                 if (callback) callback(err, {data: entities});
27727             });
27728     };
27729
27730     connection.loadEntityVersion = function(id, version, callback) {
27731         var type = iD.Entity.id.type(id),
27732             osmID = iD.Entity.id.toOSM(id);
27733
27734         connection.loadFromURL(
27735             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
27736             function(err, entities) {
27737                 if (callback) callback(err, {data: entities});
27738             });
27739     };
27740
27741     connection.loadMultiple = function(ids, callback) {
27742         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
27743             var type = k + 's',
27744                 osmIDs = _.map(v, iD.Entity.id.toOSM);
27745
27746             _.each(_.chunk(osmIDs, 150), function(arr) {
27747                 connection.loadFromURL(
27748                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
27749                     function(err, entities) {
27750                         if (callback) callback(err, {data: entities});
27751                     });
27752             });
27753         });
27754     };
27755
27756     function authenticating() {
27757         event.authenticating();
27758     }
27759
27760     function authenticated() {
27761         event.authenticated();
27762     }
27763
27764     function getLoc(attrs) {
27765         var lon = attrs.lon && attrs.lon.value,
27766             lat = attrs.lat && attrs.lat.value;
27767         return [parseFloat(lon), parseFloat(lat)];
27768     }
27769
27770     function getNodes(obj) {
27771         var elems = obj.getElementsByTagName(ndStr),
27772             nodes = new Array(elems.length);
27773         for (var i = 0, l = elems.length; i < l; i++) {
27774             nodes[i] = 'n' + elems[i].attributes.ref.value;
27775         }
27776         return nodes;
27777     }
27778
27779     function getTags(obj) {
27780         var elems = obj.getElementsByTagName(tagStr),
27781             tags = {};
27782         for (var i = 0, l = elems.length; i < l; i++) {
27783             var attrs = elems[i].attributes;
27784             tags[attrs.k.value] = attrs.v.value;
27785         }
27786         return tags;
27787     }
27788
27789     function getMembers(obj) {
27790         var elems = obj.getElementsByTagName(memberStr),
27791             members = new Array(elems.length);
27792         for (var i = 0, l = elems.length; i < l; i++) {
27793             var attrs = elems[i].attributes;
27794             members[i] = {
27795                 id: attrs.type.value[0] + attrs.ref.value,
27796                 type: attrs.type.value,
27797                 role: attrs.role.value
27798             };
27799         }
27800         return members;
27801     }
27802
27803     function getVisible(attrs) {
27804         return (!attrs.visible || attrs.visible.value !== 'false');
27805     }
27806
27807     var parsers = {
27808         node: function nodeData(obj) {
27809             var attrs = obj.attributes;
27810             return new iD.Node({
27811                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
27812                 loc: getLoc(attrs),
27813                 version: attrs.version.value,
27814                 user: attrs.user && attrs.user.value,
27815                 tags: getTags(obj),
27816                 visible: getVisible(attrs)
27817             });
27818         },
27819
27820         way: function wayData(obj) {
27821             var attrs = obj.attributes;
27822             return new iD.Way({
27823                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
27824                 version: attrs.version.value,
27825                 user: attrs.user && attrs.user.value,
27826                 tags: getTags(obj),
27827                 nodes: getNodes(obj),
27828                 visible: getVisible(attrs)
27829             });
27830         },
27831
27832         relation: function relationData(obj) {
27833             var attrs = obj.attributes;
27834             return new iD.Relation({
27835                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
27836                 version: attrs.version.value,
27837                 user: attrs.user && attrs.user.value,
27838                 tags: getTags(obj),
27839                 members: getMembers(obj),
27840                 visible: getVisible(attrs)
27841             });
27842         }
27843     };
27844
27845     function parse(dom) {
27846         if (!dom || !dom.childNodes) return;
27847
27848         var root = dom.childNodes[0],
27849             children = root.childNodes,
27850             entities = [];
27851
27852         for (var i = 0, l = children.length; i < l; i++) {
27853             var child = children[i],
27854                 parser = parsers[child.nodeName];
27855             if (parser) {
27856                 entities.push(parser(child));
27857             }
27858         }
27859
27860         return entities;
27861     }
27862
27863     connection.authenticated = function() {
27864         return oauth.authenticated();
27865     };
27866
27867     // Generate Changeset XML. Returns a string.
27868     connection.changesetJXON = function(tags) {
27869         return {
27870             osm: {
27871                 changeset: {
27872                     tag: _.map(tags, function(value, key) {
27873                         return { '@k': key, '@v': value };
27874                     }),
27875                     '@version': 0.6,
27876                     '@generator': 'iD'
27877                 }
27878             }
27879         };
27880     };
27881
27882     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
27883     // XML. Returns a string.
27884     connection.osmChangeJXON = function(changeset_id, changes) {
27885         function nest(x, order) {
27886             var groups = {};
27887             for (var i = 0; i < x.length; i++) {
27888                 var tagName = Object.keys(x[i])[0];
27889                 if (!groups[tagName]) groups[tagName] = [];
27890                 groups[tagName].push(x[i][tagName]);
27891             }
27892             var ordered = {};
27893             order.forEach(function(o) {
27894                 if (groups[o]) ordered[o] = groups[o];
27895             });
27896             return ordered;
27897         }
27898
27899         function rep(entity) {
27900             return entity.asJXON(changeset_id);
27901         }
27902
27903         return {
27904             osmChange: {
27905                 '@version': 0.6,
27906                 '@generator': 'iD',
27907                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
27908                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
27909                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
27910             }
27911         };
27912     };
27913
27914     connection.changesetTags = function(comment, imageryUsed) {
27915         var detected = iD.detect(),
27916             tags = {
27917                 created_by: 'iD ' + iD.version,
27918                 imagery_used: imageryUsed.join(';').substr(0, 255),
27919                 host: (window.location.origin + window.location.pathname).substr(0, 255),
27920                 locale: detected.locale
27921             };
27922
27923         if (comment) {
27924             tags.comment = comment.substr(0, 255);
27925         }
27926
27927         return tags;
27928     };
27929
27930     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
27931         oauth.xhr({
27932                 method: 'PUT',
27933                 path: '/api/0.6/changeset/create',
27934                 options: { header: { 'Content-Type': 'text/xml' } },
27935                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
27936             }, function(err, changeset_id) {
27937                 if (err) return callback(err);
27938                 oauth.xhr({
27939                     method: 'POST',
27940                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
27941                     options: { header: { 'Content-Type': 'text/xml' } },
27942                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
27943                 }, function(err) {
27944                     if (err) return callback(err);
27945                     // POST was successful, safe to call the callback.
27946                     // Still attempt to close changeset, but ignore response because #2667
27947                     // Add delay to allow for postgres replication #1646 #2678
27948                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
27949                     oauth.xhr({
27950                         method: 'PUT',
27951                         path: '/api/0.6/changeset/' + changeset_id + '/close',
27952                         options: { header: { 'Content-Type': 'text/xml' } }
27953                     }, d3.functor(true));
27954                 });
27955             });
27956     };
27957
27958     connection.userDetails = function(callback) {
27959         if (userDetails) {
27960             callback(undefined, userDetails);
27961             return;
27962         }
27963
27964         function done(err, user_details) {
27965             if (err) return callback(err);
27966
27967             var u = user_details.getElementsByTagName('user')[0],
27968                 img = u.getElementsByTagName('img'),
27969                 image_url = '';
27970
27971             if (img && img[0] && img[0].getAttribute('href')) {
27972                 image_url = img[0].getAttribute('href');
27973             }
27974
27975             userDetails = {
27976                 display_name: u.attributes.display_name.value,
27977                 image_url: image_url,
27978                 id: u.attributes.id.value
27979             };
27980
27981             callback(undefined, userDetails);
27982         }
27983
27984         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
27985     };
27986
27987     connection.userChangesets = function(callback) {
27988         connection.userDetails(function(err, user) {
27989             if (err) return callback(err);
27990
27991             function done(changesets) {
27992                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
27993                     function (changeset) {
27994                         return { tags: getTags(changeset) };
27995                     }));
27996             }
27997
27998             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
27999                 .on('load', done)
28000                 .on('error', callback);
28001         });
28002     };
28003
28004     connection.status = function(callback) {
28005         function done(capabilities) {
28006             var apiStatus = capabilities.getElementsByTagName('status');
28007             callback(undefined, apiStatus[0].getAttribute('api'));
28008         }
28009         d3.xml(url + '/api/capabilities').get()
28010             .on('load', done)
28011             .on('error', callback);
28012     };
28013
28014     function abortRequest(i) { i.abort(); }
28015
28016     connection.tileZoom = function(_) {
28017         if (!arguments.length) return tileZoom;
28018         tileZoom = _;
28019         return connection;
28020     };
28021
28022     connection.loadTiles = function(projection, dimensions, callback) {
28023
28024         if (off) return;
28025
28026         var s = projection.scale() * 2 * Math.PI,
28027             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
28028             ts = 256 * Math.pow(2, z - tileZoom),
28029             origin = [
28030                 s / 2 - projection.translate()[0],
28031                 s / 2 - projection.translate()[1]];
28032
28033         var tiles = d3.geo.tile()
28034             .scaleExtent([tileZoom, tileZoom])
28035             .scale(s)
28036             .size(dimensions)
28037             .translate(projection.translate())()
28038             .map(function(tile) {
28039                 var x = tile[0] * ts - origin[0],
28040                     y = tile[1] * ts - origin[1];
28041
28042                 return {
28043                     id: tile.toString(),
28044                     extent: iD.geo.Extent(
28045                         projection.invert([x, y + ts]),
28046                         projection.invert([x + ts, y]))
28047                 };
28048             });
28049
28050         function bboxUrl(tile) {
28051             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
28052         }
28053
28054         _.filter(inflight, function(v, i) {
28055             var wanted = _.find(tiles, function(tile) {
28056                 return i === tile.id;
28057             });
28058             if (!wanted) delete inflight[i];
28059             return !wanted;
28060         }).map(abortRequest);
28061
28062         tiles.forEach(function(tile) {
28063             var id = tile.id;
28064
28065             if (loadedTiles[id] || inflight[id]) return;
28066
28067             if (_.isEmpty(inflight)) {
28068                 event.loading();
28069             }
28070
28071             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
28072                 loadedTiles[id] = true;
28073                 delete inflight[id];
28074
28075                 if (callback) callback(err, _.extend({data: parsed}, tile));
28076
28077                 if (_.isEmpty(inflight)) {
28078                     event.loaded();
28079                 }
28080             });
28081         });
28082     };
28083
28084     connection.switch = function(options) {
28085         url = options.url;
28086         oauth.options(_.extend({
28087             loading: authenticating,
28088             done: authenticated
28089         }, options));
28090         event.auth();
28091         connection.flush();
28092         return connection;
28093     };
28094
28095     connection.toggle = function(_) {
28096         off = !_;
28097         return connection;
28098     };
28099
28100     connection.flush = function() {
28101         userDetails = undefined;
28102         _.forEach(inflight, abortRequest);
28103         loadedTiles = {};
28104         inflight = {};
28105         return connection;
28106     };
28107
28108     connection.loadedTiles = function(_) {
28109         if (!arguments.length) return loadedTiles;
28110         loadedTiles = _;
28111         return connection;
28112     };
28113
28114     connection.logout = function() {
28115         userDetails = undefined;
28116         oauth.logout();
28117         event.auth();
28118         return connection;
28119     };
28120
28121     connection.authenticate = function(callback) {
28122         userDetails = undefined;
28123         function done(err, res) {
28124             event.auth();
28125             if (callback) callback(err, res);
28126         }
28127         return oauth.authenticate(done);
28128     };
28129
28130     return d3.rebind(connection, event, 'on');
28131 };
28132 /*
28133     iD.Difference represents the difference between two graphs.
28134     It knows how to calculate the set of entities that were
28135     created, modified, or deleted, and also contains the logic
28136     for recursively extending a difference to the complete set
28137     of entities that will require a redraw, taking into account
28138     child and parent relationships.
28139  */
28140 iD.Difference = function(base, head) {
28141     var changes = {}, length = 0;
28142
28143     function changed(h, b) {
28144         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
28145     }
28146
28147     _.each(head.entities, function(h, id) {
28148         var b = base.entities[id];
28149         if (changed(h, b)) {
28150             changes[id] = {base: b, head: h};
28151             length++;
28152         }
28153     });
28154
28155     _.each(base.entities, function(b, id) {
28156         var h = head.entities[id];
28157         if (!changes[id] && changed(h, b)) {
28158             changes[id] = {base: b, head: h};
28159             length++;
28160         }
28161     });
28162
28163     function addParents(parents, result) {
28164         for (var i = 0; i < parents.length; i++) {
28165             var parent = parents[i];
28166
28167             if (parent.id in result)
28168                 continue;
28169
28170             result[parent.id] = parent;
28171             addParents(head.parentRelations(parent), result);
28172         }
28173     }
28174
28175     var difference = {};
28176
28177     difference.length = function() {
28178         return length;
28179     };
28180
28181     difference.changes = function() {
28182         return changes;
28183     };
28184
28185     difference.extantIDs = function() {
28186         var result = [];
28187         _.each(changes, function(change, id) {
28188             if (change.head) result.push(id);
28189         });
28190         return result;
28191     };
28192
28193     difference.modified = function() {
28194         var result = [];
28195         _.each(changes, function(change) {
28196             if (change.base && change.head) result.push(change.head);
28197         });
28198         return result;
28199     };
28200
28201     difference.created = function() {
28202         var result = [];
28203         _.each(changes, function(change) {
28204             if (!change.base && change.head) result.push(change.head);
28205         });
28206         return result;
28207     };
28208
28209     difference.deleted = function() {
28210         var result = [];
28211         _.each(changes, function(change) {
28212             if (change.base && !change.head) result.push(change.base);
28213         });
28214         return result;
28215     };
28216
28217     difference.summary = function() {
28218         var relevant = {};
28219
28220         function addEntity(entity, graph, changeType) {
28221             relevant[entity.id] = {
28222                 entity: entity,
28223                 graph: graph,
28224                 changeType: changeType
28225             };
28226         }
28227
28228         function addParents(entity) {
28229             var parents = head.parentWays(entity);
28230             for (var j = parents.length - 1; j >= 0; j--) {
28231                 var parent = parents[j];
28232                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
28233             }
28234         }
28235
28236         _.each(changes, function(change) {
28237             if (change.head && change.head.geometry(head) !== 'vertex') {
28238                 addEntity(change.head, head, change.base ? 'modified' : 'created');
28239
28240             } else if (change.base && change.base.geometry(base) !== 'vertex') {
28241                 addEntity(change.base, base, 'deleted');
28242
28243             } else if (change.base && change.head) { // modified vertex
28244                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
28245                     retagged = !_.isEqual(change.base.tags, change.head.tags);
28246
28247                 if (moved) {
28248                     addParents(change.head);
28249                 }
28250
28251                 if (retagged || (moved && change.head.hasInterestingTags())) {
28252                     addEntity(change.head, head, 'modified');
28253                 }
28254
28255             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
28256                 addEntity(change.head, head, 'created');
28257
28258             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
28259                 addEntity(change.base, base, 'deleted');
28260             }
28261         });
28262
28263         return d3.values(relevant);
28264     };
28265
28266     difference.complete = function(extent) {
28267         var result = {}, id, change;
28268
28269         for (id in changes) {
28270             change = changes[id];
28271
28272             var h = change.head,
28273                 b = change.base,
28274                 entity = h || b;
28275
28276             if (extent &&
28277                 (!h || !h.intersects(extent, head)) &&
28278                 (!b || !b.intersects(extent, base)))
28279                 continue;
28280
28281             result[id] = h;
28282
28283             if (entity.type === 'way') {
28284                 var nh = h ? h.nodes : [],
28285                     nb = b ? b.nodes : [],
28286                     diff, i;
28287
28288                 diff = _.difference(nh, nb);
28289                 for (i = 0; i < diff.length; i++) {
28290                     result[diff[i]] = head.hasEntity(diff[i]);
28291                 }
28292
28293                 diff = _.difference(nb, nh);
28294                 for (i = 0; i < diff.length; i++) {
28295                     result[diff[i]] = head.hasEntity(diff[i]);
28296                 }
28297             }
28298
28299             addParents(head.parentWays(entity), result);
28300             addParents(head.parentRelations(entity), result);
28301         }
28302
28303         return result;
28304     };
28305
28306     return difference;
28307 };
28308 iD.Entity = function(attrs) {
28309     // For prototypal inheritance.
28310     if (this instanceof iD.Entity) return;
28311
28312     // Create the appropriate subtype.
28313     if (attrs && attrs.type) {
28314         return iD.Entity[attrs.type].apply(this, arguments);
28315     } else if (attrs && attrs.id) {
28316         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
28317     }
28318
28319     // Initialize a generic Entity (used only in tests).
28320     return (new iD.Entity()).initialize(arguments);
28321 };
28322
28323 iD.Entity.id = function(type) {
28324     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
28325 };
28326
28327 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
28328
28329 iD.Entity.id.fromOSM = function(type, id) {
28330     return type[0] + id;
28331 };
28332
28333 iD.Entity.id.toOSM = function(id) {
28334     return id.slice(1);
28335 };
28336
28337 iD.Entity.id.type = function(id) {
28338     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
28339 };
28340
28341 // A function suitable for use as the second argument to d3.selection#data().
28342 iD.Entity.key = function(entity) {
28343     return entity.id + 'v' + (entity.v || 0);
28344 };
28345
28346 iD.Entity.prototype = {
28347     tags: {},
28348
28349     initialize: function(sources) {
28350         for (var i = 0; i < sources.length; ++i) {
28351             var source = sources[i];
28352             for (var prop in source) {
28353                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
28354                     if (source[prop] === undefined) {
28355                         delete this[prop];
28356                     } else {
28357                         this[prop] = source[prop];
28358                     }
28359                 }
28360             }
28361         }
28362
28363         if (!this.id && this.type) {
28364             this.id = iD.Entity.id(this.type);
28365         }
28366         if (!this.hasOwnProperty('visible')) {
28367             this.visible = true;
28368         }
28369
28370         if (iD.debug) {
28371             Object.freeze(this);
28372             Object.freeze(this.tags);
28373
28374             if (this.loc) Object.freeze(this.loc);
28375             if (this.nodes) Object.freeze(this.nodes);
28376             if (this.members) Object.freeze(this.members);
28377         }
28378
28379         return this;
28380     },
28381
28382     copy: function(resolver, copies) {
28383         if (copies[this.id])
28384             return copies[this.id];
28385
28386         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
28387         copies[this.id] = copy;
28388
28389         return copy;
28390     },
28391
28392     osmId: function() {
28393         return iD.Entity.id.toOSM(this.id);
28394     },
28395
28396     isNew: function() {
28397         return this.osmId() < 0;
28398     },
28399
28400     update: function(attrs) {
28401         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
28402     },
28403
28404     mergeTags: function(tags) {
28405         var merged = _.clone(this.tags), changed = false;
28406         for (var k in tags) {
28407             var t1 = merged[k],
28408                 t2 = tags[k];
28409             if (!t1) {
28410                 changed = true;
28411                 merged[k] = t2;
28412             } else if (t1 !== t2) {
28413                 changed = true;
28414                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
28415             }
28416         }
28417         return changed ? this.update({tags: merged}) : this;
28418     },
28419
28420     intersects: function(extent, resolver) {
28421         return this.extent(resolver).intersects(extent);
28422     },
28423
28424     isUsed: function(resolver) {
28425         return _.without(Object.keys(this.tags), 'area').length > 0 ||
28426             resolver.parentRelations(this).length > 0;
28427     },
28428
28429     hasInterestingTags: function() {
28430         return _.keys(this.tags).some(iD.interestingTag);
28431     },
28432
28433     isHighwayIntersection: function() {
28434         return false;
28435     },
28436
28437     deprecatedTags: function() {
28438         var tags = _.toPairs(this.tags);
28439         var deprecated = {};
28440
28441         iD.data.deprecated.forEach(function(d) {
28442             var match = _.toPairs(d.old)[0];
28443             tags.forEach(function(t) {
28444                 if (t[0] === match[0] &&
28445                     (t[1] === match[1] || match[1] === '*')) {
28446                     deprecated[t[0]] = t[1];
28447                 }
28448             });
28449         });
28450
28451         return deprecated;
28452     }
28453 };
28454 iD.Graph = function(other, mutable) {
28455     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
28456
28457     if (other instanceof iD.Graph) {
28458         var base = other.base();
28459         this.entities = _.assign(Object.create(base.entities), other.entities);
28460         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
28461         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
28462
28463     } else {
28464         this.entities = Object.create({});
28465         this._parentWays = Object.create({});
28466         this._parentRels = Object.create({});
28467         this.rebase(other || [], [this]);
28468     }
28469
28470     this.transients = {};
28471     this._childNodes = {};
28472     this.frozen = !mutable;
28473 };
28474
28475 iD.Graph.prototype = {
28476     hasEntity: function(id) {
28477         return this.entities[id];
28478     },
28479
28480     entity: function(id) {
28481         var entity = this.entities[id];
28482         if (!entity) {
28483             throw new Error('entity ' + id + ' not found');
28484         }
28485         return entity;
28486     },
28487
28488     transient: function(entity, key, fn) {
28489         var id = entity.id,
28490             transients = this.transients[id] ||
28491             (this.transients[id] = {});
28492
28493         if (transients[key] !== undefined) {
28494             return transients[key];
28495         }
28496
28497         transients[key] = fn.call(entity);
28498
28499         return transients[key];
28500     },
28501
28502     parentWays: function(entity) {
28503         var parents = this._parentWays[entity.id],
28504             result = [];
28505
28506         if (parents) {
28507             for (var i = 0; i < parents.length; i++) {
28508                 result.push(this.entity(parents[i]));
28509             }
28510         }
28511         return result;
28512     },
28513
28514     isPoi: function(entity) {
28515         var parentWays = this._parentWays[entity.id];
28516         return !parentWays || parentWays.length === 0;
28517     },
28518
28519     isShared: function(entity) {
28520         var parentWays = this._parentWays[entity.id];
28521         return parentWays && parentWays.length > 1;
28522     },
28523
28524     parentRelations: function(entity) {
28525         var parents = this._parentRels[entity.id],
28526             result = [];
28527
28528         if (parents) {
28529             for (var i = 0; i < parents.length; i++) {
28530                 result.push(this.entity(parents[i]));
28531             }
28532         }
28533         return result;
28534     },
28535
28536     childNodes: function(entity) {
28537         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
28538         if (!entity.nodes) return [];
28539
28540         var nodes = [];
28541         for (var i = 0; i < entity.nodes.length; i++) {
28542             nodes[i] = this.entity(entity.nodes[i]);
28543         }
28544
28545         if (iD.debug) Object.freeze(nodes);
28546
28547         this._childNodes[entity.id] = nodes;
28548         return this._childNodes[entity.id];
28549     },
28550
28551     base: function() {
28552         return {
28553             'entities': iD.util.getPrototypeOf(this.entities),
28554             'parentWays': iD.util.getPrototypeOf(this._parentWays),
28555             'parentRels': iD.util.getPrototypeOf(this._parentRels)
28556         };
28557     },
28558
28559     // Unlike other graph methods, rebase mutates in place. This is because it
28560     // is used only during the history operation that merges newly downloaded
28561     // data into each state. To external consumers, it should appear as if the
28562     // graph always contained the newly downloaded data.
28563     rebase: function(entities, stack, force) {
28564         var base = this.base(),
28565             i, j, k, id;
28566
28567         for (i = 0; i < entities.length; i++) {
28568             var entity = entities[i];
28569
28570             if (!entity.visible || (!force && base.entities[entity.id]))
28571                 continue;
28572
28573             // Merging data into the base graph
28574             base.entities[entity.id] = entity;
28575             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
28576
28577             // Restore provisionally-deleted nodes that are discovered to have an extant parent
28578             if (entity.type === 'way') {
28579                 for (j = 0; j < entity.nodes.length; j++) {
28580                     id = entity.nodes[j];
28581                     for (k = 1; k < stack.length; k++) {
28582                         var ents = stack[k].entities;
28583                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
28584                             delete ents[id];
28585                         }
28586                     }
28587                 }
28588             }
28589         }
28590
28591         for (i = 0; i < stack.length; i++) {
28592             stack[i]._updateRebased();
28593         }
28594     },
28595
28596     _updateRebased: function() {
28597         var base = this.base(),
28598             i, k, child, id, keys;
28599
28600         keys = Object.keys(this._parentWays);
28601         for (i = 0; i < keys.length; i++) {
28602             child = keys[i];
28603             if (base.parentWays[child]) {
28604                 for (k = 0; k < base.parentWays[child].length; k++) {
28605                     id = base.parentWays[child][k];
28606                     if (!this.entities.hasOwnProperty(id) && !_.includes(this._parentWays[child], id)) {
28607                         this._parentWays[child].push(id);
28608                     }
28609                 }
28610             }
28611         }
28612
28613         keys = Object.keys(this._parentRels);
28614         for (i = 0; i < keys.length; i++) {
28615             child = keys[i];
28616             if (base.parentRels[child]) {
28617                 for (k = 0; k < base.parentRels[child].length; k++) {
28618                     id = base.parentRels[child][k];
28619                     if (!this.entities.hasOwnProperty(id) && !_.includes(this._parentRels[child], id)) {
28620                         this._parentRels[child].push(id);
28621                     }
28622                 }
28623             }
28624         }
28625
28626         this.transients = {};
28627
28628         // this._childNodes is not updated, under the assumption that
28629         // ways are always downloaded with their child nodes.
28630     },
28631
28632     // Updates calculated properties (parentWays, parentRels) for the specified change
28633     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
28634
28635         parentWays = parentWays || this._parentWays;
28636         parentRels = parentRels || this._parentRels;
28637
28638         var type = entity && entity.type || oldentity && oldentity.type,
28639             removed, added, ways, rels, i;
28640
28641
28642         if (type === 'way') {
28643
28644             // Update parentWays
28645             if (oldentity && entity) {
28646                 removed = _.difference(oldentity.nodes, entity.nodes);
28647                 added = _.difference(entity.nodes, oldentity.nodes);
28648             } else if (oldentity) {
28649                 removed = oldentity.nodes;
28650                 added = [];
28651             } else if (entity) {
28652                 removed = [];
28653                 added = entity.nodes;
28654             }
28655             for (i = 0; i < removed.length; i++) {
28656                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
28657             }
28658             for (i = 0; i < added.length; i++) {
28659                 ways = _.without(parentWays[added[i]], entity.id);
28660                 ways.push(entity.id);
28661                 parentWays[added[i]] = ways;
28662             }
28663
28664         } else if (type === 'relation') {
28665
28666             // Update parentRels
28667             if (oldentity && entity) {
28668                 removed = _.difference(oldentity.members, entity.members);
28669                 added = _.difference(entity.members, oldentity);
28670             } else if (oldentity) {
28671                 removed = oldentity.members;
28672                 added = [];
28673             } else if (entity) {
28674                 removed = [];
28675                 added = entity.members;
28676             }
28677             for (i = 0; i < removed.length; i++) {
28678                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
28679             }
28680             for (i = 0; i < added.length; i++) {
28681                 rels = _.without(parentRels[added[i].id], entity.id);
28682                 rels.push(entity.id);
28683                 parentRels[added[i].id] = rels;
28684             }
28685         }
28686     },
28687
28688     replace: function(entity) {
28689         if (this.entities[entity.id] === entity)
28690             return this;
28691
28692         return this.update(function() {
28693             this._updateCalculated(this.entities[entity.id], entity);
28694             this.entities[entity.id] = entity;
28695         });
28696     },
28697
28698     remove: function(entity) {
28699         return this.update(function() {
28700             this._updateCalculated(entity, undefined);
28701             this.entities[entity.id] = undefined;
28702         });
28703     },
28704
28705     revert: function(id) {
28706         var baseEntity = this.base().entities[id],
28707             headEntity = this.entities[id];
28708
28709         if (headEntity === baseEntity)
28710             return this;
28711
28712         return this.update(function() {
28713             this._updateCalculated(headEntity, baseEntity);
28714             delete this.entities[id];
28715         });
28716     },
28717
28718     update: function() {
28719         var graph = this.frozen ? iD.Graph(this, true) : this;
28720
28721         for (var i = 0; i < arguments.length; i++) {
28722             arguments[i].call(graph, graph);
28723         }
28724
28725         if (this.frozen) graph.frozen = true;
28726
28727         return graph;
28728     },
28729
28730     // Obliterates any existing entities
28731     load: function(entities) {
28732         var base = this.base();
28733         this.entities = Object.create(base.entities);
28734
28735         for (var i in entities) {
28736             this.entities[i] = entities[i];
28737             this._updateCalculated(base.entities[i], this.entities[i]);
28738         }
28739
28740         return this;
28741     }
28742 };
28743 iD.History = function(context) {
28744     var stack, index, tree,
28745         imageryUsed = ['Bing'],
28746         dispatch = d3.dispatch('change', 'undone', 'redone'),
28747         lock = iD.util.SessionMutex('lock');
28748
28749     function perform(actions) {
28750         actions = Array.prototype.slice.call(actions);
28751
28752         var annotation;
28753
28754         if (!_.isFunction(_.last(actions))) {
28755             annotation = actions.pop();
28756         }
28757
28758         var graph = stack[index].graph;
28759         for (var i = 0; i < actions.length; i++) {
28760             graph = actions[i](graph);
28761         }
28762
28763         return {
28764             graph: graph,
28765             annotation: annotation,
28766             imageryUsed: imageryUsed
28767         };
28768     }
28769
28770     function change(previous) {
28771         var difference = iD.Difference(previous, history.graph());
28772         dispatch.change(difference);
28773         return difference;
28774     }
28775
28776     // iD uses namespaced keys so multiple installations do not conflict
28777     function getKey(n) {
28778         return 'iD_' + window.location.origin + '_' + n;
28779     }
28780
28781     var history = {
28782         graph: function() {
28783             return stack[index].graph;
28784         },
28785
28786         base: function() {
28787             return stack[0].graph;
28788         },
28789
28790         merge: function(entities, extent) {
28791             stack[0].graph.rebase(entities, _.map(stack, 'graph'), false);
28792             tree.rebase(entities, false);
28793
28794             dispatch.change(undefined, extent);
28795         },
28796
28797         perform: function() {
28798             var previous = stack[index].graph;
28799
28800             stack = stack.slice(0, index + 1);
28801             stack.push(perform(arguments));
28802             index++;
28803
28804             return change(previous);
28805         },
28806
28807         replace: function() {
28808             var previous = stack[index].graph;
28809
28810             // assert(index == stack.length - 1)
28811             stack[index] = perform(arguments);
28812
28813             return change(previous);
28814         },
28815
28816         pop: function() {
28817             var previous = stack[index].graph;
28818
28819             if (index > 0) {
28820                 index--;
28821                 stack.pop();
28822                 return change(previous);
28823             }
28824         },
28825
28826         // Same as calling pop and then perform
28827         overwrite: function() {
28828             var previous = stack[index].graph;
28829
28830             if (index > 0) {
28831                 index--;
28832                 stack.pop();
28833             }
28834             stack = stack.slice(0, index + 1);
28835             stack.push(perform(arguments));
28836             index++;
28837
28838             return change(previous);
28839         },
28840
28841         undo: function() {
28842             var previous = stack[index].graph;
28843
28844             // Pop to the next annotated state.
28845             while (index > 0) {
28846                 index--;
28847                 if (stack[index].annotation) break;
28848             }
28849
28850             dispatch.undone();
28851             return change(previous);
28852         },
28853
28854         redo: function() {
28855             var previous = stack[index].graph;
28856
28857             while (index < stack.length - 1) {
28858                 index++;
28859                 if (stack[index].annotation) break;
28860             }
28861
28862             dispatch.redone();
28863             return change(previous);
28864         },
28865
28866         undoAnnotation: function() {
28867             var i = index;
28868             while (i >= 0) {
28869                 if (stack[i].annotation) return stack[i].annotation;
28870                 i--;
28871             }
28872         },
28873
28874         redoAnnotation: function() {
28875             var i = index + 1;
28876             while (i <= stack.length - 1) {
28877                 if (stack[i].annotation) return stack[i].annotation;
28878                 i++;
28879             }
28880         },
28881
28882         intersects: function(extent) {
28883             return tree.intersects(extent, stack[index].graph);
28884         },
28885
28886         difference: function() {
28887             var base = stack[0].graph,
28888                 head = stack[index].graph;
28889             return iD.Difference(base, head);
28890         },
28891
28892         changes: function(action) {
28893             var base = stack[0].graph,
28894                 head = stack[index].graph;
28895
28896             if (action) {
28897                 head = action(head);
28898             }
28899
28900             var difference = iD.Difference(base, head);
28901
28902             return {
28903                 modified: difference.modified(),
28904                 created: difference.created(),
28905                 deleted: difference.deleted()
28906             };
28907         },
28908
28909         validate: function(changes) {
28910             return _(iD.validations)
28911                 .map(function(fn) { return fn()(changes, stack[index].graph); })
28912                 .flatten()
28913                 .value();
28914         },
28915
28916         hasChanges: function() {
28917             return this.difference().length() > 0;
28918         },
28919
28920         imageryUsed: function(sources) {
28921             if (sources) {
28922                 imageryUsed = sources;
28923                 return history;
28924             } else {
28925                 return _(stack.slice(1, index + 1))
28926                     .map('imageryUsed')
28927                     .flatten()
28928                     .uniq()
28929                     .without(undefined, 'Custom')
28930                     .value();
28931             }
28932         },
28933
28934         reset: function() {
28935             stack = [{graph: iD.Graph()}];
28936             index = 0;
28937             tree = iD.Tree(stack[0].graph);
28938             dispatch.change();
28939             return history;
28940         },
28941
28942         toJSON: function() {
28943             if (!this.hasChanges()) return;
28944
28945             var allEntities = {},
28946                 baseEntities = {},
28947                 base = stack[0];
28948
28949             var s = stack.map(function(i) {
28950                 var modified = [], deleted = [];
28951
28952                 _.forEach(i.graph.entities, function(entity, id) {
28953                     if (entity) {
28954                         var key = iD.Entity.key(entity);
28955                         allEntities[key] = entity;
28956                         modified.push(key);
28957                     } else {
28958                         deleted.push(id);
28959                     }
28960
28961                     // make sure that the originals of changed or deleted entities get merged
28962                     // into the base of the stack after restoring the data from JSON.
28963                     if (id in base.graph.entities) {
28964                         baseEntities[id] = base.graph.entities[id];
28965                     }
28966                     // get originals of parent entities too
28967                     _.forEach(base.graph._parentWays[id], function(parentId) {
28968                         if (parentId in base.graph.entities) {
28969                             baseEntities[parentId] = base.graph.entities[parentId];
28970                         }
28971                     });
28972                 });
28973
28974                 var x = {};
28975
28976                 if (modified.length) x.modified = modified;
28977                 if (deleted.length) x.deleted = deleted;
28978                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
28979                 if (i.annotation) x.annotation = i.annotation;
28980
28981                 return x;
28982             });
28983
28984             return JSON.stringify({
28985                 version: 3,
28986                 entities: _.values(allEntities),
28987                 baseEntities: _.values(baseEntities),
28988                 stack: s,
28989                 nextIDs: iD.Entity.id.next,
28990                 index: index
28991             });
28992         },
28993
28994         fromJSON: function(json, loadChildNodes) {
28995             var h = JSON.parse(json),
28996                 loadComplete = true;
28997
28998             iD.Entity.id.next = h.nextIDs;
28999             index = h.index;
29000
29001             if (h.version === 2 || h.version === 3) {
29002                 var allEntities = {};
29003
29004                 h.entities.forEach(function(entity) {
29005                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
29006                 });
29007
29008                 if (h.version === 3) {
29009                     // This merges originals for changed entities into the base of
29010                     // the stack even if the current stack doesn't have them (for
29011                     // example when iD has been restarted in a different region)
29012                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
29013                     stack[0].graph.rebase(baseEntities, _.map(stack, 'graph'), true);
29014                     tree.rebase(baseEntities, true);
29015
29016                     // When we restore a modified way, we also need to fetch any missing
29017                     // childnodes that would normally have been downloaded with it.. #2142
29018                     if (loadChildNodes) {
29019                         var missing =  _(baseEntities)
29020                                 .filter({ type: 'way' })
29021                                 .map('nodes')
29022                                 .flatten()
29023                                 .uniq()
29024                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
29025                                 .value();
29026
29027                         if (!_.isEmpty(missing)) {
29028                             loadComplete = false;
29029                             context.redrawEnable(false);
29030
29031                             var loading = iD.ui.Loading(context).blocking(true);
29032                             context.container().call(loading);
29033
29034                             var childNodesLoaded = function(err, result) {
29035                                 if (!err) {
29036                                     var visible = _.groupBy(result.data, 'visible');
29037                                     if (!_.isEmpty(visible.true)) {
29038                                         missing = _.difference(missing, _.map(visible.true, 'id'));
29039                                         stack[0].graph.rebase(visible.true, _.map(stack, 'graph'), true);
29040                                         tree.rebase(visible.true, true);
29041                                     }
29042
29043                                     // fetch older versions of nodes that were deleted..
29044                                     _.each(visible.false, function(entity) {
29045                                         context.connection()
29046                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
29047                                     });
29048                                 }
29049
29050                                 if (err || _.isEmpty(missing)) {
29051                                     loading.close();
29052                                     context.redrawEnable(true);
29053                                     dispatch.change();
29054                                 }
29055                             };
29056
29057                             context.connection().loadMultiple(missing, childNodesLoaded);
29058                         }
29059                     }
29060                 }
29061
29062                 stack = h.stack.map(function(d) {
29063                     var entities = {}, entity;
29064
29065                     if (d.modified) {
29066                         d.modified.forEach(function(key) {
29067                             entity = allEntities[key];
29068                             entities[entity.id] = entity;
29069                         });
29070                     }
29071
29072                     if (d.deleted) {
29073                         d.deleted.forEach(function(id) {
29074                             entities[id] = undefined;
29075                         });
29076                     }
29077
29078                     return {
29079                         graph: iD.Graph(stack[0].graph).load(entities),
29080                         annotation: d.annotation,
29081                         imageryUsed: d.imageryUsed
29082                     };
29083                 });
29084
29085             } else { // original version
29086                 stack = h.stack.map(function(d) {
29087                     var entities = {};
29088
29089                     for (var i in d.entities) {
29090                         var entity = d.entities[i];
29091                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
29092                     }
29093
29094                     d.graph = iD.Graph(stack[0].graph).load(entities);
29095                     return d;
29096                 });
29097             }
29098
29099             if (loadComplete) {
29100                 dispatch.change();
29101             }
29102
29103             return history;
29104         },
29105
29106         save: function() {
29107             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
29108             return history;
29109         },
29110
29111         clearSaved: function() {
29112             context.debouncedSave.cancel();
29113             if (lock.locked()) context.storage(getKey('saved_history'), null);
29114             return history;
29115         },
29116
29117         lock: function() {
29118             return lock.lock();
29119         },
29120
29121         unlock: function() {
29122             lock.unlock();
29123         },
29124
29125         // is iD not open in another window and it detects that
29126         // there's a history stored in localStorage that's recoverable?
29127         restorableChanges: function() {
29128             return lock.locked() && !!context.storage(getKey('saved_history'));
29129         },
29130
29131         // load history from a version stored in localStorage
29132         restore: function() {
29133             if (!lock.locked()) return;
29134
29135             var json = context.storage(getKey('saved_history'));
29136             if (json) history.fromJSON(json, true);
29137         },
29138
29139         _getKey: getKey
29140
29141     };
29142
29143     history.reset();
29144
29145     return d3.rebind(history, dispatch, 'on');
29146 };
29147 iD.Node = iD.Entity.node = function iD_Node() {
29148     if (!(this instanceof iD_Node)) {
29149         return (new iD_Node()).initialize(arguments);
29150     } else if (arguments.length) {
29151         this.initialize(arguments);
29152     }
29153 };
29154
29155 iD.Node.prototype = Object.create(iD.Entity.prototype);
29156
29157 _.extend(iD.Node.prototype, {
29158     type: 'node',
29159
29160     extent: function() {
29161         return new iD.geo.Extent(this.loc);
29162     },
29163
29164     geometry: function(graph) {
29165         return graph.transient(this, 'geometry', function() {
29166             return graph.isPoi(this) ? 'point' : 'vertex';
29167         });
29168     },
29169
29170     move: function(loc) {
29171         return this.update({loc: loc});
29172     },
29173
29174     isIntersection: function(resolver) {
29175         return resolver.transient(this, 'isIntersection', function() {
29176             return resolver.parentWays(this).filter(function(parent) {
29177                 return (parent.tags.highway ||
29178                     parent.tags.waterway ||
29179                     parent.tags.railway ||
29180                     parent.tags.aeroway) &&
29181                     parent.geometry(resolver) === 'line';
29182             }).length > 1;
29183         });
29184     },
29185
29186     isHighwayIntersection: function(resolver) {
29187         return resolver.transient(this, 'isHighwayIntersection', function() {
29188             return resolver.parentWays(this).filter(function(parent) {
29189                 return parent.tags.highway && parent.geometry(resolver) === 'line';
29190             }).length > 1;
29191         });
29192     },
29193
29194     asJXON: function(changeset_id) {
29195         var r = {
29196             node: {
29197                 '@id': this.osmId(),
29198                 '@lon': this.loc[0],
29199                 '@lat': this.loc[1],
29200                 '@version': (this.version || 0),
29201                 tag: _.map(this.tags, function(v, k) {
29202                     return { keyAttributes: { k: k, v: v } };
29203                 })
29204             }
29205         };
29206         if (changeset_id) r.node['@changeset'] = changeset_id;
29207         return r;
29208     },
29209
29210     asGeoJSON: function() {
29211         return {
29212             type: 'Point',
29213             coordinates: this.loc
29214         };
29215     }
29216 });
29217 iD.Relation = iD.Entity.relation = function iD_Relation() {
29218     if (!(this instanceof iD_Relation)) {
29219         return (new iD_Relation()).initialize(arguments);
29220     } else if (arguments.length) {
29221         this.initialize(arguments);
29222     }
29223 };
29224
29225 iD.Relation.prototype = Object.create(iD.Entity.prototype);
29226
29227 iD.Relation.creationOrder = function(a, b) {
29228     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
29229     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
29230
29231     if (aId < 0 || bId < 0) return aId - bId;
29232     return bId - aId;
29233 };
29234
29235 _.extend(iD.Relation.prototype, {
29236     type: 'relation',
29237     members: [],
29238
29239     copy: function(resolver, copies) {
29240         if (copies[this.id])
29241             return copies[this.id];
29242
29243         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
29244
29245         var members = this.members.map(function(member) {
29246             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
29247         });
29248
29249         copy = copy.update({members: members});
29250         copies[this.id] = copy;
29251
29252         return copy;
29253     },
29254
29255     extent: function(resolver, memo) {
29256         return resolver.transient(this, 'extent', function() {
29257             if (memo && memo[this.id]) return iD.geo.Extent();
29258             memo = memo || {};
29259             memo[this.id] = true;
29260
29261             var extent = iD.geo.Extent();
29262             for (var i = 0; i < this.members.length; i++) {
29263                 var member = resolver.hasEntity(this.members[i].id);
29264                 if (member) {
29265                     extent._extend(member.extent(resolver, memo));
29266                 }
29267             }
29268             return extent;
29269         });
29270     },
29271
29272     geometry: function(graph) {
29273         return graph.transient(this, 'geometry', function() {
29274             return this.isMultipolygon() ? 'area' : 'relation';
29275         });
29276     },
29277
29278     isDegenerate: function() {
29279         return this.members.length === 0;
29280     },
29281
29282     // Return an array of members, each extended with an 'index' property whose value
29283     // is the member index.
29284     indexedMembers: function() {
29285         var result = new Array(this.members.length);
29286         for (var i = 0; i < this.members.length; i++) {
29287             result[i] = _.extend({}, this.members[i], {index: i});
29288         }
29289         return result;
29290     },
29291
29292     // Return the first member with the given role. A copy of the member object
29293     // is returned, extended with an 'index' property whose value is the member index.
29294     memberByRole: function(role) {
29295         for (var i = 0; i < this.members.length; i++) {
29296             if (this.members[i].role === role) {
29297                 return _.extend({}, this.members[i], {index: i});
29298             }
29299         }
29300     },
29301
29302     // Return the first member with the given id. A copy of the member object
29303     // is returned, extended with an 'index' property whose value is the member index.
29304     memberById: function(id) {
29305         for (var i = 0; i < this.members.length; i++) {
29306             if (this.members[i].id === id) {
29307                 return _.extend({}, this.members[i], {index: i});
29308             }
29309         }
29310     },
29311
29312     // Return the first member with the given id and role. A copy of the member object
29313     // is returned, extended with an 'index' property whose value is the member index.
29314     memberByIdAndRole: function(id, role) {
29315         for (var i = 0; i < this.members.length; i++) {
29316             if (this.members[i].id === id && this.members[i].role === role) {
29317                 return _.extend({}, this.members[i], {index: i});
29318             }
29319         }
29320     },
29321
29322     addMember: function(member, index) {
29323         var members = this.members.slice();
29324         members.splice(index === undefined ? members.length : index, 0, member);
29325         return this.update({members: members});
29326     },
29327
29328     updateMember: function(member, index) {
29329         var members = this.members.slice();
29330         members.splice(index, 1, _.extend({}, members[index], member));
29331         return this.update({members: members});
29332     },
29333
29334     removeMember: function(index) {
29335         var members = this.members.slice();
29336         members.splice(index, 1);
29337         return this.update({members: members});
29338     },
29339
29340     removeMembersWithID: function(id) {
29341         var members = _.reject(this.members, function(m) { return m.id === id; });
29342         return this.update({members: members});
29343     },
29344
29345     // Wherever a member appears with id `needle.id`, replace it with a member
29346     // with id `replacement.id`, type `replacement.type`, and the original role,
29347     // unless a member already exists with that id and role. Return an updated
29348     // relation.
29349     replaceMember: function(needle, replacement) {
29350         if (!this.memberById(needle.id))
29351             return this;
29352
29353         var members = [];
29354
29355         for (var i = 0; i < this.members.length; i++) {
29356             var member = this.members[i];
29357             if (member.id !== needle.id) {
29358                 members.push(member);
29359             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
29360                 members.push({id: replacement.id, type: replacement.type, role: member.role});
29361             }
29362         }
29363
29364         return this.update({members: members});
29365     },
29366
29367     asJXON: function(changeset_id) {
29368         var r = {
29369             relation: {
29370                 '@id': this.osmId(),
29371                 '@version': this.version || 0,
29372                 member: _.map(this.members, function(member) {
29373                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
29374                 }),
29375                 tag: _.map(this.tags, function(v, k) {
29376                     return { keyAttributes: { k: k, v: v } };
29377                 })
29378             }
29379         };
29380         if (changeset_id) r.relation['@changeset'] = changeset_id;
29381         return r;
29382     },
29383
29384     asGeoJSON: function(resolver) {
29385         return resolver.transient(this, 'GeoJSON', function () {
29386             if (this.isMultipolygon()) {
29387                 return {
29388                     type: 'MultiPolygon',
29389                     coordinates: this.multipolygon(resolver)
29390                 };
29391             } else {
29392                 return {
29393                     type: 'FeatureCollection',
29394                     properties: this.tags,
29395                     features: this.members.map(function (member) {
29396                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
29397                     })
29398                 };
29399             }
29400         });
29401     },
29402
29403     area: function(resolver) {
29404         return resolver.transient(this, 'area', function() {
29405             return d3.geo.area(this.asGeoJSON(resolver));
29406         });
29407     },
29408
29409     isMultipolygon: function() {
29410         return this.tags.type === 'multipolygon';
29411     },
29412
29413     isComplete: function(resolver) {
29414         for (var i = 0; i < this.members.length; i++) {
29415             if (!resolver.hasEntity(this.members[i].id)) {
29416                 return false;
29417             }
29418         }
29419         return true;
29420     },
29421
29422     isRestriction: function() {
29423         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
29424     },
29425
29426     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
29427     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
29428     //
29429     // This corresponds to the structure needed for rendering a multipolygon path using a
29430     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
29431     //
29432     // In the case of invalid geometries, this function will still return a result which
29433     // includes the nodes of all way members, but some Nds may be unclosed and some inner
29434     // rings not matched with the intended outer ring.
29435     //
29436     multipolygon: function(resolver) {
29437         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
29438             inners = this.members.filter(function(m) { return 'inner' === m.role; });
29439
29440         outers = iD.geo.joinWays(outers, resolver);
29441         inners = iD.geo.joinWays(inners, resolver);
29442
29443         outers = outers.map(function(outer) { return _.map(outer.nodes, 'loc'); });
29444         inners = inners.map(function(inner) { return _.map(inner.nodes, 'loc'); });
29445
29446         var result = outers.map(function(o) {
29447             // Heuristic for detecting counterclockwise winding order. Assumes
29448             // that OpenStreetMap polygons are not hemisphere-spanning.
29449             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
29450         });
29451
29452         function findOuter(inner) {
29453             var o, outer;
29454
29455             for (o = 0; o < outers.length; o++) {
29456                 outer = outers[o];
29457                 if (iD.geo.polygonContainsPolygon(outer, inner))
29458                     return o;
29459             }
29460
29461             for (o = 0; o < outers.length; o++) {
29462                 outer = outers[o];
29463                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
29464                     return o;
29465             }
29466         }
29467
29468         for (var i = 0; i < inners.length; i++) {
29469             var inner = inners[i];
29470
29471             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
29472                 inner = inner.reverse();
29473             }
29474
29475             var o = findOuter(inners[i]);
29476             if (o !== undefined)
29477                 result[o].push(inners[i]);
29478             else
29479                 result.push([inners[i]]); // Invalid geometry
29480         }
29481
29482         return result;
29483     }
29484 });
29485 iD.oneWayTags = {
29486     'aerialway': {
29487         'chair_lift': true,
29488         'mixed_lift': true,
29489         't-bar': true,
29490         'j-bar': true,
29491         'platter': true,
29492         'rope_tow': true,
29493         'magic_carpet': true,
29494         'yes': true
29495     },
29496     'highway': {
29497         'motorway': true,
29498         'motorway_link': true
29499     },
29500     'junction': {
29501         'roundabout': true
29502     },
29503     'man_made': {
29504         'piste:halfpipe': true
29505     },
29506     'piste:type': {
29507         'downhill': true,
29508         'sled': true,
29509         'yes': true
29510     },
29511     'waterway': {
29512         'river': true,
29513         'stream': true
29514     }
29515 };
29516
29517 iD.pavedTags = {
29518     'surface': {
29519         'paved': true,
29520         'asphalt': true,
29521         'concrete': true
29522     },
29523     'tracktype': {
29524         'grade1': true
29525     }
29526 };
29527
29528 iD.interestingTag = function (key) {
29529     return key !== 'attribution' &&
29530         key !== 'created_by' &&
29531         key !== 'source' &&
29532         key !== 'odbl' &&
29533         key.indexOf('tiger:') !== 0;
29534
29535 };
29536 iD.Tree = function(head) {
29537     var rtree = rbush(),
29538         rectangles = {};
29539
29540     function entityRectangle(entity) {
29541         var rect = entity.extent(head).rectangle();
29542         rect.id = entity.id;
29543         rectangles[entity.id] = rect;
29544         return rect;
29545     }
29546
29547     function updateParents(entity, insertions, memo) {
29548         head.parentWays(entity).forEach(function(way) {
29549             if (rectangles[way.id]) {
29550                 rtree.remove(rectangles[way.id]);
29551                 insertions[way.id] = way;
29552             }
29553             updateParents(way, insertions, memo);
29554         });
29555
29556         head.parentRelations(entity).forEach(function(relation) {
29557             if (memo[entity.id]) return;
29558             memo[entity.id] = true;
29559             if (rectangles[relation.id]) {
29560                 rtree.remove(rectangles[relation.id]);
29561                 insertions[relation.id] = relation;
29562             }
29563             updateParents(relation, insertions, memo);
29564         });
29565     }
29566
29567     var tree = {};
29568
29569     tree.rebase = function(entities, force) {
29570         var insertions = {};
29571
29572         for (var i = 0; i < entities.length; i++) {
29573             var entity = entities[i];
29574
29575             if (!entity.visible)
29576                 continue;
29577
29578             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
29579                 if (!force) {
29580                     continue;
29581                 } else if (rectangles[entity.id]) {
29582                     rtree.remove(rectangles[entity.id]);
29583                 }
29584             }
29585
29586             insertions[entity.id] = entity;
29587             updateParents(entity, insertions, {});
29588         }
29589
29590         rtree.load(_.map(insertions, entityRectangle));
29591
29592         return tree;
29593     };
29594
29595     tree.intersects = function(extent, graph) {
29596         if (graph !== head) {
29597             var diff = iD.Difference(head, graph),
29598                 insertions = {};
29599
29600             head = graph;
29601
29602             diff.deleted().forEach(function(entity) {
29603                 rtree.remove(rectangles[entity.id]);
29604                 delete rectangles[entity.id];
29605             });
29606
29607             diff.modified().forEach(function(entity) {
29608                 rtree.remove(rectangles[entity.id]);
29609                 insertions[entity.id] = entity;
29610                 updateParents(entity, insertions, {});
29611             });
29612
29613             diff.created().forEach(function(entity) {
29614                 insertions[entity.id] = entity;
29615             });
29616
29617             rtree.load(_.map(insertions, entityRectangle));
29618         }
29619
29620         return rtree.search(extent.rectangle()).map(function(rect) {
29621             return head.entity(rect.id);
29622         });
29623     };
29624
29625     return tree;
29626 };
29627 iD.Way = iD.Entity.way = function iD_Way() {
29628     if (!(this instanceof iD_Way)) {
29629         return (new iD_Way()).initialize(arguments);
29630     } else if (arguments.length) {
29631         this.initialize(arguments);
29632     }
29633 };
29634
29635 iD.Way.prototype = Object.create(iD.Entity.prototype);
29636
29637 _.extend(iD.Way.prototype, {
29638     type: 'way',
29639     nodes: [],
29640
29641     copy: function(resolver, copies) {
29642         if (copies[this.id])
29643             return copies[this.id];
29644
29645         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
29646
29647         var nodes = this.nodes.map(function(id) {
29648             return resolver.entity(id).copy(resolver, copies).id;
29649         });
29650
29651         copy = copy.update({nodes: nodes});
29652         copies[this.id] = copy;
29653
29654         return copy;
29655     },
29656
29657     extent: function(resolver) {
29658         return resolver.transient(this, 'extent', function() {
29659             var extent = iD.geo.Extent();
29660             for (var i = 0; i < this.nodes.length; i++) {
29661                 var node = resolver.hasEntity(this.nodes[i]);
29662                 if (node) {
29663                     extent._extend(node.extent());
29664                 }
29665             }
29666             return extent;
29667         });
29668     },
29669
29670     first: function() {
29671         return this.nodes[0];
29672     },
29673
29674     last: function() {
29675         return this.nodes[this.nodes.length - 1];
29676     },
29677
29678     contains: function(node) {
29679         return this.nodes.indexOf(node) >= 0;
29680     },
29681
29682     affix: function(node) {
29683         if (this.nodes[0] === node) return 'prefix';
29684         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
29685     },
29686
29687     layer: function() {
29688         // explicit layer tag, clamp between -10, 10..
29689         if (this.tags.layer !== undefined) {
29690             return Math.max(-10, Math.min(+(this.tags.layer), 10));
29691         }
29692
29693         // implied layer tag..
29694         if (this.tags.location === 'overground') return 1;
29695         if (this.tags.location === 'underground') return -1;
29696         if (this.tags.location === 'underwater') return -10;
29697
29698         if (this.tags.power === 'line') return 10;
29699         if (this.tags.power === 'minor_line') return 10;
29700         if (this.tags.aerialway) return 10;
29701         if (this.tags.bridge) return 1;
29702         if (this.tags.cutting) return -1;
29703         if (this.tags.tunnel) return -1;
29704         if (this.tags.waterway) return -1;
29705         if (this.tags.man_made === 'pipeline') return -10;
29706         if (this.tags.boundary) return -10;
29707         return 0;
29708     },
29709
29710     isOneWay: function() {
29711         // explicit oneway tag..
29712         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
29713         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
29714
29715         // implied oneway tag..
29716         for (var key in this.tags) {
29717             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
29718                 return true;
29719         }
29720         return false;
29721     },
29722
29723     isClosed: function() {
29724         return this.nodes.length > 0 && this.first() === this.last();
29725     },
29726
29727     isConvex: function(resolver) {
29728         if (!this.isClosed() || this.isDegenerate()) return null;
29729
29730         var nodes = _.uniq(resolver.childNodes(this)),
29731             coords = _.map(nodes, 'loc'),
29732             curr = 0, prev = 0;
29733
29734         for (var i = 0; i < coords.length; i++) {
29735             var o = coords[(i+1) % coords.length],
29736                 a = coords[i],
29737                 b = coords[(i+2) % coords.length],
29738                 res = iD.geo.cross(o, a, b);
29739
29740             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
29741             if (curr === 0) {
29742                 continue;
29743             } else if (prev && curr !== prev) {
29744                 return false;
29745             }
29746             prev = curr;
29747         }
29748         return true;
29749     },
29750
29751     isArea: function() {
29752         if (this.tags.area === 'yes')
29753             return true;
29754         if (!this.isClosed() || this.tags.area === 'no')
29755             return false;
29756         for (var key in this.tags)
29757             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
29758                 return true;
29759         return false;
29760     },
29761
29762     isDegenerate: function() {
29763         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
29764     },
29765
29766     areAdjacent: function(n1, n2) {
29767         for (var i = 0; i < this.nodes.length; i++) {
29768             if (this.nodes[i] === n1) {
29769                 if (this.nodes[i - 1] === n2) return true;
29770                 if (this.nodes[i + 1] === n2) return true;
29771             }
29772         }
29773         return false;
29774     },
29775
29776     geometry: function(graph) {
29777         return graph.transient(this, 'geometry', function() {
29778             return this.isArea() ? 'area' : 'line';
29779         });
29780     },
29781
29782     addNode: function(id, index) {
29783         var nodes = this.nodes.slice();
29784         nodes.splice(index === undefined ? nodes.length : index, 0, id);
29785         return this.update({nodes: nodes});
29786     },
29787
29788     updateNode: function(id, index) {
29789         var nodes = this.nodes.slice();
29790         nodes.splice(index, 1, id);
29791         return this.update({nodes: nodes});
29792     },
29793
29794     replaceNode: function(needle, replacement) {
29795         if (this.nodes.indexOf(needle) < 0)
29796             return this;
29797
29798         var nodes = this.nodes.slice();
29799         for (var i = 0; i < nodes.length; i++) {
29800             if (nodes[i] === needle) {
29801                 nodes[i] = replacement;
29802             }
29803         }
29804         return this.update({nodes: nodes});
29805     },
29806
29807     removeNode: function(id) {
29808         var nodes = [];
29809
29810         for (var i = 0; i < this.nodes.length; i++) {
29811             var node = this.nodes[i];
29812             if (node !== id && nodes[nodes.length - 1] !== node) {
29813                 nodes.push(node);
29814             }
29815         }
29816
29817         // Preserve circularity
29818         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
29819             nodes.push(nodes[0]);
29820         }
29821
29822         return this.update({nodes: nodes});
29823     },
29824
29825     asJXON: function(changeset_id) {
29826         var r = {
29827             way: {
29828                 '@id': this.osmId(),
29829                 '@version': this.version || 0,
29830                 nd: _.map(this.nodes, function(id) {
29831                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
29832                 }),
29833                 tag: _.map(this.tags, function(v, k) {
29834                     return { keyAttributes: { k: k, v: v } };
29835                 })
29836             }
29837         };
29838         if (changeset_id) r.way['@changeset'] = changeset_id;
29839         return r;
29840     },
29841
29842     asGeoJSON: function(resolver) {
29843         return resolver.transient(this, 'GeoJSON', function() {
29844             var coordinates = _.map(resolver.childNodes(this), 'loc');
29845             if (this.isArea() && this.isClosed()) {
29846                 return {
29847                     type: 'Polygon',
29848                     coordinates: [coordinates]
29849                 };
29850             } else {
29851                 return {
29852                     type: 'LineString',
29853                     coordinates: coordinates
29854                 };
29855             }
29856         });
29857     },
29858
29859     area: function(resolver) {
29860         return resolver.transient(this, 'area', function() {
29861             var nodes = resolver.childNodes(this);
29862
29863             var json = {
29864                 type: 'Polygon',
29865                 coordinates: [_.map(nodes, 'loc')]
29866             };
29867
29868             if (!this.isClosed() && nodes.length) {
29869                 json.coordinates[0].push(nodes[0].loc);
29870             }
29871
29872             var area = d3.geo.area(json);
29873
29874             // Heuristic for detecting counterclockwise winding order. Assumes
29875             // that OpenStreetMap polygons are not hemisphere-spanning.
29876             if (area > 2 * Math.PI) {
29877                 json.coordinates[0] = json.coordinates[0].reverse();
29878                 area = d3.geo.area(json);
29879             }
29880
29881             return isNaN(area) ? 0 : area;
29882         });
29883     }
29884 });
29885 iD.Background = function(context) {
29886     var dispatch = d3.dispatch('change'),
29887         baseLayer = iD.TileLayer(context).projection(context.projection),
29888         overlayLayers = [],
29889         backgroundSources;
29890
29891
29892     function findSource(id) {
29893         return _.find(backgroundSources, function(d) {
29894             return d.id && d.id === id;
29895         });
29896     }
29897
29898
29899     function background(selection) {
29900         var base = selection.selectAll('.layer-background')
29901             .data([0]);
29902
29903         base.enter()
29904             .insert('div', '.layer-data')
29905             .attr('class', 'layer layer-background');
29906
29907         base.call(baseLayer);
29908
29909         var overlays = selection.selectAll('.layer-overlay')
29910             .data(overlayLayers, function(d) { return d.source().name(); });
29911
29912         overlays.enter()
29913             .insert('div', '.layer-data')
29914             .attr('class', 'layer layer-overlay');
29915
29916         overlays.each(function(layer) {
29917             d3.select(this).call(layer);
29918         });
29919
29920         overlays.exit()
29921             .remove();
29922     }
29923
29924
29925     background.updateImagery = function() {
29926         var b = background.baseLayerSource(),
29927             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
29928             meters = iD.geo.offsetToMeters(b.offset()),
29929             epsilon = 0.01,
29930             x = +meters[0].toFixed(2),
29931             y = +meters[1].toFixed(2),
29932             q = iD.util.stringQs(location.hash.substring(1));
29933
29934         var id = b.id;
29935         if (id === 'custom') {
29936             id = 'custom:' + b.template;
29937         }
29938
29939         if (id) {
29940             q.background = id;
29941         } else {
29942             delete q.background;
29943         }
29944
29945         if (o) {
29946             q.overlays = o;
29947         } else {
29948             delete q.overlays;
29949         }
29950
29951         if (Math.abs(x) > epsilon || Math.abs(y) > epsilon) {
29952             q.offset = x + ',' + y;
29953         } else {
29954             delete q.offset;
29955         }
29956
29957         location.replace('#' + iD.util.qsString(q, true));
29958
29959         var imageryUsed = [b.imageryUsed()];
29960
29961         overlayLayers.forEach(function (d) {
29962             var source = d.source();
29963             if (!source.isLocatorOverlay()) {
29964                 imageryUsed.push(source.imageryUsed());
29965             }
29966         });
29967
29968         var gpx = context.layers().layer('gpx');
29969         if (gpx && gpx.enabled() && gpx.hasGpx()) {
29970             imageryUsed.push('Local GPX');
29971         }
29972
29973         context.history().imageryUsed(imageryUsed);
29974     };
29975
29976     background.sources = function(extent) {
29977         return backgroundSources.filter(function(source) {
29978             return source.intersects(extent);
29979         });
29980     };
29981
29982     background.dimensions = function(_) {
29983         baseLayer.dimensions(_);
29984
29985         overlayLayers.forEach(function(layer) {
29986             layer.dimensions(_);
29987         });
29988     };
29989
29990     background.baseLayerSource = function(d) {
29991         if (!arguments.length) return baseLayer.source();
29992         baseLayer.source(d);
29993         dispatch.change();
29994         background.updateImagery();
29995         return background;
29996     };
29997
29998     background.bing = function() {
29999         background.baseLayerSource(findSource('Bing'));
30000     };
30001
30002     background.showsLayer = function(d) {
30003         return d === baseLayer.source() ||
30004             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
30005             overlayLayers.some(function(l) { return l.source() === d; });
30006     };
30007
30008     background.overlayLayerSources = function() {
30009         return overlayLayers.map(function (l) { return l.source(); });
30010     };
30011
30012     background.toggleOverlayLayer = function(d) {
30013         var layer;
30014
30015         for (var i = 0; i < overlayLayers.length; i++) {
30016             layer = overlayLayers[i];
30017             if (layer.source() === d) {
30018                 overlayLayers.splice(i, 1);
30019                 dispatch.change();
30020                 background.updateImagery();
30021                 return;
30022             }
30023         }
30024
30025         layer = iD.TileLayer(context)
30026             .source(d)
30027             .projection(context.projection)
30028             .dimensions(baseLayer.dimensions());
30029
30030         overlayLayers.push(layer);
30031         dispatch.change();
30032         background.updateImagery();
30033     };
30034
30035     background.nudge = function(d, zoom) {
30036         baseLayer.source().nudge(d, zoom);
30037         dispatch.change();
30038         background.updateImagery();
30039         return background;
30040     };
30041
30042     background.offset = function(d) {
30043         if (!arguments.length) return baseLayer.source().offset();
30044         baseLayer.source().offset(d);
30045         dispatch.change();
30046         background.updateImagery();
30047         return background;
30048     };
30049
30050     background.load = function(imagery) {
30051         function parseMap(qmap) {
30052             if (!qmap) return false;
30053             var args = qmap.split('/').map(Number);
30054             if (args.length < 3 || args.some(isNaN)) return false;
30055             return iD.geo.Extent([args[1], args[2]]);
30056         }
30057
30058         var q = iD.util.stringQs(location.hash.substring(1)),
30059             chosen = q.background || q.layer,
30060             extent = parseMap(q.map),
30061             best;
30062
30063         backgroundSources = imagery.map(function(source) {
30064             if (source.type === 'bing') {
30065                 return iD.BackgroundSource.Bing(source, dispatch);
30066             } else {
30067                 return iD.BackgroundSource(source);
30068             }
30069         });
30070
30071         backgroundSources.unshift(iD.BackgroundSource.None());
30072
30073         if (!chosen && extent) {
30074             best = _.find(this.sources(extent), function(s) { return s.best(); });
30075         }
30076
30077         if (chosen && chosen.indexOf('custom:') === 0) {
30078             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
30079         } else {
30080             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
30081         }
30082
30083         var locator = _.find(backgroundSources, function(d) {
30084             return d.overlay && d.default;
30085         });
30086
30087         if (locator) {
30088             background.toggleOverlayLayer(locator);
30089         }
30090
30091         var overlays = (q.overlays || '').split(',');
30092         overlays.forEach(function(overlay) {
30093             overlay = findSource(overlay);
30094             if (overlay) {
30095                 background.toggleOverlayLayer(overlay);
30096             }
30097         });
30098
30099         if (q.gpx) {
30100             var gpx = context.layers().layer('gpx');
30101             if (gpx) {
30102                 gpx.url(q.gpx);
30103             }
30104         }
30105
30106         if (q.offset) {
30107             var offset = q.offset.replace(/;/g, ',').split(',').map(function(n) {
30108                 return !isNaN(n) && n;
30109             });
30110
30111             if (offset.length === 2) {
30112                 background.offset(iD.geo.metersToOffset(offset));
30113             }
30114         }
30115     };
30116
30117     return d3.rebind(background, dispatch, 'on');
30118 };
30119 iD.BackgroundSource = function(data) {
30120     var source = _.clone(data),
30121         offset = [0, 0],
30122         name = source.name,
30123         best = !!source.best;
30124
30125     source.scaleExtent = data.scaleExtent || [0, 20];
30126     source.overzoom = data.overzoom !== false;
30127
30128     source.offset = function(_) {
30129         if (!arguments.length) return offset;
30130         offset = _;
30131         return source;
30132     };
30133
30134     source.nudge = function(_, zoomlevel) {
30135         offset[0] += _[0] / Math.pow(2, zoomlevel);
30136         offset[1] += _[1] / Math.pow(2, zoomlevel);
30137         return source;
30138     };
30139
30140     source.name = function() {
30141         return name;
30142     };
30143
30144     source.best = function() {
30145         return best;
30146     };
30147
30148     source.area = function() {
30149         if (!data.polygon) return Number.MAX_VALUE;  // worldwide
30150         var area = d3.geo.area({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
30151         return isNaN(area) ? 0 : area;
30152     };
30153
30154     source.imageryUsed = function() {
30155         return source.id || name;
30156     };
30157
30158     source.url = function(coord) {
30159         return data.template
30160             .replace('{x}', coord[0])
30161             .replace('{y}', coord[1])
30162             // TMS-flipped y coordinate
30163             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
30164             .replace(/\{z(oom)?\}/, coord[2])
30165             .replace(/\{switch:([^}]+)\}/, function(s, r) {
30166                 var subdomains = r.split(',');
30167                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
30168             })
30169             .replace('{u}', function() {
30170                 var u = '';
30171                 for (var zoom = coord[2]; zoom > 0; zoom--) {
30172                     var b = 0;
30173                     var mask = 1 << (zoom - 1);
30174                     if ((coord[0] & mask) !== 0) b++;
30175                     if ((coord[1] & mask) !== 0) b += 2;
30176                     u += b.toString();
30177                 }
30178                 return u;
30179             });
30180     };
30181
30182     source.intersects = function(extent) {
30183         extent = extent.polygon();
30184         return !data.polygon || data.polygon.some(function(polygon) {
30185             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
30186         });
30187     };
30188
30189     source.validZoom = function(z) {
30190         return source.scaleExtent[0] <= z &&
30191             (source.overzoom || source.scaleExtent[1] > z);
30192     };
30193
30194     source.isLocatorOverlay = function() {
30195         return name === 'Locator Overlay';
30196     };
30197
30198     source.copyrightNotices = function() {};
30199
30200     return source;
30201 };
30202
30203 iD.BackgroundSource.Bing = function(data, dispatch) {
30204     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
30205     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
30206
30207     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
30208
30209     var bing = iD.BackgroundSource(data),
30210         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
30211         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
30212             key + '&jsonp={callback}',
30213         providers = [];
30214
30215     d3.jsonp(url, function(json) {
30216         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
30217             return {
30218                 attribution: provider.attribution,
30219                 areas: provider.coverageAreas.map(function(area) {
30220                     return {
30221                         zoom: [area.zoomMin, area.zoomMax],
30222                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
30223                     };
30224                 })
30225             };
30226         });
30227         dispatch.change();
30228     });
30229
30230     bing.copyrightNotices = function(zoom, extent) {
30231         zoom = Math.min(zoom, 21);
30232         return providers.filter(function(provider) {
30233             return _.some(provider.areas, function(area) {
30234                 return extent.intersects(area.extent) &&
30235                     area.zoom[0] <= zoom &&
30236                     area.zoom[1] >= zoom;
30237             });
30238         }).map(function(provider) {
30239             return provider.attribution;
30240         }).join(', ');
30241     };
30242
30243     bing.logo = 'bing_maps.png';
30244     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
30245
30246     return bing;
30247 };
30248
30249 iD.BackgroundSource.None = function() {
30250     var source = iD.BackgroundSource({id: 'none', template: ''});
30251
30252     source.name = function() {
30253         return t('background.none');
30254     };
30255
30256     source.imageryUsed = function() {
30257         return 'None';
30258     };
30259
30260     source.area = function() {
30261         return -1;
30262     };
30263
30264     return source;
30265 };
30266
30267 iD.BackgroundSource.Custom = function(template) {
30268     var source = iD.BackgroundSource({id: 'custom', template: template});
30269
30270     source.name = function() {
30271         return t('background.custom');
30272     };
30273
30274     source.imageryUsed = function() {
30275         return 'Custom (' + template + ')';
30276     };
30277
30278     source.area = function() {
30279         return -2;
30280     };
30281
30282     return source;
30283 };
30284 iD.Features = function(context) {
30285     var traffic_roads = {
30286         'motorway': true,
30287         'motorway_link': true,
30288         'trunk': true,
30289         'trunk_link': true,
30290         'primary': true,
30291         'primary_link': true,
30292         'secondary': true,
30293         'secondary_link': true,
30294         'tertiary': true,
30295         'tertiary_link': true,
30296         'residential': true,
30297         'unclassified': true,
30298         'living_street': true
30299     };
30300
30301     var service_roads = {
30302         'service': true,
30303         'road': true,
30304         'track': true
30305     };
30306
30307     var paths = {
30308         'path': true,
30309         'footway': true,
30310         'cycleway': true,
30311         'bridleway': true,
30312         'steps': true,
30313         'pedestrian': true,
30314         'corridor': true
30315     };
30316
30317     var past_futures = {
30318         'proposed': true,
30319         'construction': true,
30320         'abandoned': true,
30321         'dismantled': true,
30322         'disused': true,
30323         'razed': true,
30324         'demolished': true,
30325         'obliterated': true
30326     };
30327
30328     var dispatch = d3.dispatch('change', 'redraw'),
30329         _cullFactor = 1,
30330         _cache = {},
30331         _features = {},
30332         _stats = {},
30333         _keys = [],
30334         _hidden = [];
30335
30336     function update() {
30337         _hidden = features.hidden();
30338         dispatch.change();
30339         dispatch.redraw();
30340     }
30341
30342     function defineFeature(k, filter, max) {
30343         _keys.push(k);
30344         _features[k] = {
30345             filter: filter,
30346             enabled: true,   // whether the user wants it enabled..
30347             count: 0,
30348             currentMax: (max || Infinity),
30349             defaultMax: (max || Infinity),
30350             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
30351             disable: function() { this.enabled = false; this.currentMax = 0; },
30352             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
30353             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
30354         };
30355     }
30356
30357
30358     defineFeature('points', function isPoint(entity, resolver, geometry) {
30359         return geometry === 'point';
30360     }, 200);
30361
30362     defineFeature('traffic_roads', function isTrafficRoad(entity) {
30363         return traffic_roads[entity.tags.highway];
30364     });
30365
30366     defineFeature('service_roads', function isServiceRoad(entity) {
30367         return service_roads[entity.tags.highway];
30368     });
30369
30370     defineFeature('paths', function isPath(entity) {
30371         return paths[entity.tags.highway];
30372     });
30373
30374     defineFeature('buildings', function isBuilding(entity) {
30375         return (
30376             !!entity.tags['building:part'] ||
30377             (!!entity.tags.building && entity.tags.building !== 'no') ||
30378             entity.tags.amenity === 'shelter' ||
30379             entity.tags.parking === 'multi-storey' ||
30380             entity.tags.parking === 'sheds' ||
30381             entity.tags.parking === 'carports' ||
30382             entity.tags.parking === 'garage_boxes'
30383         );
30384     }, 250);
30385
30386     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
30387         return geometry === 'area' &&
30388             !_features.buildings.filter(entity) &&
30389             !_features.water.filter(entity);
30390     });
30391
30392     defineFeature('boundaries', function isBoundary(entity) {
30393         return !!entity.tags.boundary;
30394     });
30395
30396     defineFeature('water', function isWater(entity) {
30397         return (
30398             !!entity.tags.waterway ||
30399             entity.tags.natural === 'water' ||
30400             entity.tags.natural === 'coastline' ||
30401             entity.tags.natural === 'bay' ||
30402             entity.tags.landuse === 'pond' ||
30403             entity.tags.landuse === 'basin' ||
30404             entity.tags.landuse === 'reservoir' ||
30405             entity.tags.landuse === 'salt_pond'
30406         );
30407     });
30408
30409     defineFeature('rail', function isRail(entity) {
30410         return (
30411             !!entity.tags.railway ||
30412             entity.tags.landuse === 'railway'
30413         ) && !(
30414             traffic_roads[entity.tags.highway] ||
30415             service_roads[entity.tags.highway] ||
30416             paths[entity.tags.highway]
30417         );
30418     });
30419
30420     defineFeature('power', function isPower(entity) {
30421         return !!entity.tags.power;
30422     });
30423
30424     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
30425     defineFeature('past_future', function isPastFuture(entity) {
30426         if (
30427             traffic_roads[entity.tags.highway] ||
30428             service_roads[entity.tags.highway] ||
30429             paths[entity.tags.highway]
30430         ) { return false; }
30431
30432         var strings = Object.keys(entity.tags);
30433
30434         for (var i = 0; i < strings.length; i++) {
30435             var s = strings[i];
30436             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
30437         }
30438         return false;
30439     });
30440
30441     // Lines or areas that don't match another feature filter.
30442     // IMPORTANT: The 'others' feature must be the last one defined,
30443     //   so that code in getMatches can skip this test if `hasMatch = true`
30444     defineFeature('others', function isOther(entity, resolver, geometry) {
30445         return (geometry === 'line' || geometry === 'area');
30446     });
30447
30448
30449     function features() {}
30450
30451     features.features = function() {
30452         return _features;
30453     };
30454
30455     features.keys = function() {
30456         return _keys;
30457     };
30458
30459     features.enabled = function(k) {
30460         if (!arguments.length) {
30461             return _.filter(_keys, function(k) { return _features[k].enabled; });
30462         }
30463         return _features[k] && _features[k].enabled;
30464     };
30465
30466     features.disabled = function(k) {
30467         if (!arguments.length) {
30468             return _.reject(_keys, function(k) { return _features[k].enabled; });
30469         }
30470         return _features[k] && !_features[k].enabled;
30471     };
30472
30473     features.hidden = function(k) {
30474         if (!arguments.length) {
30475             return _.filter(_keys, function(k) { return _features[k].hidden(); });
30476         }
30477         return _features[k] && _features[k].hidden();
30478     };
30479
30480     features.autoHidden = function(k) {
30481         if (!arguments.length) {
30482             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
30483         }
30484         return _features[k] && _features[k].autoHidden();
30485     };
30486
30487     features.enable = function(k) {
30488         if (_features[k] && !_features[k].enabled) {
30489             _features[k].enable();
30490             update();
30491         }
30492     };
30493
30494     features.disable = function(k) {
30495         if (_features[k] && _features[k].enabled) {
30496             _features[k].disable();
30497             update();
30498         }
30499     };
30500
30501     features.toggle = function(k) {
30502         if (_features[k]) {
30503             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
30504             update();
30505         }
30506     };
30507
30508     features.resetStats = function() {
30509         _.each(_features, function(f) { f.count = 0; });
30510         dispatch.change();
30511     };
30512
30513     features.gatherStats = function(d, resolver, dimensions) {
30514         var needsRedraw = false,
30515             type = _.groupBy(d, function(ent) { return ent.type; }),
30516             entities = [].concat(type.relation || [], type.way || [], type.node || []),
30517             currHidden, geometry, matches;
30518
30519         _.each(_features, function(f) { f.count = 0; });
30520
30521         // adjust the threshold for point/building culling based on viewport size..
30522         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
30523         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
30524
30525         for (var i = 0; i < entities.length; i++) {
30526             geometry = entities[i].geometry(resolver);
30527             if (!(geometry === 'vertex' || geometry === 'relation')) {
30528                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
30529                 for (var j = 0; j < matches.length; j++) {
30530                     _features[matches[j]].count++;
30531                 }
30532             }
30533         }
30534
30535         currHidden = features.hidden();
30536         if (currHidden !== _hidden) {
30537             _hidden = currHidden;
30538             needsRedraw = true;
30539             dispatch.change();
30540         }
30541
30542         return needsRedraw;
30543     };
30544
30545     features.stats = function() {
30546         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
30547         return _stats;
30548     };
30549
30550     features.clear = function(d) {
30551         for (var i = 0; i < d.length; i++) {
30552             features.clearEntity(d[i]);
30553         }
30554     };
30555
30556     features.clearEntity = function(entity) {
30557         delete _cache[iD.Entity.key(entity)];
30558     };
30559
30560     features.reset = function() {
30561         _cache = {};
30562     };
30563
30564     features.getMatches = function(entity, resolver, geometry) {
30565         if (geometry === 'vertex' || geometry === 'relation') return {};
30566
30567         var ent = iD.Entity.key(entity);
30568         if (!_cache[ent]) {
30569             _cache[ent] = {};
30570         }
30571
30572         if (!_cache[ent].matches) {
30573             var matches = {},
30574                 hasMatch = false;
30575
30576             for (var i = 0; i < _keys.length; i++) {
30577                 if (_keys[i] === 'others') {
30578                     if (hasMatch) continue;
30579
30580                     // Multipolygon members:
30581                     // If an entity...
30582                     //   1. is a way that hasn't matched other "interesting" feature rules,
30583                     //   2. and it belongs to a single parent multipolygon relation
30584                     // ...then match whatever feature rules the parent multipolygon has matched.
30585                     // see #2548, #2887
30586                     //
30587                     // IMPORTANT:
30588                     // For this to work, getMatches must be called on relations before ways.
30589                     //
30590                     if (entity.type === 'way') {
30591                         var parents = features.getParents(entity, resolver, geometry);
30592                         if (parents.length === 1 && parents[0].isMultipolygon()) {
30593                             var pkey = iD.Entity.key(parents[0]);
30594                             if (_cache[pkey] && _cache[pkey].matches) {
30595                                 matches = _.clone(_cache[pkey].matches);
30596                                 continue;
30597                             }
30598                         }
30599                     }
30600                 }
30601
30602                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
30603                     matches[_keys[i]] = hasMatch = true;
30604                 }
30605             }
30606             _cache[ent].matches = matches;
30607         }
30608
30609         return _cache[ent].matches;
30610     };
30611
30612     features.getParents = function(entity, resolver, geometry) {
30613         if (geometry === 'point') return [];
30614
30615         var ent = iD.Entity.key(entity);
30616         if (!_cache[ent]) {
30617             _cache[ent] = {};
30618         }
30619
30620         if (!_cache[ent].parents) {
30621             var parents = [];
30622             if (geometry === 'vertex') {
30623                 parents = resolver.parentWays(entity);
30624             } else {   // 'line', 'area', 'relation'
30625                 parents = resolver.parentRelations(entity);
30626             }
30627             _cache[ent].parents = parents;
30628         }
30629         return _cache[ent].parents;
30630     };
30631
30632     features.isHiddenFeature = function(entity, resolver, geometry) {
30633         if (!_hidden.length) return false;
30634         if (!entity.version) return false;
30635
30636         var matches = features.getMatches(entity, resolver, geometry);
30637
30638         for (var i = 0; i < _hidden.length; i++) {
30639             if (matches[_hidden[i]]) return true;
30640         }
30641         return false;
30642     };
30643
30644     features.isHiddenChild = function(entity, resolver, geometry) {
30645         if (!_hidden.length) return false;
30646         if (!entity.version || geometry === 'point') return false;
30647
30648         var parents = features.getParents(entity, resolver, geometry);
30649         if (!parents.length) return false;
30650
30651         for (var i = 0; i < parents.length; i++) {
30652             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
30653                 return false;
30654             }
30655         }
30656         return true;
30657     };
30658
30659     features.hasHiddenConnections = function(entity, resolver) {
30660         if (!_hidden.length) return false;
30661         var childNodes, connections;
30662
30663         if (entity.type === 'midpoint') {
30664             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
30665             connections = [];
30666         } else {
30667             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
30668             connections = features.getParents(entity, resolver, entity.geometry(resolver));
30669         }
30670
30671         // gather ways connected to child nodes..
30672         connections = _.reduce(childNodes, function(result, e) {
30673             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
30674         }, connections);
30675
30676         return connections.length ? _.some(connections, function(e) {
30677             return features.isHidden(e, resolver, e.geometry(resolver));
30678         }) : false;
30679     };
30680
30681     features.isHidden = function(entity, resolver, geometry) {
30682         if (!_hidden.length) return false;
30683         if (!entity.version) return false;
30684
30685         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
30686         return fn(entity, resolver, geometry);
30687     };
30688
30689     features.filter = function(d, resolver) {
30690         if (!_hidden.length) return d;
30691
30692         var result = [];
30693         for (var i = 0; i < d.length; i++) {
30694             var entity = d[i];
30695             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
30696                 result.push(entity);
30697             }
30698         }
30699         return result;
30700     };
30701
30702     return d3.rebind(features, dispatch, 'on');
30703 };
30704 iD.Map = function(context) {
30705     var dimensions = [1, 1],
30706         dispatch = d3.dispatch('move', 'drawn'),
30707         projection = context.projection,
30708         zoom = d3.behavior.zoom()
30709             .translate(projection.translate())
30710             .scale(projection.scale() * 2 * Math.PI)
30711             .scaleExtent([1024, 256 * Math.pow(2, 24)])
30712             .on('zoom', zoomPan),
30713         dblclickEnabled = true,
30714         redrawEnabled = true,
30715         transformStart,
30716         transformed = false,
30717         minzoom = 0,
30718         drawLayers = iD.svg.Layers(projection, context),
30719         drawPoints = iD.svg.Points(projection, context),
30720         drawVertices = iD.svg.Vertices(projection, context),
30721         drawLines = iD.svg.Lines(projection),
30722         drawAreas = iD.svg.Areas(projection),
30723         drawMidpoints = iD.svg.Midpoints(projection, context),
30724         drawLabels = iD.svg.Labels(projection, context),
30725         supersurface,
30726         wrapper,
30727         surface,
30728         mouse,
30729         mousemove;
30730
30731     function map(selection) {
30732         context
30733             .on('change.map', redraw);
30734         context.history()
30735             .on('change.map', redraw);
30736         context.background()
30737             .on('change.map', redraw);
30738         context.features()
30739             .on('redraw.map', redraw);
30740         drawLayers
30741             .on('change.map', function() {
30742                 context.background().updateImagery();
30743                 redraw();
30744             });
30745
30746         selection
30747             .on('dblclick.map', dblClick)
30748             .call(zoom);
30749
30750         supersurface = selection.append('div')
30751             .attr('id', 'supersurface')
30752             .call(iD.util.setTransform, 0, 0);
30753
30754         // Need a wrapper div because Opera can't cope with an absolutely positioned
30755         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
30756         wrapper = supersurface
30757             .append('div')
30758             .attr('class', 'layer layer-data');
30759
30760         map.surface = surface = wrapper
30761             .call(drawLayers)
30762             .selectAll('.surface')
30763             .attr('id', 'surface');
30764
30765         surface
30766             .on('mousedown.zoom', function() {
30767                 if (d3.event.button === 2) {
30768                     d3.event.stopPropagation();
30769                 }
30770             }, true)
30771             .on('mouseup.zoom', function() {
30772                 if (resetTransform()) redraw();
30773             })
30774             .on('mousemove.map', function() {
30775                 mousemove = d3.event;
30776             })
30777             .on('mouseover.vertices', function() {
30778                 if (map.editable() && !transformed) {
30779                     var hover = d3.event.target.__data__;
30780                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
30781                     dispatch.drawn({full: false});
30782                 }
30783             })
30784             .on('mouseout.vertices', function() {
30785                 if (map.editable() && !transformed) {
30786                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
30787                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
30788                     dispatch.drawn({full: false});
30789                 }
30790             });
30791
30792
30793         supersurface
30794             .call(context.background());
30795
30796
30797         context.on('enter.map', function() {
30798             if (map.editable() && !transformed) {
30799                 var all = context.intersects(map.extent()),
30800                     filter = d3.functor(true),
30801                     graph = context.graph();
30802
30803                 all = context.features().filter(all, graph);
30804                 surface
30805                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
30806                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
30807                 dispatch.drawn({full: false});
30808             }
30809         });
30810
30811         map.dimensions(selection.dimensions());
30812
30813         drawLabels.supersurface(supersurface);
30814     }
30815
30816     function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
30817
30818     function drawVector(difference, extent) {
30819         var graph = context.graph(),
30820             features = context.features(),
30821             all = context.intersects(map.extent()),
30822             data, filter;
30823
30824         if (difference) {
30825             var complete = difference.complete(map.extent());
30826             data = _.compact(_.values(complete));
30827             filter = function(d) { return d.id in complete; };
30828             features.clear(data);
30829
30830         } else {
30831             // force a full redraw if gatherStats detects that a feature
30832             // should be auto-hidden (e.g. points or buildings)..
30833             if (features.gatherStats(all, graph, dimensions)) {
30834                 extent = undefined;
30835             }
30836
30837             if (extent) {
30838                 data = context.intersects(map.extent().intersection(extent));
30839                 var set = d3.set(_.map(data, 'id'));
30840                 filter = function(d) { return set.has(d.id); };
30841
30842             } else {
30843                 data = all;
30844                 filter = d3.functor(true);
30845             }
30846         }
30847
30848         data = features.filter(data, graph);
30849
30850         surface
30851             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
30852             .call(drawLines, graph, data, filter)
30853             .call(drawAreas, graph, data, filter)
30854             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
30855             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
30856             .call(drawPoints, graph, data, filter);
30857
30858         dispatch.drawn({full: true});
30859     }
30860
30861     function editOff() {
30862         context.features().resetStats();
30863         surface.selectAll('.layer-osm *').remove();
30864         dispatch.drawn({full: true});
30865     }
30866
30867     function dblClick() {
30868         if (!dblclickEnabled) {
30869             d3.event.preventDefault();
30870             d3.event.stopImmediatePropagation();
30871         }
30872     }
30873
30874     function zoomPan() {
30875         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
30876             surface.interrupt();
30877             iD.ui.flash(context.container())
30878                 .select('.content')
30879                 .text(t('cannot_zoom'));
30880             setZoom(context.minEditableZoom(), true);
30881             queueRedraw();
30882             dispatch.move(map);
30883             return;
30884         }
30885
30886         projection
30887             .translate(d3.event.translate)
30888             .scale(d3.event.scale / (2 * Math.PI));
30889
30890         var scale = d3.event.scale / transformStart[0],
30891             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
30892             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
30893
30894         transformed = true;
30895         iD.util.setTransform(supersurface, tX, tY, scale);
30896         queueRedraw();
30897
30898         dispatch.move(map);
30899     }
30900
30901     function resetTransform() {
30902         if (!transformed) return false;
30903
30904         surface.selectAll('.radial-menu').interrupt().remove();
30905         iD.util.setTransform(supersurface, 0, 0);
30906         transformed = false;
30907         return true;
30908     }
30909
30910     function redraw(difference, extent) {
30911         if (!surface || !redrawEnabled) return;
30912
30913         clearTimeout(timeoutId);
30914
30915         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
30916         // It would result in artifacts where differenced entities are redrawn with
30917         // one transform and unchanged entities with another.
30918         if (resetTransform()) {
30919             difference = extent = undefined;
30920         }
30921
30922         var zoom = String(~~map.zoom());
30923         if (surface.attr('data-zoom') !== zoom) {
30924             surface.attr('data-zoom', zoom)
30925                 .classed('low-zoom', zoom <= 16);
30926         }
30927
30928         if (!difference) {
30929             supersurface.call(context.background());
30930         }
30931
30932         // OSM
30933         if (map.editable()) {
30934             context.loadTiles(projection, dimensions);
30935             drawVector(difference, extent);
30936         } else {
30937             editOff();
30938         }
30939
30940         wrapper
30941             .call(drawLayers);
30942
30943         transformStart = [
30944             projection.scale() * 2 * Math.PI,
30945             projection.translate().slice()];
30946
30947         return map;
30948     }
30949
30950     var timeoutId;
30951     function queueRedraw() {
30952         clearTimeout(timeoutId);
30953         timeoutId = setTimeout(function() { redraw(); }, 300);
30954     }
30955
30956     function pointLocation(p) {
30957         var translate = projection.translate(),
30958             scale = projection.scale() * 2 * Math.PI;
30959         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
30960     }
30961
30962     function locationPoint(l) {
30963         var translate = projection.translate(),
30964             scale = projection.scale() * 2 * Math.PI;
30965         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
30966     }
30967
30968     map.mouse = function() {
30969         var e = mousemove || d3.event, s;
30970         while ((s = e.sourceEvent)) e = s;
30971         return mouse(e);
30972     };
30973
30974     map.mouseCoordinates = function() {
30975         return projection.invert(map.mouse());
30976     };
30977
30978     map.dblclickEnable = function(_) {
30979         if (!arguments.length) return dblclickEnabled;
30980         dblclickEnabled = _;
30981         return map;
30982     };
30983
30984     map.redrawEnable = function(_) {
30985         if (!arguments.length) return redrawEnabled;
30986         redrawEnabled = _;
30987         return map;
30988     };
30989
30990     function interpolateZoom(_) {
30991         var k = projection.scale(),
30992             t = projection.translate();
30993
30994         surface.node().__chart__ = {
30995             x: t[0],
30996             y: t[1],
30997             k: k * 2 * Math.PI
30998         };
30999
31000         setZoom(_);
31001         projection.scale(k).translate(t);  // undo setZoom projection changes
31002
31003         zoom.event(surface.transition());
31004     }
31005
31006     function setZoom(_, force) {
31007         if (_ === map.zoom() && !force)
31008             return false;
31009         var scale = 256 * Math.pow(2, _),
31010             center = pxCenter(),
31011             l = pointLocation(center);
31012         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
31013         projection.scale(scale / (2 * Math.PI));
31014         zoom.scale(scale);
31015         var t = projection.translate();
31016         l = locationPoint(l);
31017         t[0] += center[0] - l[0];
31018         t[1] += center[1] - l[1];
31019         projection.translate(t);
31020         zoom.translate(projection.translate());
31021         return true;
31022     }
31023
31024     function setCenter(_) {
31025         var c = map.center();
31026         if (_[0] === c[0] && _[1] === c[1])
31027             return false;
31028         var t = projection.translate(),
31029             pxC = pxCenter(),
31030             ll = projection(_);
31031         projection.translate([
31032             t[0] - ll[0] + pxC[0],
31033             t[1] - ll[1] + pxC[1]]);
31034         zoom.translate(projection.translate());
31035         return true;
31036     }
31037
31038     map.pan = function(d) {
31039         var t = projection.translate();
31040         t[0] += d[0];
31041         t[1] += d[1];
31042         projection.translate(t);
31043         zoom.translate(projection.translate());
31044         dispatch.move(map);
31045         return redraw();
31046     };
31047
31048     map.dimensions = function(_) {
31049         if (!arguments.length) return dimensions;
31050         var center = map.center();
31051         dimensions = _;
31052         drawLayers.dimensions(dimensions);
31053         context.background().dimensions(dimensions);
31054         projection.clipExtent([[0, 0], dimensions]);
31055         mouse = iD.util.fastMouse(supersurface.node());
31056         setCenter(center);
31057         return redraw();
31058     };
31059
31060     function zoomIn(integer) {
31061       interpolateZoom(~~map.zoom() + integer);
31062     }
31063
31064     function zoomOut(integer) {
31065       interpolateZoom(~~map.zoom() - integer);
31066     }
31067
31068     map.zoomIn = function() { zoomIn(1); };
31069     map.zoomInFurther = function() { zoomIn(4); };
31070
31071     map.zoomOut = function() { zoomOut(1); };
31072     map.zoomOutFurther = function() { zoomOut(4); };
31073
31074     map.center = function(loc) {
31075         if (!arguments.length) {
31076             return projection.invert(pxCenter());
31077         }
31078
31079         if (setCenter(loc)) {
31080             dispatch.move(map);
31081         }
31082
31083         return redraw();
31084     };
31085
31086     map.zoom = function(z) {
31087         if (!arguments.length) {
31088             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
31089         }
31090
31091         if (z < minzoom) {
31092             surface.interrupt();
31093             iD.ui.flash(context.container())
31094                 .select('.content')
31095                 .text(t('cannot_zoom'));
31096             z = context.minEditableZoom();
31097         }
31098
31099         if (setZoom(z)) {
31100             dispatch.move(map);
31101         }
31102
31103         return redraw();
31104     };
31105
31106     map.zoomTo = function(entity, zoomLimits) {
31107         var extent = entity.extent(context.graph());
31108         if (!isFinite(extent.area())) return;
31109
31110         var zoom = map.trimmedExtentZoom(extent);
31111         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
31112         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
31113     };
31114
31115     map.centerZoom = function(loc, z) {
31116         var centered = setCenter(loc),
31117             zoomed   = setZoom(z);
31118
31119         if (centered || zoomed) {
31120             dispatch.move(map);
31121         }
31122
31123         return redraw();
31124     };
31125
31126     map.centerEase = function(loc) {
31127         var from = map.center().slice(),
31128             t = 0,
31129             stop;
31130
31131         surface.one('mousedown.ease', function() {
31132             stop = true;
31133         });
31134
31135         d3.timer(function() {
31136             if (stop) return true;
31137             map.center(iD.geo.interp(from, loc, (t += 1) / 10));
31138             return t === 10;
31139         }, 20);
31140         return map;
31141     };
31142
31143     map.extent = function(_) {
31144         if (!arguments.length) {
31145             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
31146                                  projection.invert([dimensions[0], 0]));
31147         } else {
31148             var extent = iD.geo.Extent(_);
31149             map.centerZoom(extent.center(), map.extentZoom(extent));
31150         }
31151     };
31152
31153     map.trimmedExtent = function(_) {
31154         if (!arguments.length) {
31155             var headerY = 60, footerY = 30, pad = 10;
31156             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
31157                     projection.invert([dimensions[0] - pad, headerY + pad]));
31158         } else {
31159             var extent = iD.geo.Extent(_);
31160             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
31161         }
31162     };
31163
31164     function calcZoom(extent, dim) {
31165         var tl = projection([extent[0][0], extent[1][1]]),
31166             br = projection([extent[1][0], extent[0][1]]);
31167
31168         // Calculate maximum zoom that fits extent
31169         var hFactor = (br[0] - tl[0]) / dim[0],
31170             vFactor = (br[1] - tl[1]) / dim[1],
31171             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
31172             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
31173             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
31174
31175         return newZoom;
31176     }
31177
31178     map.extentZoom = function(_) {
31179         return calcZoom(iD.geo.Extent(_), dimensions);
31180     };
31181
31182     map.trimmedExtentZoom = function(_) {
31183         var trimY = 120, trimX = 40,
31184             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
31185         return calcZoom(iD.geo.Extent(_), trimmed);
31186     };
31187
31188     map.editable = function() {
31189         return map.zoom() >= context.minEditableZoom();
31190     };
31191
31192     map.minzoom = function(_) {
31193         if (!arguments.length) return minzoom;
31194         minzoom = _;
31195         return map;
31196     };
31197
31198     map.layers = drawLayers;
31199
31200     return d3.rebind(map, dispatch, 'on');
31201 };
31202 iD.TileLayer = function(context) {
31203     var tileSize = 256,
31204         tile = d3.geo.tile(),
31205         projection,
31206         cache = {},
31207         tileOrigin,
31208         z,
31209         transformProp = iD.util.prefixCSSProperty('Transform'),
31210         source = d3.functor('');
31211
31212
31213     // blacklist overlay tiles around Null Island..
31214     function nearNullIsland(x, y, z) {
31215         if (z >= 7) {
31216             var center = Math.pow(2, z - 1),
31217                 width = Math.pow(2, z - 6),
31218                 min = center - (width / 2),
31219                 max = center + (width / 2) - 1;
31220             return x >= min && x <= max && y >= min && y <= max;
31221         }
31222         return false;
31223     }
31224
31225     function tileSizeAtZoom(d, z) {
31226         var epsilon = 0.002;
31227         return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
31228     }
31229
31230     function atZoom(t, distance) {
31231         var power = Math.pow(2, distance);
31232         return [
31233             Math.floor(t[0] * power),
31234             Math.floor(t[1] * power),
31235             t[2] + distance];
31236     }
31237
31238     function lookUp(d) {
31239         for (var up = -1; up > -d[2]; up--) {
31240             var tile = atZoom(d, up);
31241             if (cache[source.url(tile)] !== false) {
31242                 return tile;
31243             }
31244         }
31245     }
31246
31247     function uniqueBy(a, n) {
31248         var o = [], seen = {};
31249         for (var i = 0; i < a.length; i++) {
31250             if (seen[a[i][n]] === undefined) {
31251                 o.push(a[i]);
31252                 seen[a[i][n]] = true;
31253             }
31254         }
31255         return o;
31256     }
31257
31258     function addSource(d) {
31259         d.push(source.url(d));
31260         return d;
31261     }
31262
31263     // Update tiles based on current state of `projection`.
31264     function background(selection) {
31265         tile.scale(projection.scale() * 2 * Math.PI)
31266             .translate(projection.translate());
31267
31268         tileOrigin = [
31269             projection.scale() * Math.PI - projection.translate()[0],
31270             projection.scale() * Math.PI - projection.translate()[1]];
31271
31272         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
31273
31274         render(selection);
31275     }
31276
31277     // Derive the tiles onscreen, remove those offscreen and position them.
31278     // Important that this part not depend on `projection` because it's
31279     // rentered when tiles load/error (see #644).
31280     function render(selection) {
31281         var requests = [];
31282         var showDebug = context.debugTile() && !source.overlay;
31283
31284         if (source.validZoom(z)) {
31285             tile().forEach(function(d) {
31286                 addSource(d);
31287                 if (d[3] === '') return;
31288                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
31289                 requests.push(d);
31290                 if (cache[d[3]] === false && lookUp(d)) {
31291                     requests.push(addSource(lookUp(d)));
31292                 }
31293             });
31294
31295             requests = uniqueBy(requests, 3).filter(function(r) {
31296                 if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
31297                     return false;
31298                 }
31299                 // don't re-request tiles which have failed in the past
31300                 return cache[r[3]] !== false;
31301             });
31302         }
31303
31304         var pixelOffset = [
31305             source.offset()[0] * Math.pow(2, z),
31306             source.offset()[1] * Math.pow(2, z)
31307         ];
31308
31309         function load(d) {
31310             cache[d[3]] = true;
31311             d3.select(this)
31312                 .on('error', null)
31313                 .on('load', null)
31314                 .classed('tile-loaded', true);
31315             render(selection);
31316         }
31317
31318         function error(d) {
31319             cache[d[3]] = false;
31320             d3.select(this)
31321                 .on('error', null)
31322                 .on('load', null)
31323                 .remove();
31324             render(selection);
31325         }
31326
31327         function imageTransform(d) {
31328             var _ts = tileSize * Math.pow(2, z - d[2]);
31329             var scale = tileSizeAtZoom(d, z);
31330             return 'translate(' +
31331                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
31332                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
31333                 'scale(' + scale + ',' + scale + ')';
31334         }
31335
31336         function debugTransform(d) {
31337             var _ts = tileSize * Math.pow(2, z - d[2]);
31338             var scale = tileSizeAtZoom(d, z);
31339             return 'translate(' +
31340                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)) + 'px,' +
31341                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) + 'px)';
31342         }
31343
31344         var image = selection
31345             .selectAll('img')
31346             .data(requests, function(d) { return d[3]; });
31347
31348         image.exit()
31349             .style(transformProp, imageTransform)
31350             .classed('tile-removing', true)
31351             .each(function() {
31352                 var tile = d3.select(this);
31353                 window.setTimeout(function() {
31354                     if (tile.classed('tile-removing')) {
31355                         tile.remove();
31356                     }
31357                 }, 300);
31358             });
31359
31360         image.enter().append('img')
31361             .attr('class', 'tile')
31362             .attr('src', function(d) { return d[3]; })
31363             .on('error', error)
31364             .on('load', load);
31365
31366         image
31367             .style(transformProp, imageTransform)
31368             .classed('tile-debug', showDebug)
31369             .classed('tile-removing', false);
31370
31371
31372         var debug = selection.selectAll('.tile-label-debug')
31373             .data(showDebug ? requests : [], function(d) { return d[3]; });
31374
31375         debug.exit()
31376             .remove();
31377
31378         debug.enter()
31379             .append('div')
31380             .attr('class', 'tile-label-debug');
31381
31382         debug
31383             .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; })
31384             .style(transformProp, debugTransform);
31385     }
31386
31387     background.projection = function(_) {
31388         if (!arguments.length) return projection;
31389         projection = _;
31390         return background;
31391     };
31392
31393     background.dimensions = function(_) {
31394         if (!arguments.length) return tile.size();
31395         tile.size(_);
31396         return background;
31397     };
31398
31399     background.source = function(_) {
31400         if (!arguments.length) return source;
31401         source = _;
31402         cache = {};
31403         tile.scaleExtent(source.scaleExtent);
31404         return background;
31405     };
31406
31407     return background;
31408 };
31409 iD.svg = {
31410     PointTransform: function(projection) {
31411         return function(entity) {
31412             // http://jsperf.com/short-array-join
31413             var pt = projection(entity.loc);
31414             return 'translate(' + pt[0] + ',' + pt[1] + ')';
31415         };
31416     },
31417
31418     Path: function(projection, graph, polygon) {
31419         var cache = {},
31420             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
31421             project = projection.stream,
31422             path = d3.geo.path()
31423                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
31424
31425         return function(entity) {
31426             if (entity.id in cache) {
31427                 return cache[entity.id];
31428             } else {
31429                 return cache[entity.id] = path(entity.asGeoJSON(graph));
31430             }
31431         };
31432     },
31433
31434     OneWaySegments: function(projection, graph, dt) {
31435         return function(entity) {
31436             var a,
31437                 b,
31438                 i = 0,
31439                 offset = dt,
31440                 segments = [],
31441                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
31442                 coordinates = graph.childNodes(entity).map(function(n) {
31443                     return n.loc;
31444                 });
31445
31446             if (entity.tags.oneway === '-1') coordinates.reverse();
31447
31448             d3.geo.stream({
31449                 type: 'LineString',
31450                 coordinates: coordinates
31451             }, projection.stream(clip({
31452                 lineStart: function() {},
31453                 lineEnd: function() {
31454                     a = null;
31455                 },
31456                 point: function(x, y) {
31457                     b = [x, y];
31458
31459                     if (a) {
31460                         var span = iD.geo.euclideanDistance(a, b) - offset;
31461
31462                         if (span >= 0) {
31463                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
31464                                 dx = dt * Math.cos(angle),
31465                                 dy = dt * Math.sin(angle),
31466                                 p = [a[0] + offset * Math.cos(angle),
31467                                      a[1] + offset * Math.sin(angle)];
31468
31469                             var segment = 'M' + a[0] + ',' + a[1] +
31470                                           'L' + p[0] + ',' + p[1];
31471
31472                             for (span -= dt; span >= 0; span -= dt) {
31473                                 p[0] += dx;
31474                                 p[1] += dy;
31475                                 segment += 'L' + p[0] + ',' + p[1];
31476                             }
31477
31478                             segment += 'L' + b[0] + ',' + b[1];
31479                             segments.push({id: entity.id, index: i, d: segment});
31480                         }
31481
31482                         offset = -span;
31483                         i++;
31484                     }
31485
31486                     a = b;
31487                 }
31488             })));
31489
31490             return segments;
31491         };
31492     },
31493
31494     RelationMemberTags: function(graph) {
31495         return function(entity) {
31496             var tags = entity.tags;
31497             graph.parentRelations(entity).forEach(function(relation) {
31498                 var type = relation.tags.type;
31499                 if (type === 'multipolygon' || type === 'boundary') {
31500                     tags = _.extend({}, relation.tags, tags);
31501                 }
31502             });
31503             return tags;
31504         };
31505     }
31506 };
31507 iD.svg.Areas = function(projection) {
31508     // Patterns only work in Firefox when set directly on element.
31509     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
31510     var patterns = {
31511         wetland: 'wetland',
31512         beach: 'beach',
31513         scrub: 'scrub',
31514         construction: 'construction',
31515         military: 'construction',
31516         cemetery: 'cemetery',
31517         grave_yard: 'cemetery',
31518         meadow: 'meadow',
31519         farm: 'farmland',
31520         farmland: 'farmland',
31521         orchard: 'orchard'
31522     };
31523
31524     var patternKeys = ['landuse', 'natural', 'amenity'];
31525
31526     function setPattern(d) {
31527         for (var i = 0; i < patternKeys.length; i++) {
31528             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
31529                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
31530                 return;
31531             }
31532         }
31533         this.style.fill = this.style.stroke = '';
31534     }
31535
31536     return function drawAreas(surface, graph, entities, filter) {
31537         var path = iD.svg.Path(projection, graph, true),
31538             areas = {},
31539             multipolygon;
31540
31541         for (var i = 0; i < entities.length; i++) {
31542             var entity = entities[i];
31543             if (entity.geometry(graph) !== 'area') continue;
31544
31545             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
31546             if (multipolygon) {
31547                 areas[multipolygon.id] = {
31548                     entity: multipolygon.mergeTags(entity.tags),
31549                     area: Math.abs(entity.area(graph))
31550                 };
31551             } else if (!areas[entity.id]) {
31552                 areas[entity.id] = {
31553                     entity: entity,
31554                     area: Math.abs(entity.area(graph))
31555                 };
31556             }
31557         }
31558
31559         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
31560         areas.sort(function areaSort(a, b) { return b.area - a.area; });
31561         areas = _.map(areas, 'entity');
31562
31563         var strokes = areas.filter(function(area) {
31564             return area.type === 'way';
31565         });
31566
31567         var data = {
31568             clip: areas,
31569             shadow: strokes,
31570             stroke: strokes,
31571             fill: areas
31572         };
31573
31574         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
31575            .filter(filter)
31576            .data(data.clip, iD.Entity.key);
31577
31578         clipPaths.enter()
31579            .append('clipPath')
31580            .attr('class', 'clipPath')
31581            .attr('id', function(entity) { return entity.id + '-clippath'; })
31582            .append('path');
31583
31584         clipPaths.selectAll('path')
31585            .attr('d', path);
31586
31587         clipPaths.exit()
31588            .remove();
31589
31590         var areagroup = surface
31591             .selectAll('.layer-areas')
31592             .selectAll('g.areagroup')
31593             .data(['fill', 'shadow', 'stroke']);
31594
31595         areagroup.enter()
31596             .append('g')
31597             .attr('class', function(d) { return 'layer areagroup area-' + d; });
31598
31599         var paths = areagroup
31600             .selectAll('path')
31601             .filter(filter)
31602             .data(function(layer) { return data[layer]; }, iD.Entity.key);
31603
31604         // Remove exiting areas first, so they aren't included in the `fills`
31605         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
31606         paths.exit()
31607             .remove();
31608
31609         var fills = surface.selectAll('.area-fill path.area')[0];
31610
31611         var bisect = d3.bisector(function(node) {
31612             return -node.__data__.area(graph);
31613         }).left;
31614
31615         function sortedByArea(entity) {
31616             if (this.__data__ === 'fill') {
31617                 return fills[bisect(fills, -entity.area(graph))];
31618             }
31619         }
31620
31621         paths.enter()
31622             .insert('path', sortedByArea)
31623             .each(function(entity) {
31624                 var layer = this.parentNode.__data__;
31625
31626                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
31627
31628                 if (layer === 'fill') {
31629                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
31630                     setPattern.apply(this, arguments);
31631                 }
31632             })
31633             .call(iD.svg.TagClasses());
31634
31635         paths
31636             .attr('d', path);
31637     };
31638 };
31639 /*
31640     A standalone SVG element that contains only a `defs` sub-element. To be
31641     used once globally, since defs IDs must be unique within a document.
31642 */
31643 iD.svg.Defs = function(context) {
31644
31645     function SVGSpriteDefinition(id, href) {
31646         return function(defs) {
31647             d3.xml(href, 'image/svg+xml', function(err, svg) {
31648                 if (err) return;
31649                 defs.node().appendChild(
31650                     d3.select(svg.documentElement).attr('id', id).node()
31651                 );
31652             });
31653         };
31654     }
31655
31656     return function drawDefs(selection) {
31657         var defs = selection.append('defs');
31658
31659         // marker
31660         defs.append('marker')
31661             .attr({
31662                 id: 'oneway-marker',
31663                 viewBox: '0 0 10 10',
31664                 refY: 2.5,
31665                 refX: 5,
31666                 markerWidth: 2,
31667                 markerHeight: 2,
31668                 markerUnits: 'strokeWidth',
31669                 orient: 'auto'
31670             })
31671             .append('path')
31672             .attr('class', 'oneway')
31673             .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')
31674             .attr('stroke', 'none')
31675             .attr('fill', '#000')
31676             .attr('opacity', '0.5');
31677
31678         // patterns
31679         var patterns = defs.selectAll('pattern')
31680             .data([
31681                 // pattern name, pattern image name
31682                 ['wetland', 'wetland'],
31683                 ['construction', 'construction'],
31684                 ['cemetery', 'cemetery'],
31685                 ['orchard', 'orchard'],
31686                 ['farmland', 'farmland'],
31687                 ['beach', 'dots'],
31688                 ['scrub', 'dots'],
31689                 ['meadow', 'dots']
31690             ])
31691             .enter()
31692             .append('pattern')
31693             .attr({
31694                 id: function (d) {
31695                     return 'pattern-' + d[0];
31696                 },
31697                 width: 32,
31698                 height: 32,
31699                 patternUnits: 'userSpaceOnUse'
31700             });
31701
31702         patterns.append('rect')
31703             .attr({
31704                 x: 0,
31705                 y: 0,
31706                 width: 32,
31707                 height: 32,
31708                 'class': function (d) {
31709                     return 'pattern-color-' + d[0];
31710                 }
31711             });
31712
31713         patterns.append('image')
31714             .attr({
31715                 x: 0,
31716                 y: 0,
31717                 width: 32,
31718                 height: 32
31719             })
31720             .attr('xlink:href', function (d) {
31721                 return context.imagePath('pattern/' + d[1] + '.png');
31722             });
31723
31724         // clip paths
31725         defs.selectAll()
31726             .data([12, 18, 20, 32, 45])
31727             .enter().append('clipPath')
31728             .attr('id', function (d) {
31729                 return 'clip-square-' + d;
31730             })
31731             .append('rect')
31732             .attr('x', 0)
31733             .attr('y', 0)
31734             .attr('width', function (d) {
31735                 return d;
31736             })
31737             .attr('height', function (d) {
31738                 return d;
31739             });
31740
31741         defs.call(SVGSpriteDefinition(
31742             'iD-sprite',
31743             context.imagePath('iD-sprite.svg')));
31744
31745         defs.call(SVGSpriteDefinition(
31746             'maki-sprite',
31747             context.imagePath('maki-sprite.svg')));
31748     };
31749 };
31750 iD.svg.Gpx = function(projection, context, dispatch) {
31751     var showLabels = true,
31752         layer;
31753
31754     function init() {
31755         if (iD.svg.Gpx.initialized) return;  // run once
31756
31757         iD.svg.Gpx.geojson = {};
31758         iD.svg.Gpx.enabled = true;
31759
31760         function over() {
31761             d3.event.stopPropagation();
31762             d3.event.preventDefault();
31763             d3.event.dataTransfer.dropEffect = 'copy';
31764         }
31765
31766         d3.select('body')
31767             .attr('dropzone', 'copy')
31768             .on('drop.localgpx', function() {
31769                 d3.event.stopPropagation();
31770                 d3.event.preventDefault();
31771                 if (!iD.detect().filedrop) return;
31772                 drawGpx.files(d3.event.dataTransfer.files);
31773             })
31774             .on('dragenter.localgpx', over)
31775             .on('dragexit.localgpx', over)
31776             .on('dragover.localgpx', over);
31777
31778         iD.svg.Gpx.initialized = true;
31779     }
31780
31781
31782     function drawGpx(surface) {
31783         var geojson = iD.svg.Gpx.geojson,
31784             enabled = iD.svg.Gpx.enabled;
31785
31786         layer = surface.selectAll('.layer-gpx')
31787             .data(enabled ? [0] : []);
31788
31789         layer.enter()
31790             .append('g')
31791             .attr('class', 'layer-gpx');
31792
31793         layer.exit()
31794             .remove();
31795
31796
31797         var paths = layer
31798             .selectAll('path')
31799             .data([geojson]);
31800
31801         paths.enter()
31802             .append('path')
31803             .attr('class', 'gpx');
31804
31805         paths.exit()
31806             .remove();
31807
31808         var path = d3.geo.path()
31809             .projection(projection);
31810
31811         paths
31812             .attr('d', path);
31813
31814
31815         var labels = layer.selectAll('text')
31816             .data(showLabels && geojson.features ? geojson.features : []);
31817
31818         labels.enter()
31819             .append('text')
31820             .attr('class', 'gpx');
31821
31822         labels.exit()
31823             .remove();
31824
31825         labels
31826             .text(function(d) {
31827                 return d.properties.desc || d.properties.name;
31828             })
31829             .attr('x', function(d) {
31830                 var centroid = path.centroid(d);
31831                 return centroid[0] + 7;
31832             })
31833             .attr('y', function(d) {
31834                 var centroid = path.centroid(d);
31835                 return centroid[1];
31836             });
31837
31838     }
31839
31840     function toDom(x) {
31841         return (new DOMParser()).parseFromString(x, 'text/xml');
31842     }
31843
31844     drawGpx.showLabels = function(_) {
31845         if (!arguments.length) return showLabels;
31846         showLabels = _;
31847         return this;
31848     };
31849
31850     drawGpx.enabled = function(_) {
31851         if (!arguments.length) return iD.svg.Gpx.enabled;
31852         iD.svg.Gpx.enabled = _;
31853         dispatch.change();
31854         return this;
31855     };
31856
31857     drawGpx.hasGpx = function() {
31858         var geojson = iD.svg.Gpx.geojson;
31859         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
31860     };
31861
31862     drawGpx.geojson = function(gj) {
31863         if (!arguments.length) return iD.svg.Gpx.geojson;
31864         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
31865         iD.svg.Gpx.geojson = gj;
31866         dispatch.change();
31867         return this;
31868     };
31869
31870     drawGpx.url = function(url) {
31871         d3.text(url, function(err, data) {
31872             if (!err) {
31873                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
31874             }
31875         });
31876         return this;
31877     };
31878
31879     drawGpx.files = function(fileList) {
31880         if (!fileList.length) return this;
31881         var f = fileList[0],
31882             reader = new FileReader();
31883
31884         reader.onload = function(e) {
31885             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
31886         };
31887
31888         reader.readAsText(f);
31889         return this;
31890     };
31891
31892     drawGpx.fitZoom = function() {
31893         if (!this.hasGpx()) return this;
31894         var geojson = iD.svg.Gpx.geojson;
31895
31896         var map = context.map(),
31897             viewport = map.trimmedExtent().polygon(),
31898             coords = _.reduce(geojson.features, function(coords, feature) {
31899                 var c = feature.geometry.coordinates;
31900                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
31901             }, []);
31902
31903         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
31904             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
31905             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
31906         }
31907
31908         return this;
31909     };
31910
31911     init();
31912     return drawGpx;
31913 };
31914 iD.svg.Icon = function(name, svgklass, useklass) {
31915     return function drawIcon(selection) {
31916         selection.selectAll('svg')
31917             .data([0])
31918             .enter()
31919             .append('svg')
31920             .attr('class', 'icon ' + (svgklass || ''))
31921             .append('use')
31922             .attr('xlink:href', name)
31923             .attr('class', useklass);
31924     };
31925 };
31926 iD.svg.Labels = function(projection, context) {
31927     var path = d3.geo.path().projection(projection);
31928
31929     // Replace with dict and iterate over entities tags instead?
31930     var label_stack = [
31931         ['line', 'aeroway'],
31932         ['line', 'highway'],
31933         ['line', 'railway'],
31934         ['line', 'waterway'],
31935         ['area', 'aeroway'],
31936         ['area', 'amenity'],
31937         ['area', 'building'],
31938         ['area', 'historic'],
31939         ['area', 'leisure'],
31940         ['area', 'man_made'],
31941         ['area', 'natural'],
31942         ['area', 'shop'],
31943         ['area', 'tourism'],
31944         ['point', 'aeroway'],
31945         ['point', 'amenity'],
31946         ['point', 'building'],
31947         ['point', 'historic'],
31948         ['point', 'leisure'],
31949         ['point', 'man_made'],
31950         ['point', 'natural'],
31951         ['point', 'shop'],
31952         ['point', 'tourism'],
31953         ['line', 'name'],
31954         ['area', 'name'],
31955         ['point', 'name']
31956     ];
31957
31958     var default_size = 12;
31959
31960     var font_sizes = label_stack.map(function(d) {
31961         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
31962             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
31963         if (m) return parseInt(m[1], 10);
31964
31965         style = iD.util.getStyle('text.' + d[0]);
31966         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
31967         if (m) return parseInt(m[1], 10);
31968
31969         return default_size;
31970     });
31971
31972     var iconSize = 18;
31973
31974     var pointOffsets = [
31975         [15, -11, 'start'], // right
31976         [10, -11, 'start'], // unused right now
31977         [-15, -11, 'end']
31978     ];
31979
31980     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
31981         75, 20, 80, 15, 95, 10, 90, 5, 95];
31982
31983
31984     var noIcons = ['building', 'landuse', 'natural'];
31985     function blacklisted(preset) {
31986         return _.some(noIcons, function(s) {
31987             return preset.id.indexOf(s) >= 0;
31988         });
31989     }
31990
31991     function get(array, prop) {
31992         return function(d, i) { return array[i][prop]; };
31993     }
31994
31995     var textWidthCache = {};
31996
31997     function textWidth(text, size, elem) {
31998         var c = textWidthCache[size];
31999         if (!c) c = textWidthCache[size] = {};
32000
32001         if (c[text]) {
32002             return c[text];
32003
32004         } else if (elem) {
32005             c[text] = elem.getComputedTextLength();
32006             return c[text];
32007
32008         } else {
32009             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
32010             if (str === null) {
32011                 return size / 3 * 2 * text.length;
32012             } else {
32013                 return size / 3 * (2 * text.length + str.length);
32014             }
32015         }
32016     }
32017
32018     function drawLineLabels(group, entities, filter, classes, labels) {
32019         var texts = group.selectAll('text.' + classes)
32020             .filter(filter)
32021             .data(entities, iD.Entity.key);
32022
32023         texts.enter()
32024             .append('text')
32025             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
32026             .append('textPath')
32027             .attr('class', 'textpath');
32028
32029
32030         texts.selectAll('.textpath')
32031             .filter(filter)
32032             .data(entities, iD.Entity.key)
32033             .attr({
32034                 'startOffset': '50%',
32035                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
32036             })
32037             .text(iD.util.displayName);
32038
32039         texts.exit().remove();
32040     }
32041
32042     function drawLinePaths(group, entities, filter, classes, labels) {
32043         var halos = group.selectAll('path')
32044             .filter(filter)
32045             .data(entities, iD.Entity.key);
32046
32047         halos.enter()
32048             .append('path')
32049             .style('stroke-width', get(labels, 'font-size'))
32050             .attr('id', function(d) { return 'labelpath-' + d.id; })
32051             .attr('class', classes);
32052
32053         halos.attr('d', get(labels, 'lineString'));
32054
32055         halos.exit().remove();
32056     }
32057
32058     function drawPointLabels(group, entities, filter, classes, labels) {
32059         var texts = group.selectAll('text.' + classes)
32060             .filter(filter)
32061             .data(entities, iD.Entity.key);
32062
32063         texts.enter()
32064             .append('text')
32065             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
32066
32067         texts.attr('x', get(labels, 'x'))
32068             .attr('y', get(labels, 'y'))
32069             .style('text-anchor', get(labels, 'textAnchor'))
32070             .text(iD.util.displayName)
32071             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
32072
32073         texts.exit().remove();
32074         return texts;
32075     }
32076
32077     function drawAreaLabels(group, entities, filter, classes, labels) {
32078         entities = entities.filter(hasText);
32079         labels = labels.filter(hasText);
32080         return drawPointLabels(group, entities, filter, classes, labels);
32081
32082         function hasText(d, i) {
32083             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
32084         }
32085     }
32086
32087     function drawAreaIcons(group, entities, filter, classes, labels) {
32088         var icons = group.selectAll('use')
32089             .filter(filter)
32090             .data(entities, iD.Entity.key);
32091
32092         icons.enter()
32093             .append('use')
32094             .attr('class', 'icon areaicon')
32095             .attr('width', '18px')
32096             .attr('height', '18px');
32097
32098         icons.attr('transform', get(labels, 'transform'))
32099             .attr('xlink:href', function(d) {
32100                 var icon = context.presets().match(d, context.graph()).icon;
32101                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
32102             });
32103
32104
32105         icons.exit().remove();
32106     }
32107
32108     function reverse(p) {
32109         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
32110         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
32111     }
32112
32113     function lineString(nodes) {
32114         return 'M' + nodes.join('L');
32115     }
32116
32117     function subpath(nodes, from, to) {
32118         function segmentLength(i) {
32119             var dx = nodes[i][0] - nodes[i + 1][0];
32120             var dy = nodes[i][1] - nodes[i + 1][1];
32121             return Math.sqrt(dx * dx + dy * dy);
32122         }
32123
32124         var sofar = 0,
32125             start, end, i0, i1;
32126         for (var i = 0; i < nodes.length - 1; i++) {
32127             var current = segmentLength(i);
32128             var portion;
32129             if (!start && sofar + current >= from) {
32130                 portion = (from - sofar) / current;
32131                 start = [
32132                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
32133                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
32134                 ];
32135                 i0 = i + 1;
32136             }
32137             if (!end && sofar + current >= to) {
32138                 portion = (to - sofar) / current;
32139                 end = [
32140                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
32141                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
32142                 ];
32143                 i1 = i + 1;
32144             }
32145             sofar += current;
32146
32147         }
32148         var ret = nodes.slice(i0, i1);
32149         ret.unshift(start);
32150         ret.push(end);
32151         return ret;
32152
32153     }
32154
32155     function hideOnMouseover() {
32156         var layers = d3.select(this)
32157             .selectAll('.layer-label, .layer-halo');
32158
32159         layers.selectAll('.proximate')
32160             .classed('proximate', false);
32161
32162         var mouse = context.mouse(),
32163             pad = 50,
32164             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
32165             ids = _.map(rtree.search(rect), 'id');
32166
32167         if (!ids.length) return;
32168         layers.selectAll('.' + ids.join(', .'))
32169             .classed('proximate', true);
32170     }
32171
32172     var rtree = rbush(),
32173         rectangles = {};
32174
32175     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
32176         var hidePoints = !surface.selectAll('.node.point').node();
32177
32178         var labelable = [], i, k, entity;
32179         for (i = 0; i < label_stack.length; i++) labelable.push([]);
32180
32181         if (fullRedraw) {
32182             rtree.clear();
32183             rectangles = {};
32184         } else {
32185             for (i = 0; i < entities.length; i++) {
32186                 rtree.remove(rectangles[entities[i].id]);
32187             }
32188         }
32189
32190         // Split entities into groups specified by label_stack
32191         for (i = 0; i < entities.length; i++) {
32192             entity = entities[i];
32193             var geometry = entity.geometry(graph);
32194
32195             if (geometry === 'vertex')
32196                 continue;
32197             if (hidePoints && geometry === 'point')
32198                 continue;
32199
32200             var preset = geometry === 'area' && context.presets().match(entity, graph),
32201                 icon = preset && !blacklisted(preset) && preset.icon;
32202
32203             if (!icon && !iD.util.displayName(entity))
32204                 continue;
32205
32206             for (k = 0; k < label_stack.length; k++) {
32207                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
32208                     labelable[k].push(entity);
32209                     break;
32210                 }
32211             }
32212         }
32213
32214         var positions = {
32215             point: [],
32216             line: [],
32217             area: []
32218         };
32219
32220         var labelled = {
32221             point: [],
32222             line: [],
32223             area: []
32224         };
32225
32226         // Try and find a valid label for labellable entities
32227         for (k = 0; k < labelable.length; k++) {
32228             var font_size = font_sizes[k];
32229             for (i = 0; i < labelable[k].length; i++) {
32230                 entity = labelable[k][i];
32231                 var name = iD.util.displayName(entity),
32232                     width = name && textWidth(name, font_size),
32233                     p;
32234                 if (entity.geometry(graph) === 'point') {
32235                     p = getPointLabel(entity, width, font_size);
32236                 } else if (entity.geometry(graph) === 'line') {
32237                     p = getLineLabel(entity, width, font_size);
32238                 } else if (entity.geometry(graph) === 'area') {
32239                     p = getAreaLabel(entity, width, font_size);
32240                 }
32241                 if (p) {
32242                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
32243                     positions[entity.geometry(graph)].push(p);
32244                     labelled[entity.geometry(graph)].push(entity);
32245                 }
32246             }
32247         }
32248
32249         function getPointLabel(entity, width, height) {
32250             var coord = projection(entity.loc),
32251                 m = 5,  // margin
32252                 offset = pointOffsets[0],
32253                 p = {
32254                     height: height,
32255                     width: width,
32256                     x: coord[0] + offset[0],
32257                     y: coord[1] + offset[1],
32258                     textAnchor: offset[2]
32259                 };
32260             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
32261             if (tryInsert(rect, entity.id)) return p;
32262         }
32263
32264
32265         function getLineLabel(entity, width, height) {
32266             var nodes = _.map(graph.childNodes(entity), 'loc').map(projection),
32267                 length = iD.geo.pathLength(nodes);
32268             if (length < width + 20) return;
32269
32270             for (var i = 0; i < lineOffsets.length; i++) {
32271                 var offset = lineOffsets[i],
32272                     middle = offset / 100 * length,
32273                     start = middle - width/2;
32274                 if (start < 0 || start + width > length) continue;
32275                 var sub = subpath(nodes, start, start + width),
32276                     rev = reverse(sub),
32277                     rect = [
32278                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
32279                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
32280                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
32281                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
32282                     ];
32283                 if (rev) sub = sub.reverse();
32284                 if (tryInsert(rect, entity.id)) return {
32285                     'font-size': height + 2,
32286                     lineString: lineString(sub),
32287                     startOffset: offset + '%'
32288                 };
32289             }
32290         }
32291
32292         function getAreaLabel(entity, width, height) {
32293             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
32294                 extent = entity.extent(graph),
32295                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
32296                 rect;
32297
32298             if (isNaN(centroid[0]) || entitywidth < 20) return;
32299
32300             var iconX = centroid[0] - (iconSize/2),
32301                 iconY = centroid[1] - (iconSize/2),
32302                 textOffset = iconSize + 5;
32303
32304             var p = {
32305                 transform: 'translate(' + iconX + ',' + iconY + ')'
32306             };
32307
32308             if (width && entitywidth >= width + 20) {
32309                 p.x = centroid[0];
32310                 p.y = centroid[1] + textOffset;
32311                 p.textAnchor = 'middle';
32312                 p.height = height;
32313                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
32314             } else {
32315                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
32316             }
32317
32318             if (tryInsert(rect, entity.id)) return p;
32319
32320         }
32321
32322         function tryInsert(rect, id) {
32323             // Check that label is visible
32324             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
32325                 rect[3] > dimensions[1]) return false;
32326             var v = rtree.search(rect).length === 0;
32327             if (v) {
32328                 rect.id = id;
32329                 rtree.insert(rect);
32330                 rectangles[id] = rect;
32331             }
32332             return v;
32333         }
32334
32335         var label = surface.selectAll('.layer-label'),
32336             halo = surface.selectAll('.layer-halo');
32337
32338         // points
32339         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
32340         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
32341
32342         // lines
32343         drawLinePaths(halo, labelled.line, filter, '', positions.line);
32344         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
32345         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
32346
32347         // areas
32348         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
32349         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
32350         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
32351
32352         // debug
32353         var showDebug = context.debugCollision();
32354         var debug = label.selectAll('.layer-label-debug')
32355             .data(showDebug ? [true] : []);
32356
32357         debug.enter()
32358             .append('g')
32359             .attr('class', 'layer-label-debug');
32360
32361         debug.exit()
32362             .remove();
32363
32364         if (showDebug) {
32365             var gj = rtree.all().map(function(d) {
32366                 return { type: 'Polygon', coordinates: [[
32367                     [d[0], d[1]],
32368                     [d[2], d[1]],
32369                     [d[2], d[3]],
32370                     [d[0], d[3]],
32371                     [d[0], d[1]]
32372                 ]]};
32373             });
32374
32375             var debugboxes = debug.selectAll('.bbox').data(gj);
32376
32377             debugboxes.enter()
32378                 .append('path')
32379                 .attr('class', 'bbox');
32380
32381             debugboxes.exit()
32382                 .remove();
32383
32384             debugboxes
32385                 .attr('d', d3.geo.path().projection(null));
32386         }
32387     }
32388
32389     drawLabels.supersurface = function(supersurface) {
32390         supersurface
32391             .on('mousemove.hidelabels', hideOnMouseover)
32392             .on('mousedown.hidelabels', function () {
32393                 supersurface.on('mousemove.hidelabels', null);
32394             })
32395             .on('mouseup.hidelabels', function () {
32396                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
32397             });
32398     };
32399
32400     return drawLabels;
32401 };
32402 iD.svg.Layers = function(projection, context) {
32403     var dispatch = d3.dispatch('change'),
32404         svg = d3.select(null),
32405         layers = [
32406             { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
32407             { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
32408             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
32409             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context, dispatch) }
32410         ];
32411
32412
32413     function drawLayers(selection) {
32414         svg = selection.selectAll('.surface')
32415             .data([0]);
32416
32417         svg.enter()
32418             .append('svg')
32419             .attr('class', 'surface')
32420             .append('defs');
32421
32422         var groups = svg.selectAll('.data-layer')
32423             .data(layers);
32424
32425         groups.enter()
32426             .append('g')
32427             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
32428
32429         groups
32430             .each(function(d) { d3.select(this).call(d.layer); });
32431
32432         groups.exit()
32433             .remove();
32434     }
32435
32436     drawLayers.all = function() {
32437         return layers;
32438     };
32439
32440     drawLayers.layer = function(id) {
32441         var obj = _.find(layers, function(o) {return o.id === id;});
32442         return obj && obj.layer;
32443     };
32444
32445     drawLayers.only = function(what) {
32446         var arr = [].concat(what);
32447         drawLayers.remove(_.difference(_.map(layers, 'id'), arr));
32448         return this;
32449     };
32450
32451     drawLayers.remove = function(what) {
32452         var arr = [].concat(what);
32453         arr.forEach(function(id) {
32454             layers = _.reject(layers, function(o) {return o.id === id;});
32455         });
32456         dispatch.change();
32457         return this;
32458     };
32459
32460     drawLayers.add = function(what) {
32461         var arr = [].concat(what);
32462         arr.forEach(function(obj) {
32463             if ('id' in obj && 'layer' in obj) {
32464                 layers.push(obj);
32465             }
32466         });
32467         dispatch.change();
32468         return this;
32469     };
32470
32471     drawLayers.dimensions = function(_) {
32472         if (!arguments.length) return svg.dimensions();
32473         svg.dimensions(_);
32474         layers.forEach(function(obj) {
32475             if (obj.layer.dimensions) {
32476                 obj.layer.dimensions(_);
32477             }
32478         });
32479         return this;
32480     };
32481
32482
32483     return d3.rebind(drawLayers, dispatch, 'on');
32484 };
32485 iD.svg.Lines = function(projection) {
32486
32487     var highway_stack = {
32488         motorway: 0,
32489         motorway_link: 1,
32490         trunk: 2,
32491         trunk_link: 3,
32492         primary: 4,
32493         primary_link: 5,
32494         secondary: 6,
32495         tertiary: 7,
32496         unclassified: 8,
32497         residential: 9,
32498         service: 10,
32499         footway: 11
32500     };
32501
32502     function waystack(a, b) {
32503         var as = 0, bs = 0;
32504
32505         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
32506         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
32507         return as - bs;
32508     }
32509
32510     return function drawLines(surface, graph, entities, filter) {
32511         var ways = [], pathdata = {}, onewaydata = {},
32512             getPath = iD.svg.Path(projection, graph);
32513
32514         for (var i = 0; i < entities.length; i++) {
32515             var entity = entities[i],
32516                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
32517             if (outer) {
32518                 ways.push(entity.mergeTags(outer.tags));
32519             } else if (entity.geometry(graph) === 'line') {
32520                 ways.push(entity);
32521             }
32522         }
32523
32524         ways = ways.filter(getPath);
32525
32526         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
32527
32528         _.forOwn(pathdata, function(v, k) {
32529             onewaydata[k] = _(v)
32530                 .filter(function(d) { return d.isOneWay(); })
32531                 .map(iD.svg.OneWaySegments(projection, graph, 35))
32532                 .flatten()
32533                 .valueOf();
32534         });
32535
32536         var layergroup = surface
32537             .selectAll('.layer-lines')
32538             .selectAll('g.layergroup')
32539             .data(d3.range(-10, 11));
32540
32541         layergroup.enter()
32542             .append('g')
32543             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
32544
32545
32546         var linegroup = layergroup
32547             .selectAll('g.linegroup')
32548             .data(['shadow', 'casing', 'stroke']);
32549
32550         linegroup.enter()
32551             .append('g')
32552             .attr('class', function(d) { return 'layer linegroup line-' + d; });
32553
32554
32555         var lines = linegroup
32556             .selectAll('path')
32557             .filter(filter)
32558             .data(
32559                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
32560                 iD.Entity.key
32561             );
32562
32563         // Optimization: call simple TagClasses only on enter selection. This
32564         // works because iD.Entity.key is defined to include the entity v attribute.
32565         lines.enter()
32566             .append('path')
32567             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
32568             .call(iD.svg.TagClasses());
32569
32570         lines
32571             .sort(waystack)
32572             .attr('d', getPath)
32573             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
32574
32575         lines.exit()
32576             .remove();
32577
32578
32579         var onewaygroup = layergroup
32580             .selectAll('g.onewaygroup')
32581             .data(['oneway']);
32582
32583         onewaygroup.enter()
32584             .append('g')
32585             .attr('class', 'layer onewaygroup');
32586
32587
32588         var oneways = onewaygroup
32589             .selectAll('path')
32590             .filter(filter)
32591             .data(
32592                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
32593                 function(d) { return [d.id, d.index]; }
32594             );
32595
32596         oneways.enter()
32597             .append('path')
32598             .attr('class', 'oneway')
32599             .attr('marker-mid', 'url(#oneway-marker)');
32600
32601         oneways
32602             .attr('d', function(d) { return d.d; });
32603
32604         if (iD.detect().ie) {
32605             oneways.each(function() { this.parentNode.insertBefore(this, this); });
32606         }
32607
32608         oneways.exit()
32609             .remove();
32610
32611     };
32612 };
32613 iD.svg.MapillaryImages = function(projection, context, dispatch) {
32614     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
32615         minZoom = 12,
32616         layer = d3.select(null),
32617         _mapillary;
32618
32619
32620     function init() {
32621         if (iD.svg.MapillaryImages.initialized) return;  // run once
32622         iD.svg.MapillaryImages.enabled = false;
32623         iD.svg.MapillaryImages.initialized = true;
32624     }
32625
32626     function getMapillary() {
32627         if (iD.services.mapillary && !_mapillary) {
32628             _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
32629         } else if (!iD.services.mapillary && _mapillary) {
32630             _mapillary = null;
32631         }
32632         return _mapillary;
32633     }
32634
32635     function showThumbnail(image) {
32636         var mapillary = getMapillary();
32637         if (!mapillary) return;
32638
32639         var thumb = mapillary.selectedThumbnail(),
32640             posX = projection(image.loc)[0],
32641             width = layer.dimensions()[0],
32642             position = (posX < width / 2) ? 'right' : 'left';
32643
32644         if (thumb) {
32645             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
32646                 .classed('selected', function(d) { return d.key === thumb.key; });
32647         }
32648
32649         mapillary.showThumbnail(image.key, position);
32650     }
32651
32652     function hideThumbnail() {
32653         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
32654             .classed('selected', false);
32655
32656         var mapillary = getMapillary();
32657         if (mapillary) {
32658             mapillary.hideThumbnail();
32659         }
32660     }
32661
32662     function showLayer() {
32663         editOn();
32664         layer
32665             .style('opacity', 0)
32666             .transition()
32667             .duration(500)
32668             .style('opacity', 1)
32669             .each('end', debouncedRedraw);
32670     }
32671
32672     function hideLayer() {
32673         debouncedRedraw.cancel();
32674         hideThumbnail();
32675         layer
32676             .transition()
32677             .duration(500)
32678             .style('opacity', 0)
32679             .each('end', editOff);
32680     }
32681
32682     function editOn() {
32683         layer.style('display', 'block');
32684     }
32685
32686     function editOff() {
32687         layer.selectAll('.viewfield-group').remove();
32688         layer.style('display', 'none');
32689     }
32690
32691     function transform(d) {
32692         var t = iD.svg.PointTransform(projection)(d);
32693         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
32694         return t;
32695     }
32696
32697     function update() {
32698         var mapillary = getMapillary(),
32699             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
32700
32701         var markers = layer.selectAll('.viewfield-group')
32702             .data(data, function(d) { return d.key; });
32703
32704         // Enter
32705         var enter = markers.enter()
32706             .append('g')
32707             .attr('class', 'viewfield-group');
32708
32709         enter.append('path')
32710             .attr('class', 'viewfield')
32711             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
32712             .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');
32713
32714         enter.append('circle')
32715             .attr('dx', '0')
32716             .attr('dy', '0')
32717             .attr('r', '6');
32718
32719         // Exit
32720         markers.exit()
32721             .remove();
32722
32723         // Update
32724         markers
32725             .attr('transform', transform);
32726     }
32727
32728     function drawImages(selection) {
32729         var enabled = iD.svg.MapillaryImages.enabled,
32730             mapillary = getMapillary();
32731
32732         layer = selection.selectAll('.layer-mapillary-images')
32733             .data(mapillary ? [0] : []);
32734
32735         layer.enter()
32736             .append('g')
32737             .attr('class', 'layer-mapillary-images')
32738             .style('display', enabled ? 'block' : 'none')
32739             .on('click', function() {   // deselect/select
32740                 var mapillary = getMapillary();
32741                 if (!mapillary) return;
32742                 var d = d3.event.target.__data__,
32743                     thumb = mapillary.selectedThumbnail();
32744                 if (thumb && thumb.key === d.key) {
32745                     hideThumbnail();
32746                 } else {
32747                     mapillary.selectedThumbnail(d);
32748                     context.map().centerEase(d.loc);
32749                     showThumbnail(d);
32750                 }
32751             })
32752             .on('mouseover', function() {
32753                 var mapillary = getMapillary();
32754                 if (!mapillary) return;
32755                 showThumbnail(d3.event.target.__data__);
32756             })
32757             .on('mouseout', function() {
32758                 var mapillary = getMapillary();
32759                 if (!mapillary) return;
32760                 var thumb = mapillary.selectedThumbnail();
32761                 if (thumb) {
32762                     showThumbnail(thumb);
32763                 } else {
32764                     hideThumbnail();
32765                 }
32766             });
32767
32768         layer.exit()
32769             .remove();
32770
32771         if (enabled) {
32772             if (mapillary && ~~context.map().zoom() >= minZoom) {
32773                 editOn();
32774                 update();
32775                 mapillary.loadImages(projection, layer.dimensions());
32776             } else {
32777                 editOff();
32778             }
32779         }
32780     }
32781
32782     drawImages.enabled = function(_) {
32783         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
32784         iD.svg.MapillaryImages.enabled = _;
32785         if (iD.svg.MapillaryImages.enabled) {
32786             showLayer();
32787         } else {
32788             hideLayer();
32789         }
32790         dispatch.change();
32791         return this;
32792     };
32793
32794     drawImages.supported = function() {
32795         return !!getMapillary();
32796     };
32797
32798     drawImages.dimensions = function(_) {
32799         if (!arguments.length) return layer.dimensions();
32800         layer.dimensions(_);
32801         return this;
32802     };
32803
32804     init();
32805     return drawImages;
32806 };
32807 iD.svg.MapillarySigns = function(projection, context, dispatch) {
32808     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
32809         minZoom = 12,
32810         layer = d3.select(null),
32811         _mapillary;
32812
32813
32814     function init() {
32815         if (iD.svg.MapillarySigns.initialized) return;  // run once
32816         iD.svg.MapillarySigns.enabled = false;
32817         iD.svg.MapillarySigns.initialized = true;
32818     }
32819
32820     function getMapillary() {
32821         if (iD.services.mapillary && !_mapillary) {
32822             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
32823         } else if (!iD.services.mapillary && _mapillary) {
32824             _mapillary = null;
32825         }
32826         return _mapillary;
32827     }
32828
32829     function showThumbnail(image) {
32830         var mapillary = getMapillary();
32831         if (!mapillary) return;
32832
32833         var thumb = mapillary.selectedThumbnail(),
32834             posX = projection(image.loc)[0],
32835             width = layer.dimensions()[0],
32836             position = (posX < width / 2) ? 'right' : 'left';
32837
32838         if (thumb) {
32839             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
32840                 .classed('selected', function(d) { return d.key === thumb.key; });
32841         }
32842
32843         mapillary.showThumbnail(image.key, position);
32844     }
32845
32846     function hideThumbnail() {
32847         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
32848             .classed('selected', false);
32849
32850         var mapillary = getMapillary();
32851         if (mapillary) {
32852             mapillary.hideThumbnail();
32853         }
32854     }
32855
32856     function showLayer() {
32857         editOn();
32858         debouncedRedraw();
32859     }
32860
32861     function hideLayer() {
32862         debouncedRedraw.cancel();
32863         hideThumbnail();
32864         editOff();
32865     }
32866
32867     function editOn() {
32868         layer.style('display', 'block');
32869     }
32870
32871     function editOff() {
32872         layer.selectAll('.icon-sign').remove();
32873         layer.style('display', 'none');
32874     }
32875
32876     function update() {
32877         var mapillary = getMapillary(),
32878             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
32879
32880         var signs = layer.selectAll('.icon-sign')
32881             .data(data, function(d) { return d.key; });
32882
32883         // Enter
32884         var enter = signs.enter()
32885             .append('foreignObject')
32886             .attr('class', 'icon-sign')
32887             .attr('width', '32px')      // for Firefox
32888             .attr('height', '32px');    // for Firefox
32889
32890         enter
32891             .append('xhtml:body')
32892             .html(mapillary.signHTML);
32893
32894         enter
32895             .on('click', function(d) {   // deselect/select
32896                 var mapillary = getMapillary();
32897                 if (!mapillary) return;
32898                 var thumb = mapillary.selectedThumbnail();
32899                 if (thumb && thumb.key === d.key) {
32900                     hideThumbnail();
32901                 } else {
32902                     mapillary.selectedThumbnail(d);
32903                     context.map().centerEase(d.loc);
32904                     showThumbnail(d);
32905                 }
32906             })
32907             .on('mouseover', showThumbnail)
32908             .on('mouseout', function() {
32909                 var mapillary = getMapillary();
32910                 if (!mapillary) return;
32911                 var thumb = mapillary.selectedThumbnail();
32912                 if (thumb) {
32913                     showThumbnail(thumb);
32914                 } else {
32915                     hideThumbnail();
32916                 }
32917             });
32918
32919         // Exit
32920         signs.exit()
32921             .remove();
32922
32923         // Update
32924         signs
32925             .attr('transform', iD.svg.PointTransform(projection));
32926     }
32927
32928     function drawSigns(selection) {
32929         var enabled = iD.svg.MapillarySigns.enabled,
32930             mapillary = getMapillary();
32931
32932         layer = selection.selectAll('.layer-mapillary-signs')
32933             .data(mapillary ? [0] : []);
32934
32935         layer.enter()
32936             .append('g')
32937             .attr('class', 'layer-mapillary-signs')
32938             .style('display', enabled ? 'block' : 'none')
32939             .attr('transform', 'translate(-16, -16)');  // center signs on loc
32940
32941         layer.exit()
32942             .remove();
32943
32944         if (enabled) {
32945             if (mapillary && ~~context.map().zoom() >= minZoom) {
32946                 editOn();
32947                 update();
32948                 mapillary.loadSigns(context, projection, layer.dimensions());
32949             } else {
32950                 editOff();
32951             }
32952         }
32953     }
32954
32955     drawSigns.enabled = function(_) {
32956         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
32957         iD.svg.MapillarySigns.enabled = _;
32958         if (iD.svg.MapillarySigns.enabled) {
32959             showLayer();
32960         } else {
32961             hideLayer();
32962         }
32963         dispatch.change();
32964         return this;
32965     };
32966
32967     drawSigns.supported = function() {
32968         var mapillary = getMapillary();
32969         return (mapillary && mapillary.signsSupported());
32970     };
32971
32972     drawSigns.dimensions = function(_) {
32973         if (!arguments.length) return layer.dimensions();
32974         layer.dimensions(_);
32975         return this;
32976     };
32977
32978     init();
32979     return drawSigns;
32980 };
32981 iD.svg.Midpoints = function(projection, context) {
32982     return function drawMidpoints(surface, graph, entities, filter, extent) {
32983         var poly = extent.polygon(),
32984             midpoints = {};
32985
32986         for (var i = 0; i < entities.length; i++) {
32987             var entity = entities[i];
32988
32989             if (entity.type !== 'way')
32990                 continue;
32991             if (!filter(entity))
32992                 continue;
32993             if (context.selectedIDs().indexOf(entity.id) < 0)
32994                 continue;
32995
32996             var nodes = graph.childNodes(entity);
32997             for (var j = 0; j < nodes.length - 1; j++) {
32998
32999                 var a = nodes[j],
33000                     b = nodes[j + 1],
33001                     id = [a.id, b.id].sort().join('-');
33002
33003                 if (midpoints[id]) {
33004                     midpoints[id].parents.push(entity);
33005                 } else {
33006                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
33007                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
33008                             loc = null;
33009
33010                         if (extent.intersects(point)) {
33011                             loc = point;
33012                         } else {
33013                             for (var k = 0; k < 4; k++) {
33014                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
33015                                 if (point &&
33016                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
33017                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
33018                                 {
33019                                     loc = point;
33020                                     break;
33021                                 }
33022                             }
33023                         }
33024
33025                         if (loc) {
33026                             midpoints[id] = {
33027                                 type: 'midpoint',
33028                                 id: id,
33029                                 loc: loc,
33030                                 edge: [a.id, b.id],
33031                                 parents: [entity]
33032                             };
33033                         }
33034                     }
33035                 }
33036             }
33037         }
33038
33039         function midpointFilter(d) {
33040             if (midpoints[d.id])
33041                 return true;
33042
33043             for (var i = 0; i < d.parents.length; i++)
33044                 if (filter(d.parents[i]))
33045                     return true;
33046
33047             return false;
33048         }
33049
33050         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
33051             .filter(midpointFilter)
33052             .data(_.values(midpoints), function(d) { return d.id; });
33053
33054         var enter = groups.enter()
33055             .insert('g', ':first-child')
33056             .attr('class', 'midpoint');
33057
33058         enter.append('polygon')
33059             .attr('points', '-6,8 10,0 -6,-8')
33060             .attr('class', 'shadow');
33061
33062         enter.append('polygon')
33063             .attr('points', '-3,4 5,0 -3,-4')
33064             .attr('class', 'fill');
33065
33066         groups
33067             .attr('transform', function(d) {
33068                 var translate = iD.svg.PointTransform(projection),
33069                     a = context.entity(d.edge[0]),
33070                     b = context.entity(d.edge[1]),
33071                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
33072                 return translate(d) + ' rotate(' + angle + ')';
33073             })
33074             .call(iD.svg.TagClasses().tags(
33075                 function(d) { return d.parents[0].tags; }
33076             ));
33077
33078         // Propagate data bindings.
33079         groups.select('polygon.shadow');
33080         groups.select('polygon.fill');
33081
33082         groups.exit()
33083             .remove();
33084     };
33085 };
33086 iD.svg.Osm = function() {
33087     return function drawOsm(selection) {
33088         var layers = selection.selectAll('.layer-osm')
33089             .data(['areas', 'lines', 'hit', 'halo', 'label']);
33090
33091         layers.enter().append('g')
33092             .attr('class', function(d) { return 'layer-osm layer-' + d; });
33093     };
33094 };
33095 iD.svg.Points = function(projection, context) {
33096     function markerPath(selection, klass) {
33097         selection
33098             .attr('class', klass)
33099             .attr('transform', 'translate(-8, -23)')
33100             .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');
33101     }
33102
33103     function sortY(a, b) {
33104         return b.loc[1] - a.loc[1];
33105     }
33106
33107     return function drawPoints(surface, graph, entities, filter) {
33108         var wireframe = surface.classed('fill-wireframe'),
33109             points = wireframe ? [] : _.filter(entities, function(e) {
33110                 return e.geometry(graph) === 'point';
33111             });
33112
33113         points.sort(sortY);
33114
33115         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
33116             .filter(filter)
33117             .data(points, iD.Entity.key);
33118
33119         var group = groups.enter()
33120             .append('g')
33121             .attr('class', function(d) { return 'node point ' + d.id; })
33122             .order();
33123
33124         group.append('path')
33125             .call(markerPath, 'shadow');
33126
33127         group.append('path')
33128             .call(markerPath, 'stroke');
33129
33130         group.append('use')
33131             .attr('transform', 'translate(-6, -20)')
33132             .attr('class', 'icon')
33133             .attr('width', '12px')
33134             .attr('height', '12px');
33135
33136         groups.attr('transform', iD.svg.PointTransform(projection))
33137             .call(iD.svg.TagClasses());
33138
33139         // Selecting the following implicitly
33140         // sets the data (point entity) on the element
33141         groups.select('.shadow');
33142         groups.select('.stroke');
33143         groups.select('.icon')
33144             .attr('xlink:href', function(entity) {
33145                 var preset = context.presets().match(entity, graph);
33146                 return preset.icon ? '#' + preset.icon + '-12' : '';
33147             });
33148
33149         groups.exit()
33150             .remove();
33151     };
33152 };
33153 iD.svg.TagClasses = function() {
33154     var primaries = [
33155             'building', 'highway', 'railway', 'waterway', 'aeroway',
33156             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
33157             'leisure', 'place'
33158         ],
33159         statuses = [
33160             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
33161             'razed', 'demolished', 'obliterated'
33162         ],
33163         secondaries = [
33164             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
33165             'surface', 'tracktype', 'crossing'
33166         ],
33167         tagClassRe = /^tag-/,
33168         tags = function(entity) { return entity.tags; };
33169
33170
33171     var tagClasses = function(selection) {
33172         selection.each(function tagClassesEach(entity) {
33173             var value = this.className,
33174                 classes, primary, status;
33175
33176             if (value.baseVal !== undefined) value = value.baseVal;
33177
33178             classes = value.trim().split(/\s+/).filter(function(name) {
33179                 return name.length && !tagClassRe.test(name);
33180             }).join(' ');
33181
33182             var t = tags(entity), i, k, v;
33183
33184             // pick at most one primary classification tag..
33185             for (i = 0; i < primaries.length; i++) {
33186                 k = primaries[i];
33187                 v = t[k];
33188                 if (!v || v === 'no') continue;
33189
33190                 primary = k;
33191                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
33192                     status = v;
33193                     classes += ' tag-' + k;
33194                 } else {
33195                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
33196                 }
33197
33198                 break;
33199             }
33200
33201             // add at most one status tag, only if relates to primary tag..
33202             if (!status) {
33203                 for (i = 0; i < statuses.length; i++) {
33204                     k = statuses[i];
33205                     v = t[k];
33206                     if (!v || v === 'no') continue;
33207
33208                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
33209                         status = k;
33210                     }
33211                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
33212                         status = k;
33213                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
33214                         status = k;
33215                         primary = v;
33216                         classes += ' tag-' + v;
33217                     }  // else ignore e.g.  `highway=path + abandoned=railway`
33218
33219                     if (status) break;
33220                 }
33221             }
33222
33223             if (status) {
33224                 classes += ' tag-status tag-status-' + status;
33225             }
33226
33227             // add any secondary (structure) tags
33228             for (i = 0; i < secondaries.length; i++) {
33229                 k = secondaries[i];
33230                 v = t[k];
33231                 if (!v || v === 'no') continue;
33232                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
33233             }
33234
33235             // For highways, look for surface tagging..
33236             if (primary === 'highway') {
33237                 var paved = (t.highway !== 'track');
33238                 for (k in t) {
33239                     v = t[k];
33240                     if (k in iD.pavedTags) {
33241                         paved = !!iD.pavedTags[k][v];
33242                         break;
33243                     }
33244                 }
33245                 if (!paved) {
33246                     classes += ' tag-unpaved';
33247                 }
33248             }
33249
33250             classes = classes.trim();
33251
33252             if (classes !== value) {
33253                 d3.select(this).attr('class', classes);
33254             }
33255         });
33256     };
33257
33258     tagClasses.tags = function(_) {
33259         if (!arguments.length) return tags;
33260         tags = _;
33261         return tagClasses;
33262     };
33263
33264     return tagClasses;
33265 };
33266 iD.svg.Turns = function(projection) {
33267     return function drawTurns(surface, graph, turns) {
33268         function key(turn) {
33269             return [turn.from.node + turn.via.node + turn.to.node].join('-');
33270         }
33271
33272         function icon(turn) {
33273             var u = turn.u ? '-u' : '';
33274             if (!turn.restriction)
33275                 return '#turn-yes' + u;
33276             var restriction = graph.entity(turn.restriction).tags.restriction;
33277             return '#turn-' +
33278                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
33279         }
33280
33281         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
33282             .data(turns, key);
33283
33284         // Enter
33285         var enter = groups.enter().append('g')
33286             .attr('class', 'turn');
33287
33288         var nEnter = enter.filter(function (turn) { return !turn.u; });
33289
33290         nEnter.append('rect')
33291             .attr('transform', 'translate(-22, -12)')
33292             .attr('width', '44')
33293             .attr('height', '24');
33294
33295         nEnter.append('use')
33296             .attr('transform', 'translate(-22, -12)')
33297             .attr('width', '44')
33298             .attr('height', '24');
33299
33300
33301         var uEnter = enter.filter(function (turn) { return turn.u; });
33302
33303         uEnter.append('circle')
33304             .attr('r', '16');
33305
33306         uEnter.append('use')
33307             .attr('transform', 'translate(-16, -16)')
33308             .attr('width', '32')
33309             .attr('height', '32');
33310
33311
33312         // Update
33313         groups
33314             .attr('transform', function (turn) {
33315                 var v = graph.entity(turn.via.node),
33316                     t = graph.entity(turn.to.node),
33317                     a = iD.geo.angle(v, t, projection),
33318                     p = projection(v.loc),
33319                     r = turn.u ? 0 : 60;
33320
33321                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
33322                     'rotate(' + a * 180 / Math.PI + ')';
33323             });
33324
33325         groups.select('use')
33326             .attr('xlink:href', icon);
33327
33328         groups.select('rect');
33329         groups.select('circle');
33330
33331
33332         // Exit
33333         groups.exit()
33334             .remove();
33335
33336         return this;
33337     };
33338 };
33339 iD.svg.Vertices = function(projection, context) {
33340     var radiuses = {
33341         //       z16-, z17, z18+, tagged
33342         shadow: [6,    7.5,   7.5,  11.5],
33343         stroke: [2.5,  3.5,   3.5,  7],
33344         fill:   [1,    1.5,   1.5,  1.5]
33345     };
33346
33347     var hover;
33348
33349     function siblingAndChildVertices(ids, graph, extent) {
33350         var vertices = {};
33351
33352         function addChildVertices(entity) {
33353             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
33354                 var i;
33355                 if (entity.type === 'way') {
33356                     for (i = 0; i < entity.nodes.length; i++) {
33357                         addChildVertices(graph.entity(entity.nodes[i]));
33358                     }
33359                 } else if (entity.type === 'relation') {
33360                     for (i = 0; i < entity.members.length; i++) {
33361                         var member = context.hasEntity(entity.members[i].id);
33362                         if (member) {
33363                             addChildVertices(member);
33364                         }
33365                     }
33366                 } else if (entity.intersects(extent, graph)) {
33367                     vertices[entity.id] = entity;
33368                 }
33369             }
33370         }
33371
33372         ids.forEach(function(id) {
33373             var entity = context.hasEntity(id);
33374             if (entity && entity.type === 'node') {
33375                 vertices[entity.id] = entity;
33376                 context.graph().parentWays(entity).forEach(function(entity) {
33377                     addChildVertices(entity);
33378                 });
33379             } else if (entity) {
33380                 addChildVertices(entity);
33381             }
33382         });
33383
33384         return vertices;
33385     }
33386
33387     function draw(selection, vertices, klass, graph, zoom) {
33388         var icons = {},
33389             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
33390
33391         var groups = selection
33392             .data(vertices, iD.Entity.key);
33393
33394         function icon(entity) {
33395             if (entity.id in icons) return icons[entity.id];
33396             icons[entity.id] =
33397                 entity.hasInterestingTags() &&
33398                 context.presets().match(entity, graph).icon;
33399             return icons[entity.id];
33400         }
33401
33402         function setClass(klass) {
33403             return function(entity) {
33404                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
33405             };
33406         }
33407
33408         function setAttributes(selection) {
33409             ['shadow','stroke','fill'].forEach(function(klass) {
33410                 var rads = radiuses[klass];
33411                 selection.selectAll('.' + klass)
33412                     .each(function(entity) {
33413                         var i = z && icon(entity),
33414                             c = i ? 0.5 : 0,
33415                             r = rads[i ? 3 : z];
33416                         this.setAttribute('cx', c);
33417                         this.setAttribute('cy', -c);
33418                         this.setAttribute('r', r);
33419                         if (i && klass === 'fill') {
33420                             this.setAttribute('visibility', 'hidden');
33421                         } else {
33422                             this.removeAttribute('visibility');
33423                         }
33424                     });
33425             });
33426
33427             selection.selectAll('use')
33428                 .each(function() {
33429                     if (z) {
33430                         this.removeAttribute('visibility');
33431                     } else {
33432                         this.setAttribute('visibility', 'hidden');
33433                     }
33434                 });
33435         }
33436
33437         var enter = groups.enter()
33438             .append('g')
33439             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
33440
33441         enter.append('circle')
33442             .each(setClass('shadow'));
33443
33444         enter.append('circle')
33445             .each(setClass('stroke'));
33446
33447         // Vertices with icons get a `use`.
33448         enter.filter(function(d) { return icon(d); })
33449             .append('use')
33450             .attr('transform', 'translate(-6, -6)')
33451             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
33452             .attr('width', '12px')
33453             .attr('height', '12px')
33454             .each(setClass('icon'));
33455
33456         // Vertices with tags get a fill.
33457         enter.filter(function(d) { return d.hasInterestingTags(); })
33458             .append('circle')
33459             .each(setClass('fill'));
33460
33461         groups
33462             .attr('transform', iD.svg.PointTransform(projection))
33463             .classed('shared', function(entity) { return graph.isShared(entity); })
33464             .call(setAttributes);
33465
33466         groups.exit()
33467             .remove();
33468     }
33469
33470     function drawVertices(surface, graph, entities, filter, extent, zoom) {
33471         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
33472             wireframe = surface.classed('fill-wireframe'),
33473             vertices = [];
33474
33475         for (var i = 0; i < entities.length; i++) {
33476             var entity = entities[i],
33477                 geometry = entity.geometry(graph);
33478
33479             if (wireframe && geometry === 'point') {
33480                 vertices.push(entity);
33481                 continue;
33482             }
33483
33484             if (geometry !== 'vertex')
33485                 continue;
33486
33487             if (entity.id in selected ||
33488                 entity.hasInterestingTags() ||
33489                 entity.isIntersection(graph)) {
33490                 vertices.push(entity);
33491             }
33492         }
33493
33494         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
33495             .filter(filter)
33496             .call(draw, vertices, 'vertex-persistent', graph, zoom);
33497
33498         drawHover(surface, graph, extent, zoom);
33499     }
33500
33501     function drawHover(surface, graph, extent, zoom) {
33502         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
33503
33504         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
33505             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
33506     }
33507
33508     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
33509         if (target === hover) return;
33510         hover = target;
33511         drawHover(surface, graph, extent, zoom);
33512     };
33513
33514     return drawVertices;
33515 };
33516 iD.ui = function(context) {
33517     function render(container) {
33518         var map = context.map();
33519
33520         if (iD.detect().opera) container.classed('opera', true);
33521
33522         var hash = iD.behavior.Hash(context);
33523
33524         hash();
33525
33526         if (!hash.hadHash) {
33527             map.centerZoom([0, 0], 2);
33528         }
33529
33530         container.append('svg')
33531             .attr('id', 'defs')
33532             .call(iD.svg.Defs(context));
33533
33534         container.append('div')
33535             .attr('id', 'sidebar')
33536             .attr('class', 'col4')
33537             .call(ui.sidebar);
33538
33539         var content = container.append('div')
33540             .attr('id', 'content');
33541
33542         var bar = content.append('div')
33543             .attr('id', 'bar')
33544             .attr('class', 'fillD');
33545
33546         content.append('div')
33547             .attr('id', 'map')
33548             .call(map);
33549
33550         content
33551             .call(iD.ui.MapInMap(context));
33552
33553         content.append('div')
33554             .call(iD.ui.Info(context));
33555
33556         bar.append('div')
33557             .attr('class', 'spacer col4');
33558
33559         var limiter = bar.append('div')
33560             .attr('class', 'limiter');
33561
33562         limiter.append('div')
33563             .attr('class', 'button-wrap joined col3')
33564             .call(iD.ui.Modes(context), limiter);
33565
33566         limiter.append('div')
33567             .attr('class', 'button-wrap joined col1')
33568             .call(iD.ui.UndoRedo(context));
33569
33570         limiter.append('div')
33571             .attr('class', 'button-wrap col1')
33572             .call(iD.ui.Save(context));
33573
33574         bar.append('div')
33575             .attr('class', 'full-screen')
33576             .call(iD.ui.FullScreen(context));
33577
33578         bar.append('div')
33579             .attr('class', 'spinner')
33580             .call(iD.ui.Spinner(context));
33581
33582         var controls = bar.append('div')
33583             .attr('class', 'map-controls');
33584
33585         controls.append('div')
33586             .attr('class', 'map-control zoombuttons')
33587             .call(iD.ui.Zoom(context));
33588
33589         controls.append('div')
33590             .attr('class', 'map-control geolocate-control')
33591             .call(iD.ui.Geolocate(context));
33592
33593         controls.append('div')
33594             .attr('class', 'map-control background-control')
33595             .call(iD.ui.Background(context));
33596
33597         controls.append('div')
33598             .attr('class', 'map-control map-data-control')
33599             .call(iD.ui.MapData(context));
33600
33601         controls.append('div')
33602             .attr('class', 'map-control help-control')
33603             .call(iD.ui.Help(context));
33604
33605         var about = content.append('div')
33606             .attr('id', 'about');
33607
33608         about.append('div')
33609             .attr('id', 'attrib')
33610             .call(iD.ui.Attribution(context));
33611
33612         var footer = about.append('div')
33613             .attr('id', 'footer')
33614             .attr('class', 'fillD');
33615
33616         footer.append('div')
33617             .attr('class', 'api-status')
33618             .call(iD.ui.Status(context));
33619
33620         footer.append('div')
33621             .attr('id', 'scale-block')
33622             .call(iD.ui.Scale(context));
33623
33624         var aboutList = footer.append('div')
33625             .attr('id', 'info-block')
33626             .append('ul')
33627             .attr('id', 'about-list');
33628
33629         if (!context.embed()) {
33630             aboutList.call(iD.ui.Account(context));
33631         }
33632
33633         aboutList.append('li')
33634             .append('a')
33635             .attr('target', '_blank')
33636             .attr('tabindex', -1)
33637             .attr('href', 'https://github.com/openstreetmap/iD')
33638             .text(iD.version);
33639
33640         var issueLinks = aboutList.append('li');
33641
33642         issueLinks.append('a')
33643             .attr('target', '_blank')
33644             .attr('tabindex', -1)
33645             .attr('href', 'https://github.com/openstreetmap/iD/issues')
33646             .call(iD.svg.Icon('#icon-bug', 'light'))
33647             .call(bootstrap.tooltip()
33648                 .title(t('report_a_bug'))
33649                 .placement('top')
33650             );
33651
33652         issueLinks.append('a')
33653             .attr('target', '_blank')
33654             .attr('tabindex', -1)
33655             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
33656             .call(iD.svg.Icon('#icon-translate', 'light'))
33657             .call(bootstrap.tooltip()
33658                 .title(t('help_translate'))
33659                 .placement('top')
33660             );
33661
33662         aboutList.append('li')
33663             .attr('class', 'feature-warning')
33664             .attr('tabindex', -1)
33665             .call(iD.ui.FeatureInfo(context));
33666
33667         aboutList.append('li')
33668             .attr('class', 'user-list')
33669             .attr('tabindex', -1)
33670             .call(iD.ui.Contributors(context));
33671
33672         window.onbeforeunload = function() {
33673             return context.save();
33674         };
33675
33676         window.onunload = function() {
33677             context.history().unlock();
33678         };
33679
33680         var mapDimensions = map.dimensions();
33681
33682         d3.select(window).on('resize.editor', function() {
33683             mapDimensions = content.dimensions(null);
33684             map.dimensions(mapDimensions);
33685         });
33686
33687         function pan(d) {
33688             return function() {
33689                 d3.event.preventDefault();
33690                 if (!context.inIntro()) context.pan(d);
33691             };
33692         }
33693
33694         // pan amount
33695         var pa = 10;
33696
33697         var keybinding = d3.keybinding('main')
33698             .on('⌫', function() { d3.event.preventDefault(); })
33699             .on('←', pan([pa, 0]))
33700             .on('↑', pan([0, pa]))
33701             .on('→', pan([-pa, 0]))
33702             .on('↓', pan([0, -pa]))
33703             .on('⇧←', pan([mapDimensions[0], 0]))
33704             .on('⇧↑', pan([0, mapDimensions[1]]))
33705             .on('⇧→', pan([-mapDimensions[0], 0]))
33706             .on('⇧↓', pan([0, -mapDimensions[1]]))
33707             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
33708             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
33709             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
33710             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
33711
33712         d3.select(document)
33713             .call(keybinding);
33714
33715         context.enter(iD.modes.Browse(context));
33716
33717         context.container()
33718             .call(iD.ui.Splash(context))
33719             .call(iD.ui.Restore(context));
33720
33721         var authenticating = iD.ui.Loading(context)
33722             .message(t('loading_auth'));
33723
33724         context.connection()
33725             .on('authenticating.ui', function() {
33726                 context.container()
33727                     .call(authenticating);
33728             })
33729             .on('authenticated.ui', function() {
33730                 authenticating.close();
33731             });
33732     }
33733
33734     function ui(container) {
33735         context.container(container);
33736         context.loadLocale(function() {
33737             render(container);
33738         });
33739     }
33740
33741     ui.sidebar = iD.ui.Sidebar(context);
33742
33743     return ui;
33744 };
33745
33746 iD.ui.tooltipHtml = function(text, key) {
33747     var s = '<span>' + text + '</span>';
33748     if (key) {
33749         s += '<div class="keyhint-wrap">' +
33750             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
33751             '<span class="keyhint"> ' + key + '</span></div>';
33752     }
33753     return s;
33754 };
33755 iD.ui.Account = function(context) {
33756     var connection = context.connection();
33757
33758     function update(selection) {
33759         if (!connection.authenticated()) {
33760             selection.selectAll('#userLink, #logoutLink')
33761                 .classed('hide', true);
33762             return;
33763         }
33764
33765         connection.userDetails(function(err, details) {
33766             var userLink = selection.select('#userLink'),
33767                 logoutLink = selection.select('#logoutLink');
33768
33769             userLink.html('');
33770             logoutLink.html('');
33771
33772             if (err) return;
33773
33774             selection.selectAll('#userLink, #logoutLink')
33775                 .classed('hide', false);
33776
33777             // Link
33778             userLink.append('a')
33779                 .attr('href', connection.userURL(details.display_name))
33780                 .attr('target', '_blank');
33781
33782             // Add thumbnail or dont
33783             if (details.image_url) {
33784                 userLink.append('img')
33785                     .attr('class', 'icon pre-text user-icon')
33786                     .attr('src', details.image_url);
33787             } else {
33788                 userLink
33789                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
33790             }
33791
33792             // Add user name
33793             userLink.append('span')
33794                 .attr('class', 'label')
33795                 .text(details.display_name);
33796
33797             logoutLink.append('a')
33798                 .attr('class', 'logout')
33799                 .attr('href', '#')
33800                 .text(t('logout'))
33801                 .on('click.logout', function() {
33802                     d3.event.preventDefault();
33803                     connection.logout();
33804                 });
33805         });
33806     }
33807
33808     return function(selection) {
33809         selection.append('li')
33810             .attr('id', 'logoutLink')
33811             .classed('hide', true);
33812
33813         selection.append('li')
33814             .attr('id', 'userLink')
33815             .classed('hide', true);
33816
33817         connection.on('auth.account', function() { update(selection); });
33818         update(selection);
33819     };
33820 };
33821 iD.ui.Attribution = function(context) {
33822     var selection;
33823
33824     function attribution(data, klass) {
33825         var div = selection.selectAll('.' + klass)
33826             .data([0]);
33827
33828         div.enter()
33829             .append('div')
33830             .attr('class', klass);
33831
33832         var background = div.selectAll('.attribution')
33833             .data(data, function(d) { return d.name(); });
33834
33835         background.enter()
33836             .append('span')
33837             .attr('class', 'attribution')
33838             .each(function(d) {
33839                 if (d.terms_html) {
33840                     d3.select(this)
33841                         .html(d.terms_html);
33842                     return;
33843                 }
33844
33845                 var source = d.terms_text || d.id || d.name();
33846
33847                 if (d.logo) {
33848                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
33849                 }
33850
33851                 if (d.terms_url) {
33852                     d3.select(this)
33853                         .append('a')
33854                         .attr('href', d.terms_url)
33855                         .attr('target', '_blank')
33856                         .html(source);
33857                 } else {
33858                     d3.select(this)
33859                         .text(source);
33860                 }
33861             });
33862
33863         background.exit()
33864             .remove();
33865
33866         var copyright = background.selectAll('.copyright-notice')
33867             .data(function(d) {
33868                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
33869                 return notice ? [notice] : [];
33870             });
33871
33872         copyright.enter()
33873             .append('span')
33874             .attr('class', 'copyright-notice');
33875
33876         copyright.text(String);
33877
33878         copyright.exit()
33879             .remove();
33880     }
33881
33882     function update() {
33883         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
33884         attribution(context.background().overlayLayerSources().filter(function (s) {
33885             return s.validZoom(context.map().zoom());
33886         }), 'overlay-layer-attribution');
33887     }
33888
33889     return function(select) {
33890         selection = select;
33891
33892         context.background()
33893             .on('change.attribution', update);
33894
33895         context.map()
33896             .on('move.attribution', _.throttle(update, 400, {leading: false}));
33897
33898         update();
33899     };
33900 };
33901 iD.ui.Background = function(context) {
33902     var key = 'B',
33903         opacities = [1, 0.75, 0.5, 0.25],
33904         directions = [
33905             ['right', [0.5, 0]],
33906             ['top', [0, -0.5]],
33907             ['left', [-0.5, 0]],
33908             ['bottom', [0, 0.5]]],
33909         opacityDefault = (context.storage('background-opacity') !== null) ?
33910             (+context.storage('background-opacity')) : 1.0,
33911         customTemplate = context.storage('background-custom-template') || '',
33912         previous;
33913
33914     // Can be 0 from <1.3.0 use or due to issue #1923.
33915     if (opacityDefault === 0) opacityDefault = 1.0;
33916
33917
33918     function background(selection) {
33919
33920         function sortSources(a, b) {
33921             return a.best() && !b.best() ? -1
33922                 : b.best() && !a.best() ? 1
33923                 : d3.descending(a.area(), b.area()) || d3.ascending(a.name(), b.name()) || 0;
33924         }
33925
33926         function setOpacity(d) {
33927             var bg = context.container().selectAll('.layer-background')
33928                 .transition()
33929                 .style('opacity', d)
33930                 .attr('data-opacity', d);
33931
33932             if (!iD.detect().opera) {
33933                 iD.util.setTransform(bg, 0, 0);
33934             }
33935
33936             opacityList.selectAll('li')
33937                 .classed('active', function(_) { return _ === d; });
33938
33939             context.storage('background-opacity', d);
33940         }
33941
33942         function setTooltips(selection) {
33943             selection.each(function(d) {
33944                 var item = d3.select(this);
33945                 if (d === previous) {
33946                     item.call(bootstrap.tooltip()
33947                         .html(true)
33948                         .title(function() {
33949                             var tip = '<div>' + t('background.switch') + '</div>';
33950                             return iD.ui.tooltipHtml(tip, iD.ui.cmd('⌘B'));
33951                         })
33952                         .placement('top')
33953                     );
33954                 } else if (d.description) {
33955                     item.call(bootstrap.tooltip()
33956                         .title(d.description)
33957                         .placement('top')
33958                     );
33959                 } else {
33960                     item.call(bootstrap.tooltip().destroy);
33961                 }
33962             });
33963         }
33964
33965         function selectLayer() {
33966             function active(d) {
33967                 return context.background().showsLayer(d);
33968             }
33969
33970             content.selectAll('.layer, .custom_layer')
33971                 .classed('active', active)
33972                 .classed('switch', function(d) { return d === previous; })
33973                 .call(setTooltips)
33974                 .selectAll('input')
33975                 .property('checked', active);
33976         }
33977
33978         function clickSetSource(d) {
33979             previous = context.background().baseLayerSource();
33980             d3.event.preventDefault();
33981             context.background().baseLayerSource(d);
33982             selectLayer();
33983             document.activeElement.blur();
33984         }
33985
33986         function editCustom() {
33987             d3.event.preventDefault();
33988             var template = window.prompt(t('background.custom_prompt'), customTemplate);
33989             if (!template ||
33990                 template.indexOf('google.com') !== -1 ||
33991                 template.indexOf('googleapis.com') !== -1 ||
33992                 template.indexOf('google.ru') !== -1) {
33993                 selectLayer();
33994                 return;
33995             }
33996             setCustom(template);
33997         }
33998
33999         function setCustom(template) {
34000             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
34001             selectLayer();
34002             context.storage('background-custom-template', template);
34003         }
34004
34005         function clickSetOverlay(d) {
34006             d3.event.preventDefault();
34007             context.background().toggleOverlayLayer(d);
34008             selectLayer();
34009             document.activeElement.blur();
34010         }
34011
34012         function drawList(layerList, type, change, filter) {
34013             var sources = context.background()
34014                 .sources(context.map().extent())
34015                 .filter(filter);
34016
34017             var layerLinks = layerList.selectAll('li.layer')
34018                 .data(sources, function(d) { return d.name(); });
34019
34020             var enter = layerLinks.enter()
34021                 .insert('li', '.custom_layer')
34022                 .attr('class', 'layer')
34023                 .classed('best', function(d) { return d.best(); });
34024
34025             enter.filter(function(d) { return d.best(); })
34026                 .append('div')
34027                 .attr('class', 'best')
34028                 .call(bootstrap.tooltip()
34029                     .title(t('background.best_imagery'))
34030                     .placement('left'))
34031                 .append('span')
34032                 .html('&#9733;');
34033
34034             var label = enter.append('label');
34035
34036             label.append('input')
34037                 .attr('type', type)
34038                 .attr('name', 'layers')
34039                 .on('change', change);
34040
34041             label.append('span')
34042                 .text(function(d) { return d.name(); });
34043
34044
34045             layerLinks.exit()
34046                 .remove();
34047
34048             layerList.selectAll('li.layer')
34049                 .sort(sortSources)
34050                 .style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
34051         }
34052
34053         function update() {
34054             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
34055             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
34056
34057             selectLayer();
34058
34059             var source = context.background().baseLayerSource();
34060             if (source.id === 'custom') {
34061                 customTemplate = source.template;
34062             }
34063
34064             updateOffsetVal();
34065         }
34066
34067         function updateOffsetVal() {
34068             var meters = iD.geo.offsetToMeters(context.background().offset()),
34069                 x = +meters[0].toFixed(2),
34070                 y = +meters[1].toFixed(2);
34071
34072             d3.selectAll('.nudge-inner-rect')
34073                 .select('input')
34074                 .classed('error', false)
34075                 .property('value', x + ', ' + y);
34076
34077             d3.selectAll('.nudge-reset')
34078                 .classed('disabled', function() {
34079                     return (x === 0 && y === 0);
34080                 });
34081         }
34082
34083         function resetOffset() {
34084             context.background().offset([0, 0]);
34085             updateOffsetVal();
34086         }
34087
34088         function nudge(d) {
34089             context.background().nudge(d, context.map().zoom());
34090             updateOffsetVal();
34091         }
34092
34093         function buttonOffset(d) {
34094             var timeout = window.setTimeout(function() {
34095                     interval = window.setInterval(nudge.bind(null, d), 100);
34096                 }, 500),
34097                 interval;
34098
34099             d3.select(window).on('mouseup', function() {
34100                 window.clearInterval(interval);
34101                 window.clearTimeout(timeout);
34102                 d3.select(window).on('mouseup', null);
34103             });
34104
34105             nudge(d);
34106         }
34107
34108         function inputOffset() {
34109             var input = d3.select(this);
34110             var d = input.node().value;
34111
34112             if (d === '') return resetOffset();
34113
34114             d = d.replace(/;/g, ',').split(',').map(function(n) {
34115                 // if n is NaN, it will always get mapped to false.
34116                 return !isNaN(n) && n;
34117             });
34118
34119             if (d.length !== 2 || !d[0] || !d[1]) {
34120                 input.classed('error', true);
34121                 return;
34122             }
34123
34124             context.background().offset(iD.geo.metersToOffset(d));
34125             updateOffsetVal();
34126         }
34127
34128         function dragOffset() {
34129             var origin = [d3.event.clientX, d3.event.clientY];
34130
34131             context.container()
34132                 .append('div')
34133                 .attr('class', 'nudge-surface');
34134
34135             d3.select(window)
34136                 .on('mousemove.offset', function() {
34137                     var latest = [d3.event.clientX, d3.event.clientY];
34138                     var d = [
34139                         -(origin[0] - latest[0]) / 4,
34140                         -(origin[1] - latest[1]) / 4
34141                     ];
34142
34143                     origin = latest;
34144                     nudge(d);
34145                 })
34146                 .on('mouseup.offset', function() {
34147                     d3.selectAll('.nudge-surface')
34148                         .remove();
34149
34150                     d3.select(window)
34151                         .on('mousemove.offset', null)
34152                         .on('mouseup.offset', null);
34153                 });
34154
34155             d3.event.preventDefault();
34156         }
34157
34158         function hide() {
34159             setVisible(false);
34160         }
34161
34162         function toggle() {
34163             if (d3.event) d3.event.preventDefault();
34164             tooltip.hide(button);
34165             setVisible(!button.classed('active'));
34166         }
34167
34168         function quickSwitch() {
34169             if (previous) {
34170                 clickSetSource(previous);
34171             }
34172         }
34173
34174         function setVisible(show) {
34175             if (show !== shown) {
34176                 button.classed('active', show);
34177                 shown = show;
34178
34179                 if (show) {
34180                     selection.on('mousedown.background-inside', function() {
34181                         return d3.event.stopPropagation();
34182                     });
34183                     content.style('display', 'block')
34184                         .style('right', '-300px')
34185                         .transition()
34186                         .duration(200)
34187                         .style('right', '0px');
34188                 } else {
34189                     content.style('display', 'block')
34190                         .style('right', '0px')
34191                         .transition()
34192                         .duration(200)
34193                         .style('right', '-300px')
34194                         .each('end', function() {
34195                             d3.select(this).style('display', 'none');
34196                         });
34197                     selection.on('mousedown.background-inside', null);
34198                 }
34199             }
34200         }
34201
34202
34203         var content = selection.append('div')
34204                 .attr('class', 'fillL map-overlay col3 content hide'),
34205             tooltip = bootstrap.tooltip()
34206                 .placement('left')
34207                 .html(true)
34208                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
34209             button = selection.append('button')
34210                 .attr('tabindex', -1)
34211                 .on('click', toggle)
34212                 .call(iD.svg.Icon('#icon-layers', 'light'))
34213                 .call(tooltip),
34214             shown = false;
34215
34216
34217         /* opacity switcher */
34218
34219         var opa = content.append('div')
34220                 .attr('class', 'opacity-options-wrapper');
34221
34222         opa.append('h4')
34223             .text(t('background.title'));
34224
34225         var opacityList = opa.append('ul')
34226             .attr('class', 'opacity-options');
34227
34228         opacityList.selectAll('div.opacity')
34229             .data(opacities)
34230             .enter()
34231             .append('li')
34232             .attr('data-original-title', function(d) {
34233                 return t('background.percent_brightness', { opacity: (d * 100) });
34234             })
34235             .on('click.set-opacity', setOpacity)
34236             .html('<div class="select-box"></div>')
34237             .call(bootstrap.tooltip()
34238                 .placement('left'))
34239             .append('div')
34240             .attr('class', 'opacity')
34241             .style('opacity', function(d) { return 1.25 - d; });
34242
34243
34244         /* background switcher */
34245
34246         var backgroundList = content.append('ul')
34247             .attr('class', 'layer-list');
34248
34249         var custom = backgroundList.append('li')
34250             .attr('class', 'custom_layer')
34251             .datum(iD.BackgroundSource.Custom());
34252
34253         custom.append('button')
34254             .attr('class', 'layer-browse')
34255             .call(bootstrap.tooltip()
34256                 .title(t('background.custom_button'))
34257                 .placement('left'))
34258             .on('click', editCustom)
34259             .call(iD.svg.Icon('#icon-search'));
34260
34261         var label = custom.append('label');
34262
34263         label.append('input')
34264             .attr('type', 'radio')
34265             .attr('name', 'layers')
34266             .on('change', function () {
34267                 if (customTemplate) {
34268                     setCustom(customTemplate);
34269                 } else {
34270                     editCustom();
34271                 }
34272             });
34273
34274         label.append('span')
34275             .text(t('background.custom'));
34276
34277         content.append('div')
34278             .attr('class', 'imagery-faq')
34279             .append('a')
34280             .attr('target', '_blank')
34281             .attr('tabindex', -1)
34282             .call(iD.svg.Icon('#icon-out-link', 'inline'))
34283             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
34284             .append('span')
34285             .text(t('background.imagery_source_faq'));
34286
34287         var overlayList = content.append('ul')
34288             .attr('class', 'layer-list');
34289
34290         var controls = content.append('div')
34291             .attr('class', 'controls-list');
34292
34293
34294         /* minimap toggle */
34295
34296         var minimapLabel = controls
34297             .append('label')
34298             .call(bootstrap.tooltip()
34299                 .html(true)
34300                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
34301                 .placement('top')
34302             );
34303
34304         minimapLabel.classed('minimap-toggle', true)
34305             .append('input')
34306             .attr('type', 'checkbox')
34307             .on('change', function() {
34308                 iD.ui.MapInMap.toggle();
34309                 d3.event.preventDefault();
34310             });
34311
34312         minimapLabel.append('span')
34313             .text(t('background.minimap.description'));
34314
34315
34316         /* imagery offset controls */
34317
34318         var adjustments = content.append('div')
34319             .attr('class', 'adjustments');
34320
34321         adjustments.append('a')
34322             .text(t('background.fix_misalignment'))
34323             .attr('href', '#')
34324             .classed('hide-toggle', true)
34325             .classed('expanded', false)
34326             .on('click', function() {
34327                 var exp = d3.select(this).classed('expanded');
34328                 nudgeContainer.style('display', exp ? 'none' : 'block');
34329                 d3.select(this).classed('expanded', !exp);
34330                 d3.event.preventDefault();
34331             });
34332
34333         var nudgeContainer = adjustments.append('div')
34334             .attr('class', 'nudge-container cf')
34335             .style('display', 'none');
34336
34337         nudgeContainer.append('div')
34338             .attr('class', 'nudge-instructions')
34339             .text(t('background.offset'));
34340
34341         var nudgeRect = nudgeContainer.append('div')
34342             .attr('class', 'nudge-outer-rect')
34343             .on('mousedown', dragOffset);
34344
34345         nudgeRect.append('div')
34346             .attr('class', 'nudge-inner-rect')
34347             .append('input')
34348             .on('change', inputOffset)
34349             .on('mousedown', function() {
34350                 d3.event.stopPropagation();
34351             });
34352
34353         nudgeContainer.append('div')
34354             .selectAll('button')
34355             .data(directions).enter()
34356             .append('button')
34357             .attr('class', function(d) { return d[0] + ' nudge'; })
34358             .on('mousedown', function(d) {
34359                 buttonOffset(d[1]);
34360             });
34361
34362         nudgeContainer.append('button')
34363             .attr('title', t('background.reset'))
34364             .attr('class', 'nudge-reset disabled')
34365             .on('click', resetOffset)
34366             .call(iD.svg.Icon('#icon-undo'));
34367
34368         context.map()
34369             .on('move.background-update', _.debounce(update, 1000));
34370
34371         context.background()
34372             .on('change.background-update', update);
34373
34374
34375         update();
34376         setOpacity(opacityDefault);
34377
34378         var keybinding = d3.keybinding('background')
34379             .on(key, toggle)
34380             .on(iD.ui.cmd('⌘B'), quickSwitch)
34381             .on('F', hide)
34382             .on('H', hide);
34383
34384         d3.select(document)
34385             .call(keybinding);
34386
34387         context.surface().on('mousedown.background-outside', hide);
34388         context.container().on('mousedown.background-outside', hide);
34389     }
34390
34391     return background;
34392 };
34393 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
34394 // For example, ⌘Z -> Ctrl+Z
34395 iD.ui.cmd = function(code) {
34396     if (iD.detect().os === 'mac') {
34397         return code;
34398     }
34399
34400     if (iD.detect().os === 'win') {
34401         if (code === '⌘⇧Z') return 'Ctrl+Y';
34402     }
34403
34404     var result = '',
34405         replacements = {
34406             '⌘': 'Ctrl',
34407             '⇧': 'Shift',
34408             '⌥': 'Alt',
34409             '⌫': 'Backspace',
34410             '⌦': 'Delete'
34411         };
34412
34413     for (var i = 0; i < code.length; i++) {
34414         if (code[i] in replacements) {
34415             result += replacements[code[i]] + '+';
34416         } else {
34417             result += code[i];
34418         }
34419     }
34420
34421     return result;
34422 };
34423 iD.ui.Commit = function(context) {
34424     var dispatch = d3.dispatch('cancel', 'save');
34425
34426     function commit(selection) {
34427         var changes = context.history().changes(),
34428             summary = context.history().difference().summary();
34429
34430         function zoomToEntity(change) {
34431             var entity = change.entity;
34432             if (change.changeType !== 'deleted' &&
34433                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
34434                 context.map().zoomTo(entity);
34435                 context.surface().selectAll(
34436                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
34437                     .classed('hover', true);
34438             }
34439         }
34440
34441         var header = selection.append('div')
34442             .attr('class', 'header fillL');
34443
34444         header.append('h3')
34445             .text(t('commit.title'));
34446
34447         var body = selection.append('div')
34448             .attr('class', 'body');
34449
34450
34451         // Comment Section
34452         var commentSection = body.append('div')
34453             .attr('class', 'modal-section form-field commit-form');
34454
34455         commentSection.append('label')
34456             .attr('class', 'form-label')
34457             .text(t('commit.message_label'));
34458
34459         var commentField = commentSection.append('textarea')
34460             .attr('placeholder', t('commit.description_placeholder'))
34461             .attr('maxlength', 255)
34462             .property('value', context.storage('comment') || '')
34463             .on('input.save', checkComment)
34464             .on('change.save', checkComment)
34465             .on('blur.save', function() {
34466                 context.storage('comment', this.value);
34467             });
34468
34469         function checkComment() {
34470             d3.selectAll('.save-section .save-button')
34471                 .attr('disabled', (this.value.length ? null : true));
34472
34473             var googleWarning = clippyArea
34474                .html('')
34475                .selectAll('a')
34476                .data(this.value.match(/google/i) ? [true] : []);
34477
34478             googleWarning.exit().remove();
34479
34480             googleWarning.enter()
34481                .append('a')
34482                .attr('target', '_blank')
34483                .attr('tabindex', -1)
34484                .call(iD.svg.Icon('#icon-alert', 'inline'))
34485                .attr('href', t('commit.google_warning_link'))
34486                .append('span')
34487                .text(t('commit.google_warning'));
34488         }
34489
34490         commentField.node().select();
34491
34492         context.connection().userChangesets(function (err, changesets) {
34493             if (err) return;
34494
34495             var comments = [];
34496
34497             for (var i = 0; i < changesets.length; i++) {
34498                 if (changesets[i].tags.comment) {
34499                     comments.push({
34500                         title: changesets[i].tags.comment,
34501                         value: changesets[i].tags.comment
34502                     });
34503                 }
34504             }
34505
34506             commentField.call(d3.combobox().caseSensitive(true).data(comments));
34507         });
34508
34509         var clippyArea = commentSection.append('div')
34510             .attr('class', 'clippy-area');
34511
34512
34513         var changeSetInfo = commentSection.append('div')
34514             .attr('class', 'changeset-info');
34515
34516         changeSetInfo.append('a')
34517             .attr('target', '_blank')
34518             .attr('tabindex', -1)
34519             .call(iD.svg.Icon('#icon-out-link', 'inline'))
34520             .attr('href', t('commit.about_changeset_comments_link'))
34521             .append('span')
34522             .text(t('commit.about_changeset_comments'));
34523
34524         // Warnings
34525         var warnings = body.selectAll('div.warning-section')
34526             .data([context.history().validate(changes)])
34527             .enter()
34528             .append('div')
34529             .attr('class', 'modal-section warning-section fillL2')
34530             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
34531             .style('background', '#ffb');
34532
34533         warnings.append('h3')
34534             .text(t('commit.warnings'));
34535
34536         var warningLi = warnings.append('ul')
34537             .attr('class', 'changeset-list')
34538             .selectAll('li')
34539             .data(function(d) { return d; })
34540             .enter()
34541             .append('li')
34542             .style()
34543             .on('mouseover', mouseover)
34544             .on('mouseout', mouseout)
34545             .on('click', warningClick);
34546
34547         warningLi
34548             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
34549
34550         warningLi
34551             .append('strong').text(function(d) {
34552                 return d.message;
34553             });
34554
34555         warningLi.filter(function(d) { return d.tooltip; })
34556             .call(bootstrap.tooltip()
34557                 .title(function(d) { return d.tooltip; })
34558                 .placement('top')
34559             );
34560
34561
34562         // Upload Explanation
34563         var saveSection = body.append('div')
34564             .attr('class','modal-section save-section fillL cf');
34565
34566         var prose = saveSection.append('p')
34567             .attr('class', 'commit-info')
34568             .html(t('commit.upload_explanation'));
34569
34570         context.connection().userDetails(function(err, user) {
34571             if (err) return;
34572
34573             var userLink = d3.select(document.createElement('div'));
34574
34575             if (user.image_url) {
34576                 userLink.append('img')
34577                     .attr('src', user.image_url)
34578                     .attr('class', 'icon pre-text user-icon');
34579             }
34580
34581             userLink.append('a')
34582                 .attr('class','user-info')
34583                 .text(user.display_name)
34584                 .attr('href', context.connection().userURL(user.display_name))
34585                 .attr('tabindex', -1)
34586                 .attr('target', '_blank');
34587
34588             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
34589         });
34590
34591
34592         // Buttons
34593         var buttonSection = saveSection.append('div')
34594             .attr('class','buttons fillL cf');
34595
34596         var cancelButton = buttonSection.append('button')
34597             .attr('class', 'secondary-action col5 button cancel-button')
34598             .on('click.cancel', function() { dispatch.cancel(); });
34599
34600         cancelButton.append('span')
34601             .attr('class', 'label')
34602             .text(t('commit.cancel'));
34603
34604         var saveButton = buttonSection.append('button')
34605             .attr('class', 'action col5 button save-button')
34606             .attr('disabled', function() {
34607                 var n = d3.select('.commit-form textarea').node();
34608                 return (n && n.value.length) ? null : true;
34609             })
34610             .on('click.save', function() {
34611                 dispatch.save({
34612                     comment: commentField.node().value
34613                 });
34614             });
34615
34616         saveButton.append('span')
34617             .attr('class', 'label')
34618             .text(t('commit.save'));
34619
34620
34621         // Changes
34622         var changeSection = body.selectAll('div.commit-section')
34623             .data([0])
34624             .enter()
34625             .append('div')
34626             .attr('class', 'commit-section modal-section fillL2');
34627
34628         changeSection.append('h3')
34629             .text(t('commit.changes', {count: summary.length}));
34630
34631         var li = changeSection.append('ul')
34632             .attr('class', 'changeset-list')
34633             .selectAll('li')
34634             .data(summary)
34635             .enter()
34636             .append('li')
34637             .on('mouseover', mouseover)
34638             .on('mouseout', mouseout)
34639             .on('click', zoomToEntity);
34640
34641         li.each(function(d) {
34642             d3.select(this)
34643                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
34644         });
34645
34646         li.append('span')
34647             .attr('class', 'change-type')
34648             .text(function(d) {
34649                 return t('commit.' + d.changeType) + ' ';
34650             });
34651
34652         li.append('strong')
34653             .attr('class', 'entity-type')
34654             .text(function(d) {
34655                 return context.presets().match(d.entity, d.graph).name();
34656             });
34657
34658         li.append('span')
34659             .attr('class', 'entity-name')
34660             .text(function(d) {
34661                 var name = iD.util.displayName(d.entity) || '',
34662                     string = '';
34663                 if (name !== '') string += ':';
34664                 return string += ' ' + name;
34665             });
34666
34667         li.style('opacity', 0)
34668             .transition()
34669             .style('opacity', 1);
34670
34671
34672         function mouseover(d) {
34673             if (d.entity) {
34674                 context.surface().selectAll(
34675                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
34676                 ).classed('hover', true);
34677             }
34678         }
34679
34680         function mouseout() {
34681             context.surface().selectAll('.hover')
34682                 .classed('hover', false);
34683         }
34684
34685         function warningClick(d) {
34686             if (d.entity) {
34687                 context.map().zoomTo(d.entity);
34688                 context.enter(
34689                     iD.modes.Select(context, [d.entity.id])
34690                         .suppressMenu(true));
34691             }
34692         }
34693
34694         // Call checkComment off the bat, in case a changeset
34695         // comment is recovered from localStorage
34696         commentField.trigger('input');
34697     }
34698
34699     return d3.rebind(commit, dispatch, 'on');
34700 };
34701 iD.ui.confirm = function(selection) {
34702     var modal = iD.ui.modal(selection);
34703
34704     modal.select('.modal')
34705         .classed('modal-alert', true);
34706
34707     var section = modal.select('.content');
34708
34709     section.append('div')
34710         .attr('class', 'modal-section header');
34711
34712     section.append('div')
34713         .attr('class', 'modal-section message-text');
34714
34715     var buttons = section.append('div')
34716         .attr('class', 'modal-section buttons cf');
34717
34718     modal.okButton = function() {
34719         buttons
34720             .append('button')
34721             .attr('class', 'action col4')
34722             .on('click.confirm', function() {
34723                 modal.remove();
34724             })
34725             .text(t('confirm.okay'));
34726
34727         return modal;
34728     };
34729
34730     return modal;
34731 };
34732 iD.ui.Conflicts = function(context) {
34733     var dispatch = d3.dispatch('download', 'cancel', 'save'),
34734         list;
34735
34736     function conflicts(selection) {
34737         var header = selection
34738             .append('div')
34739             .attr('class', 'header fillL');
34740
34741         header
34742             .append('button')
34743             .attr('class', 'fr')
34744             .on('click', function() { dispatch.cancel(); })
34745             .call(iD.svg.Icon('#icon-close'));
34746
34747         header
34748             .append('h3')
34749             .text(t('save.conflict.header'));
34750
34751         var body = selection
34752             .append('div')
34753             .attr('class', 'body fillL');
34754
34755         body
34756             .append('div')
34757             .attr('class', 'conflicts-help')
34758             .text(t('save.conflict.help'))
34759             .append('a')
34760             .attr('class', 'conflicts-download')
34761             .text(t('save.conflict.download_changes'))
34762             .on('click.download', function() { dispatch.download(); });
34763
34764         body
34765             .append('div')
34766             .attr('class', 'conflict-container fillL3')
34767             .call(showConflict, 0);
34768
34769         body
34770             .append('div')
34771             .attr('class', 'conflicts-done')
34772             .attr('opacity', 0)
34773             .style('display', 'none')
34774             .text(t('save.conflict.done'));
34775
34776         var buttons = body
34777             .append('div')
34778             .attr('class','buttons col12 joined conflicts-buttons');
34779
34780         buttons
34781             .append('button')
34782             .attr('disabled', list.length > 1)
34783             .attr('class', 'action conflicts-button col6')
34784             .text(t('save.title'))
34785             .on('click.try_again', function() { dispatch.save(); });
34786
34787         buttons
34788             .append('button')
34789             .attr('class', 'secondary-action conflicts-button col6')
34790             .text(t('confirm.cancel'))
34791             .on('click.cancel', function() { dispatch.cancel(); });
34792     }
34793
34794
34795     function showConflict(selection, index) {
34796         if (index < 0 || index >= list.length) return;
34797
34798         var parent = d3.select(selection.node().parentNode);
34799
34800         // enable save button if this is the last conflict being reviewed..
34801         if (index === list.length - 1) {
34802             window.setTimeout(function() {
34803                 parent.select('.conflicts-button')
34804                     .attr('disabled', null);
34805
34806                 parent.select('.conflicts-done')
34807                     .transition()
34808                     .attr('opacity', 1)
34809                     .style('display', 'block');
34810             }, 250);
34811         }
34812
34813         var item = selection
34814             .selectAll('.conflict')
34815             .data([list[index]]);
34816
34817         var enter = item.enter()
34818             .append('div')
34819             .attr('class', 'conflict');
34820
34821         enter
34822             .append('h4')
34823             .attr('class', 'conflict-count')
34824             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
34825
34826         enter
34827             .append('a')
34828             .attr('class', 'conflict-description')
34829             .attr('href', '#')
34830             .text(function(d) { return d.name; })
34831             .on('click', function(d) {
34832                 zoomToEntity(d.id);
34833                 d3.event.preventDefault();
34834             });
34835
34836         var details = enter
34837             .append('div')
34838             .attr('class', 'conflict-detail-container');
34839
34840         details
34841             .append('ul')
34842             .attr('class', 'conflict-detail-list')
34843             .selectAll('li')
34844             .data(function(d) { return d.details || []; })
34845             .enter()
34846             .append('li')
34847             .attr('class', 'conflict-detail-item')
34848             .html(function(d) { return d; });
34849
34850         details
34851             .append('div')
34852             .attr('class', 'conflict-choices')
34853             .call(addChoices);
34854
34855         details
34856             .append('div')
34857             .attr('class', 'conflict-nav-buttons joined cf')
34858             .selectAll('button')
34859             .data(['previous', 'next'])
34860             .enter()
34861             .append('button')
34862             .text(function(d) { return t('save.conflict.' + d); })
34863             .attr('class', 'conflict-nav-button action col6')
34864             .attr('disabled', function(d, i) {
34865                 return (i === 0 && index === 0) ||
34866                     (i === 1 && index === list.length - 1) || null;
34867             })
34868             .on('click', function(d, i) {
34869                 var container = parent.select('.conflict-container'),
34870                 sign = (i === 0 ? -1 : 1);
34871
34872                 container
34873                     .selectAll('.conflict')
34874                     .remove();
34875
34876                 container
34877                     .call(showConflict, index + sign);
34878
34879                 d3.event.preventDefault();
34880             });
34881
34882         item.exit()
34883             .remove();
34884
34885     }
34886
34887     function addChoices(selection) {
34888         var choices = selection
34889             .append('ul')
34890             .attr('class', 'layer-list')
34891             .selectAll('li')
34892             .data(function(d) { return d.choices || []; });
34893
34894         var enter = choices.enter()
34895             .append('li')
34896             .attr('class', 'layer');
34897
34898         var label = enter
34899             .append('label');
34900
34901         label
34902             .append('input')
34903             .attr('type', 'radio')
34904             .attr('name', function(d) { return d.id; })
34905             .on('change', function(d, i) {
34906                 var ul = this.parentNode.parentNode.parentNode;
34907                 ul.__data__.chosen = i;
34908                 choose(ul, d);
34909             });
34910
34911         label
34912             .append('span')
34913             .text(function(d) { return d.text; });
34914
34915         choices
34916             .each(function(d, i) {
34917                 var ul = this.parentNode;
34918                 if (ul.__data__.chosen === i) choose(ul, d);
34919             });
34920     }
34921
34922     function choose(ul, datum) {
34923         if (d3.event) d3.event.preventDefault();
34924
34925         d3.select(ul)
34926             .selectAll('li')
34927             .classed('active', function(d) { return d === datum; })
34928             .selectAll('input')
34929             .property('checked', function(d) { return d === datum; });
34930
34931         var extent = iD.geo.Extent(),
34932             entity;
34933
34934         entity = context.graph().hasEntity(datum.id);
34935         if (entity) extent._extend(entity.extent(context.graph()));
34936
34937         datum.action();
34938
34939         entity = context.graph().hasEntity(datum.id);
34940         if (entity) extent._extend(entity.extent(context.graph()));
34941
34942         zoomToEntity(datum.id, extent);
34943     }
34944
34945     function zoomToEntity(id, extent) {
34946         context.surface().selectAll('.hover')
34947             .classed('hover', false);
34948
34949         var entity = context.graph().hasEntity(id);
34950         if (entity) {
34951             if (extent) {
34952                 context.map().trimmedExtent(extent);
34953             } else {
34954                 context.map().zoomTo(entity);
34955             }
34956             context.surface().selectAll(
34957                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
34958                 .classed('hover', true);
34959         }
34960     }
34961
34962
34963     // The conflict list should be an array of objects like:
34964     // {
34965     //     id: id,
34966     //     name: entityName(local),
34967     //     details: merge.conflicts(),
34968     //     chosen: 1,
34969     //     choices: [
34970     //         choice(id, keepMine, forceLocal),
34971     //         choice(id, keepTheirs, forceRemote)
34972     //     ]
34973     // }
34974     conflicts.list = function(_) {
34975         if (!arguments.length) return list;
34976         list = _;
34977         return conflicts;
34978     };
34979
34980     return d3.rebind(conflicts, dispatch, 'on');
34981 };
34982 iD.ui.Contributors = function(context) {
34983     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
34984         limit = 4,
34985         hidden = false,
34986         wrap = d3.select(null);
34987
34988     function update() {
34989         var users = {},
34990             entities = context.intersects(context.map().extent());
34991
34992         entities.forEach(function(entity) {
34993             if (entity && entity.user) users[entity.user] = true;
34994         });
34995
34996         var u = Object.keys(users),
34997             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
34998
34999         wrap.html('')
35000             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
35001
35002         var userList = d3.select(document.createElement('span'));
35003
35004         userList.selectAll()
35005             .data(subset)
35006             .enter()
35007             .append('a')
35008             .attr('class', 'user-link')
35009             .attr('href', function(d) { return context.connection().userURL(d); })
35010             .attr('target', '_blank')
35011             .attr('tabindex', -1)
35012             .text(String);
35013
35014         if (u.length > limit) {
35015             var count = d3.select(document.createElement('span'));
35016
35017             count.append('a')
35018                 .attr('target', '_blank')
35019                 .attr('tabindex', -1)
35020                 .attr('href', function() {
35021                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
35022                 })
35023                 .text(u.length - limit + 1);
35024
35025             wrap.append('span')
35026                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
35027
35028         } else {
35029             wrap.append('span')
35030                 .html(t('contributors.list', { users: userList.html() }));
35031         }
35032
35033         if (!u.length) {
35034             hidden = true;
35035             wrap
35036                 .transition()
35037                 .style('opacity', 0);
35038
35039         } else if (hidden) {
35040             wrap
35041                 .transition()
35042                 .style('opacity', 1);
35043         }
35044     }
35045
35046     return function(selection) {
35047         wrap = selection;
35048         update();
35049
35050         context.connection().on('loaded.contributors', debouncedUpdate);
35051         context.map().on('move.contributors', debouncedUpdate);
35052     };
35053 };
35054 iD.ui.Disclosure = function() {
35055     var dispatch = d3.dispatch('toggled'),
35056         title,
35057         expanded = false,
35058         content = function () {};
35059
35060     var disclosure = function(selection) {
35061         var $link = selection.selectAll('.hide-toggle')
35062             .data([0]);
35063
35064         $link.enter().append('a')
35065             .attr('href', '#')
35066             .attr('class', 'hide-toggle');
35067
35068         $link.text(title)
35069             .on('click', toggle)
35070             .classed('expanded', expanded);
35071
35072         var $body = selection.selectAll('div')
35073             .data([0]);
35074
35075         $body.enter().append('div');
35076
35077         $body.classed('hide', !expanded)
35078             .call(content);
35079
35080         function toggle() {
35081             expanded = !expanded;
35082             $link.classed('expanded', expanded);
35083             $body.call(iD.ui.Toggle(expanded));
35084             dispatch.toggled(expanded);
35085         }
35086     };
35087
35088     disclosure.title = function(_) {
35089         if (!arguments.length) return title;
35090         title = _;
35091         return disclosure;
35092     };
35093
35094     disclosure.expanded = function(_) {
35095         if (!arguments.length) return expanded;
35096         expanded = _;
35097         return disclosure;
35098     };
35099
35100     disclosure.content = function(_) {
35101         if (!arguments.length) return content;
35102         content = _;
35103         return disclosure;
35104     };
35105
35106     return d3.rebind(disclosure, dispatch, 'on');
35107 };
35108 iD.ui.EntityEditor = function(context) {
35109     var dispatch = d3.dispatch('choose'),
35110         state = 'select',
35111         coalesceChanges = false,
35112         modified = false,
35113         base,
35114         id,
35115         preset,
35116         reference;
35117
35118     var presetEditor = iD.ui.preset(context)
35119         .on('change', changeTags);
35120     var rawTagEditor = iD.ui.RawTagEditor(context)
35121         .on('change', changeTags);
35122
35123     function entityEditor(selection) {
35124         var entity = context.entity(id),
35125             tags = _.clone(entity.tags);
35126
35127         var $header = selection.selectAll('.header')
35128             .data([0]);
35129
35130         // Enter
35131         var $enter = $header.enter().append('div')
35132             .attr('class', 'header fillL cf');
35133
35134         $enter.append('button')
35135             .attr('class', 'fl preset-reset preset-choose')
35136             .append('span')
35137             .html('&#9668;');
35138
35139         $enter.append('button')
35140             .attr('class', 'fr preset-close')
35141             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
35142
35143         $enter.append('h3');
35144
35145         // Update
35146         $header.select('h3')
35147             .text(t('inspector.edit'));
35148
35149         $header.select('.preset-close')
35150             .on('click', function() {
35151                 context.enter(iD.modes.Browse(context));
35152             });
35153
35154         var $body = selection.selectAll('.inspector-body')
35155             .data([0]);
35156
35157         // Enter
35158         $enter = $body.enter().append('div')
35159             .attr('class', 'inspector-body');
35160
35161         $enter.append('div')
35162             .attr('class', 'preset-list-item inspector-inner')
35163             .append('div')
35164             .attr('class', 'preset-list-button-wrap')
35165             .append('button')
35166             .attr('class', 'preset-list-button preset-reset')
35167             .call(bootstrap.tooltip()
35168                 .title(t('inspector.back_tooltip'))
35169                 .placement('bottom'))
35170             .append('div')
35171             .attr('class', 'label');
35172
35173         $body.select('.preset-list-button-wrap')
35174             .call(reference.button);
35175
35176         $body.select('.preset-list-item')
35177             .call(reference.body);
35178
35179         $enter.append('div')
35180             .attr('class', 'inspector-border inspector-preset');
35181
35182         $enter.append('div')
35183             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
35184
35185         $enter.append('div')
35186             .attr('class', 'inspector-border raw-member-editor inspector-inner');
35187
35188         $enter.append('div')
35189             .attr('class', 'raw-membership-editor inspector-inner');
35190
35191         selection.selectAll('.preset-reset')
35192             .on('click', function() {
35193                 dispatch.choose(preset);
35194             });
35195
35196         // Update
35197         $body.select('.preset-list-item button')
35198             .call(iD.ui.PresetIcon()
35199                 .geometry(context.geometry(id))
35200                 .preset(preset));
35201
35202         $body.select('.preset-list-item .label')
35203             .text(preset.name());
35204
35205         $body.select('.inspector-preset')
35206             .call(presetEditor
35207                 .preset(preset)
35208                 .entityID(id)
35209                 .tags(tags)
35210                 .state(state));
35211
35212         $body.select('.raw-tag-editor')
35213             .call(rawTagEditor
35214                 .preset(preset)
35215                 .entityID(id)
35216                 .tags(tags)
35217                 .state(state));
35218
35219         if (entity.type === 'relation') {
35220             $body.select('.raw-member-editor')
35221                 .style('display', 'block')
35222                 .call(iD.ui.RawMemberEditor(context)
35223                     .entityID(id));
35224         } else {
35225             $body.select('.raw-member-editor')
35226                 .style('display', 'none');
35227         }
35228
35229         $body.select('.raw-membership-editor')
35230             .call(iD.ui.RawMembershipEditor(context)
35231                 .entityID(id));
35232
35233         function historyChanged() {
35234             if (state === 'hide') return;
35235
35236             var entity = context.hasEntity(id),
35237                 graph = context.graph();
35238             if (!entity) return;
35239
35240             entityEditor.preset(context.presets().match(entity, graph));
35241             entityEditor.modified(base !== graph);
35242             entityEditor(selection);
35243         }
35244
35245         context.history()
35246             .on('change.entity-editor', historyChanged);
35247     }
35248
35249     function clean(o) {
35250
35251         function cleanVal(k, v) {
35252             function keepSpaces(k) {
35253                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
35254                     'operating_times', 'smoking_hours', 'happy_hours'];
35255                 return _.some(whitelist, function(s) { return k.indexOf(s) !== -1; });
35256             }
35257
35258             var blacklist = ['description', 'note', 'fixme'];
35259             if (_.some(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
35260
35261             var cleaned = v.split(';')
35262                 .map(function(s) { return s.trim(); })
35263                 .join(keepSpaces(k) ? '; ' : ';');
35264
35265             // The code below is not intended to validate websites and emails.
35266             // It is only intended to prevent obvious copy-paste errors. (#2323)
35267
35268             // clean website- and email-like tags
35269             if (k.indexOf('website') !== -1 ||
35270                 k.indexOf('email') !== -1 ||
35271                 cleaned.indexOf('http') === 0) {
35272                 cleaned = cleaned
35273                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
35274
35275             }
35276
35277             return cleaned;
35278         }
35279
35280         var out = {}, k, v;
35281         for (k in o) {
35282             if (k && (v = o[k]) !== undefined) {
35283                 out[k] = cleanVal(k, v);
35284             }
35285         }
35286         return out;
35287     }
35288
35289     // Tag changes that fire on input can all get coalesced into a single
35290     // history operation when the user leaves the field.  #2342
35291     function changeTags(changed, onInput) {
35292         var entity = context.entity(id),
35293             annotation = t('operations.change_tags.annotation'),
35294             tags = _.extend({}, entity.tags, changed);
35295
35296         if (!onInput) {
35297             tags = clean(tags);
35298         }
35299         if (!_.isEqual(entity.tags, tags)) {
35300             if (coalesceChanges) {
35301                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
35302             } else {
35303                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
35304                 coalesceChanges = !!onInput;
35305             }
35306         }
35307     }
35308
35309     entityEditor.modified = function(_) {
35310         if (!arguments.length) return modified;
35311         modified = _;
35312         d3.selectAll('button.preset-close use')
35313             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
35314     };
35315
35316     entityEditor.state = function(_) {
35317         if (!arguments.length) return state;
35318         state = _;
35319         return entityEditor;
35320     };
35321
35322     entityEditor.entityID = function(_) {
35323         if (!arguments.length) return id;
35324         id = _;
35325         base = context.graph();
35326         entityEditor.preset(context.presets().match(context.entity(id), base));
35327         entityEditor.modified(false);
35328         coalesceChanges = false;
35329         return entityEditor;
35330     };
35331
35332     entityEditor.preset = function(_) {
35333         if (!arguments.length) return preset;
35334         if (_ !== preset) {
35335             preset = _;
35336             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
35337                 .showing(false);
35338         }
35339         return entityEditor;
35340     };
35341
35342     return d3.rebind(entityEditor, dispatch, 'on');
35343 };
35344 iD.ui.FeatureInfo = function(context) {
35345     function update(selection) {
35346         var features = context.features(),
35347             stats = features.stats(),
35348             count = 0,
35349             hiddenList = _.compact(_.map(features.hidden(), function(k) {
35350                 if (stats[k]) {
35351                     count += stats[k];
35352                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
35353                 }
35354             }));
35355
35356         selection.html('');
35357
35358         if (hiddenList.length) {
35359             var tooltip = bootstrap.tooltip()
35360                     .placement('top')
35361                     .html(true)
35362                     .title(function() {
35363                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
35364                     });
35365
35366             var warning = selection.append('a')
35367                 .attr('href', '#')
35368                 .attr('tabindex', -1)
35369                 .html(t('feature_info.hidden_warning', { count: count }))
35370                 .call(tooltip)
35371                 .on('click', function() {
35372                     tooltip.hide(warning);
35373                     // open map data panel?
35374                     d3.event.preventDefault();
35375                 });
35376         }
35377
35378         selection
35379             .classed('hide', !hiddenList.length);
35380     }
35381
35382     return function(selection) {
35383         update(selection);
35384
35385         context.features().on('change.feature_info', function() {
35386             update(selection);
35387         });
35388     };
35389 };
35390 iD.ui.FeatureList = function(context) {
35391     var geocodeResults;
35392
35393     function featureList(selection) {
35394         var header = selection.append('div')
35395             .attr('class', 'header fillL cf');
35396
35397         header.append('h3')
35398             .text(t('inspector.feature_list'));
35399
35400         function keypress() {
35401             var q = search.property('value'),
35402                 items = list.selectAll('.feature-list-item');
35403             if (d3.event.keyCode === 13 && q.length && items.size()) {
35404                 click(items.datum());
35405             }
35406         }
35407
35408         function inputevent() {
35409             geocodeResults = undefined;
35410             drawList();
35411         }
35412
35413         var searchWrap = selection.append('div')
35414             .attr('class', 'search-header');
35415
35416         var search = searchWrap.append('input')
35417             .attr('placeholder', t('inspector.search'))
35418             .attr('type', 'search')
35419             .on('keypress', keypress)
35420             .on('input', inputevent);
35421
35422         searchWrap
35423             .call(iD.svg.Icon('#icon-search', 'pre-text'));
35424
35425         var listWrap = selection.append('div')
35426             .attr('class', 'inspector-body');
35427
35428         var list = listWrap.append('div')
35429             .attr('class', 'feature-list cf');
35430
35431         context
35432             .on('exit.feature-list', clearSearch);
35433         context.map()
35434             .on('drawn.feature-list', mapDrawn);
35435
35436         function clearSearch() {
35437             search.property('value', '');
35438             drawList();
35439         }
35440
35441         function mapDrawn(e) {
35442             if (e.full) {
35443                 drawList();
35444             }
35445         }
35446
35447         function features() {
35448             var entities = {},
35449                 result = [],
35450                 graph = context.graph(),
35451                 q = search.property('value').toLowerCase();
35452
35453             if (!q) return result;
35454
35455             var idMatch = q.match(/^([nwr])([0-9]+)$/);
35456
35457             if (idMatch) {
35458                 result.push({
35459                     id: idMatch[0],
35460                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
35461                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
35462                     name: idMatch[2]
35463                 });
35464             }
35465
35466             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
35467
35468             if (locationMatch) {
35469                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
35470                 result.push({
35471                     id: -1,
35472                     geometry: 'point',
35473                     type: t('inspector.location'),
35474                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
35475                     location: loc
35476                 });
35477             }
35478
35479             function addEntity(entity) {
35480                 if (entity.id in entities || result.length > 200)
35481                     return;
35482
35483                 entities[entity.id] = true;
35484
35485                 var name = iD.util.displayName(entity) || '';
35486                 if (name.toLowerCase().indexOf(q) >= 0) {
35487                     result.push({
35488                         id: entity.id,
35489                         entity: entity,
35490                         geometry: context.geometry(entity.id),
35491                         type: context.presets().match(entity, graph).name(),
35492                         name: name
35493                     });
35494                 }
35495
35496                 graph.parentRelations(entity).forEach(function(parent) {
35497                     addEntity(parent);
35498                 });
35499             }
35500
35501             var visible = context.surface().selectAll('.point, .line, .area')[0];
35502             for (var i = 0; i < visible.length && result.length <= 200; i++) {
35503                 addEntity(visible[i].__data__);
35504             }
35505
35506             (geocodeResults || []).forEach(function(d) {
35507                 // https://github.com/openstreetmap/iD/issues/1890
35508                 if (d.osm_type && d.osm_id) {
35509                     result.push({
35510                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
35511                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
35512                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
35513                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
35514                         name: d.display_name,
35515                         extent: new iD.geo.Extent(
35516                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
35517                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
35518                     });
35519                 }
35520             });
35521
35522             return result;
35523         }
35524
35525         function drawList() {
35526             var value = search.property('value'),
35527                 results = features();
35528
35529             list.classed('filtered', value.length);
35530
35531             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
35532
35533             var resultsIndicator = list.selectAll('.no-results-item')
35534                 .data([0])
35535                 .enter().append('button')
35536                 .property('disabled', true)
35537                 .attr('class', 'no-results-item')
35538                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
35539
35540             resultsIndicator.append('span')
35541                 .attr('class', 'entity-name');
35542
35543             list.selectAll('.no-results-item .entity-name')
35544                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
35545
35546             list.selectAll('.geocode-item')
35547                 .data([0])
35548                 .enter().append('button')
35549                 .attr('class', 'geocode-item')
35550                 .on('click', geocode)
35551                 .append('div')
35552                 .attr('class', 'label')
35553                 .append('span')
35554                 .attr('class', 'entity-name')
35555                 .text(t('geocoder.search'));
35556
35557             list.selectAll('.no-results-item')
35558                 .style('display', (value.length && !results.length) ? 'block' : 'none');
35559
35560             list.selectAll('.geocode-item')
35561                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
35562
35563             list.selectAll('.feature-list-item')
35564                 .data([-1])
35565                 .remove();
35566
35567             var items = list.selectAll('.feature-list-item')
35568                 .data(results, function(d) { return d.id; });
35569
35570             var enter = items.enter()
35571                 .insert('button', '.geocode-item')
35572                 .attr('class', 'feature-list-item')
35573                 .on('mouseover', mouseover)
35574                 .on('mouseout', mouseout)
35575                 .on('click', click);
35576
35577             var label = enter
35578                 .append('div')
35579                 .attr('class', 'label');
35580
35581             label.each(function(d) {
35582                 d3.select(this)
35583                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
35584             });
35585
35586             label.append('span')
35587                 .attr('class', 'entity-type')
35588                 .text(function(d) { return d.type; });
35589
35590             label.append('span')
35591                 .attr('class', 'entity-name')
35592                 .text(function(d) { return d.name; });
35593
35594             enter.style('opacity', 0)
35595                 .transition()
35596                 .style('opacity', 1);
35597
35598             items.order();
35599
35600             items.exit()
35601                 .remove();
35602         }
35603
35604         function mouseover(d) {
35605             if (d.id === -1) return;
35606
35607             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
35608                 .classed('hover', true);
35609         }
35610
35611         function mouseout() {
35612             context.surface().selectAll('.hover')
35613                 .classed('hover', false);
35614         }
35615
35616         function click(d) {
35617             d3.event.preventDefault();
35618             if (d.location) {
35619                 context.map().centerZoom([d.location[1], d.location[0]], 20);
35620             }
35621             else if (d.entity) {
35622                 if (d.entity.type === 'node') {
35623                     context.map().center(d.entity.loc);
35624                 } else if (d.entity.type === 'way') {
35625                     var center = context.projection(context.map().center()),
35626                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
35627                     context.map().center(edge.loc);
35628                 }
35629                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
35630             } else {
35631                 context.zoomToEntity(d.id);
35632             }
35633         }
35634
35635         function geocode() {
35636             var searchVal = encodeURIComponent(search.property('value'));
35637             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
35638                 geocodeResults = resp || [];
35639                 drawList();
35640             });
35641         }
35642     }
35643
35644     return featureList;
35645 };
35646 iD.ui.flash = function(selection) {
35647     var modal = iD.ui.modal(selection);
35648
35649     modal.select('.modal').classed('modal-flash', true);
35650
35651     modal.select('.content')
35652         .classed('modal-section', true)
35653         .append('div')
35654         .attr('class', 'description');
35655
35656     modal.on('click.flash', function() { modal.remove(); });
35657
35658     setTimeout(function() {
35659         modal.remove();
35660         return true;
35661     }, 1500);
35662
35663     return modal;
35664 };
35665 iD.ui.FullScreen = function(context) {
35666     var element = context.container().node(),
35667         keybinding = d3.keybinding('full-screen');
35668         // button;
35669
35670     function getFullScreenFn() {
35671         if (element.requestFullscreen) {
35672             return element.requestFullscreen;
35673         } else if (element.msRequestFullscreen) {
35674             return  element.msRequestFullscreen;
35675         } else if (element.mozRequestFullScreen) {
35676             return  element.mozRequestFullScreen;
35677         } else if (element.webkitRequestFullscreen) {
35678             return element.webkitRequestFullscreen;
35679         }
35680     }
35681
35682     function getExitFullScreenFn() {
35683         if (document.exitFullscreen) {
35684             return document.exitFullscreen;
35685         } else if (document.msExitFullscreen) {
35686             return  document.msExitFullscreen;
35687         } else if (document.mozCancelFullScreen) {
35688             return  document.mozCancelFullScreen;
35689         } else if (document.webkitExitFullscreen) {
35690             return document.webkitExitFullscreen;
35691         }
35692     }
35693
35694     function isFullScreen() {
35695         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
35696             document.msFullscreenElement;
35697     }
35698
35699     function isSupported() {
35700         return !!getFullScreenFn();
35701     }
35702
35703     function fullScreen() {
35704         d3.event.preventDefault();
35705         if (!isFullScreen()) {
35706             // button.classed('active', true);
35707             getFullScreenFn().apply(element);
35708         } else {
35709             // button.classed('active', false);
35710             getExitFullScreenFn().apply(document);
35711         }
35712     }
35713
35714     return function() { // selection) {
35715         if (!isSupported())
35716             return;
35717
35718         // var tooltip = bootstrap.tooltip()
35719         //     .placement('left');
35720
35721         // button = selection.append('button')
35722         //     .attr('title', t('full_screen'))
35723         //     .attr('tabindex', -1)
35724         //     .on('click', fullScreen)
35725         //     .call(tooltip);
35726
35727         // button.append('span')
35728         //     .attr('class', 'icon full-screen');
35729
35730         keybinding
35731             .on('f11', fullScreen)
35732             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
35733
35734         d3.select(document)
35735             .call(keybinding);
35736     };
35737 };
35738 iD.ui.Geolocate = function(context) {
35739     var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
35740         locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
35741         timeoutId;
35742
35743     function click() {
35744         context.enter(iD.modes.Browse(context));
35745         context.container().call(locating);
35746         navigator.geolocation.getCurrentPosition(success, error, geoOptions);
35747
35748         // This timeout ensures that we still call finish() even if
35749         // the user declines to share their location in Firefox
35750         timeoutId = setTimeout(finish, 10000 /* 10sec */ );
35751     }
35752
35753     function success(position) {
35754         var map = context.map(),
35755             extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
35756                 .padByMeters(position.coords.accuracy);
35757
35758         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
35759         finish();
35760     }
35761
35762     function error() {
35763         finish();
35764     }
35765
35766     function finish() {
35767         locating.close();  // unblock ui
35768         if (timeoutId) { clearTimeout(timeoutId); }
35769         timeoutId = undefined;
35770     }
35771
35772     return function(selection) {
35773         if (!navigator.geolocation) return;
35774
35775         selection.append('button')
35776             .attr('tabindex', -1)
35777             .attr('title', t('geolocate.title'))
35778             .on('click', click)
35779             .call(iD.svg.Icon('#icon-geolocate', 'light'))
35780             .call(bootstrap.tooltip()
35781                 .placement('left'));
35782     };
35783 };
35784 iD.ui.Help = function(context) {
35785     var key = 'H';
35786
35787     var docKeys = [
35788         'help.help',
35789         'help.editing_saving',
35790         'help.roads',
35791         'help.gps',
35792         'help.imagery',
35793         'help.addresses',
35794         'help.inspector',
35795         'help.buildings',
35796         'help.relations'];
35797
35798     var docs = docKeys.map(function(key) {
35799         var text = t(key);
35800         return {
35801             title: text.split('\n')[0].replace('#', '').trim(),
35802             html: marked(text.split('\n').slice(1).join('\n'))
35803         };
35804     });
35805
35806     function help(selection) {
35807
35808         function hide() {
35809             setVisible(false);
35810         }
35811
35812         function toggle() {
35813             if (d3.event) d3.event.preventDefault();
35814             tooltip.hide(button);
35815             setVisible(!button.classed('active'));
35816         }
35817
35818         function setVisible(show) {
35819             if (show !== shown) {
35820                 button.classed('active', show);
35821                 shown = show;
35822
35823                 if (show) {
35824                     selection.on('mousedown.help-inside', function() {
35825                         return d3.event.stopPropagation();
35826                     });
35827                     pane.style('display', 'block')
35828                         .style('right', '-500px')
35829                         .transition()
35830                         .duration(200)
35831                         .style('right', '0px');
35832                 } else {
35833                     pane.style('right', '0px')
35834                         .transition()
35835                         .duration(200)
35836                         .style('right', '-500px')
35837                         .each('end', function() {
35838                             d3.select(this).style('display', 'none');
35839                         });
35840                     selection.on('mousedown.help-inside', null);
35841                 }
35842             }
35843         }
35844
35845         function clickHelp(d, i) {
35846             pane.property('scrollTop', 0);
35847             doctitle.html(d.title);
35848             body.html(d.html);
35849             body.selectAll('a')
35850                 .attr('target', '_blank');
35851             menuItems.classed('selected', function(m) {
35852                 return m.title === d.title;
35853             });
35854
35855             nav.html('');
35856
35857             if (i > 0) {
35858                 var prevLink = nav.append('a')
35859                     .attr('class', 'previous')
35860                     .on('click', function() {
35861                         clickHelp(docs[i - 1], i - 1);
35862                     });
35863                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
35864             }
35865             if (i < docs.length - 1) {
35866                 var nextLink = nav.append('a')
35867                     .attr('class', 'next')
35868                     .on('click', function() {
35869                         clickHelp(docs[i + 1], i + 1);
35870                     });
35871                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
35872             }
35873         }
35874
35875         function clickWalkthrough() {
35876             d3.select(document.body).call(iD.ui.intro(context));
35877             setVisible(false);
35878         }
35879
35880
35881         var pane = selection.append('div')
35882                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
35883             tooltip = bootstrap.tooltip()
35884                 .placement('left')
35885                 .html(true)
35886                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
35887             button = selection.append('button')
35888                 .attr('tabindex', -1)
35889                 .on('click', toggle)
35890                 .call(iD.svg.Icon('#icon-help', 'light'))
35891                 .call(tooltip),
35892             shown = false;
35893
35894
35895         var toc = pane.append('ul')
35896             .attr('class', 'toc');
35897
35898         var menuItems = toc.selectAll('li')
35899             .data(docs)
35900             .enter()
35901             .append('li')
35902             .append('a')
35903             .html(function(d) { return d.title; })
35904             .on('click', clickHelp);
35905
35906         toc.append('li')
35907             .attr('class','walkthrough')
35908             .append('a')
35909             .text(t('splash.walkthrough'))
35910             .on('click', clickWalkthrough);
35911
35912         var content = pane.append('div')
35913             .attr('class', 'left-content');
35914
35915         var doctitle = content.append('h2')
35916             .text(t('help.title'));
35917
35918         var body = content.append('div')
35919             .attr('class', 'body');
35920
35921         var nav = content.append('div')
35922             .attr('class', 'nav');
35923
35924         clickHelp(docs[0], 0);
35925
35926         var keybinding = d3.keybinding('help')
35927             .on(key, toggle)
35928             .on('B', hide)
35929             .on('F', hide);
35930
35931         d3.select(document)
35932             .call(keybinding);
35933
35934         context.surface().on('mousedown.help-outside', hide);
35935         context.container().on('mousedown.help-outside', hide);
35936     }
35937
35938     return help;
35939 };
35940 iD.ui.Info = function(context) {
35941     var key = iD.ui.cmd('⌘I'),
35942         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
35943         hidden = true;
35944
35945     function info(selection) {
35946         function radiansToMeters(r) {
35947             // using WGS84 authalic radius (6371007.1809 m)
35948             return r * 6371007.1809;
35949         }
35950
35951         function steradiansToSqmeters(r) {
35952             // http://gis.stackexchange.com/a/124857/40446
35953             return r / 12.56637 * 510065621724000;
35954         }
35955
35956         function toLineString(feature) {
35957             if (feature.type === 'LineString') return feature;
35958
35959             var result = { type: 'LineString', coordinates: [] };
35960             if (feature.type === 'Polygon') {
35961                 result.coordinates = feature.coordinates[0];
35962             } else if (feature.type === 'MultiPolygon') {
35963                 result.coordinates = feature.coordinates[0][0];
35964             }
35965
35966             return result;
35967         }
35968
35969         function displayLength(m) {
35970             var d = m * (imperial ? 3.28084 : 1),
35971                 p, unit;
35972
35973             if (imperial) {
35974                 if (d >= 5280) {
35975                     d /= 5280;
35976                     unit = 'mi';
35977                 } else {
35978                     unit = 'ft';
35979                 }
35980             } else {
35981                 if (d >= 1000) {
35982                     d /= 1000;
35983                     unit = 'km';
35984                 } else {
35985                     unit = 'm';
35986                 }
35987             }
35988
35989             // drop unnecessary precision
35990             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
35991
35992             return String(d.toFixed(p)) + ' ' + unit;
35993         }
35994
35995         function displayArea(m2) {
35996             var d = m2 * (imperial ? 10.7639111056 : 1),
35997                 d1, d2, p1, p2, unit1, unit2;
35998
35999             if (imperial) {
36000                 if (d >= 6969600) {     // > 0.25mi² show mi²
36001                     d1 = d / 27878400;
36002                     unit1 = 'mi²';
36003                 } else {
36004                     d1 = d;
36005                     unit1 = 'ft²';
36006                 }
36007
36008                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
36009                     d2 = d / 43560;
36010                     unit2 = 'ac';
36011                 }
36012
36013             } else {
36014                 if (d >= 250000) {    // > 0.25km² show km²
36015                     d1 = d / 1000000;
36016                     unit1 = 'km²';
36017                 } else {
36018                     d1 = d;
36019                     unit1 = 'm²';
36020                 }
36021
36022                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
36023                     d2 = d / 10000;
36024                     unit2 = 'ha';
36025                 }
36026             }
36027
36028             // drop unnecessary precision
36029             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
36030             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
36031
36032             return String(d1.toFixed(p1)) + ' ' + unit1 +
36033                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
36034         }
36035
36036
36037         function redraw() {
36038             if (hidden) return;
36039
36040             var resolver = context.graph(),
36041                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
36042                 singular = selected.length === 1 ? selected[0] : null,
36043                 extent = iD.geo.Extent(),
36044                 entity;
36045
36046             wrap.html('');
36047             wrap.append('h4')
36048                 .attr('class', 'infobox-heading fillD')
36049                 .text(singular || t('infobox.selected', { n: selected.length }));
36050
36051             if (!selected.length) return;
36052
36053             var center;
36054             for (var i = 0; i < selected.length; i++) {
36055                 entity = context.entity(selected[i]);
36056                 extent._extend(entity.extent(resolver));
36057             }
36058             center = extent.center();
36059
36060
36061             var list = wrap.append('ul');
36062
36063             // multiple wrap, just display extent center..
36064             if (!singular) {
36065                 list.append('li')
36066                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
36067                 return;
36068             }
36069
36070             // single wrap, display details..
36071             if (!entity) return;
36072             var geometry = entity.geometry(resolver);
36073
36074             if (geometry === 'line' || geometry === 'area') {
36075                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
36076                     feature = entity.asGeoJSON(resolver),
36077                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
36078                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
36079                     centroid = d3.geo.centroid(feature);
36080
36081                 list.append('li')
36082                     .text(t('infobox.geometry') + ': ' +
36083                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
36084
36085                 if (closed) {
36086                     var area = steradiansToSqmeters(entity.area(resolver));
36087                     list.append('li')
36088                         .text(t('infobox.area') + ': ' + displayArea(area));
36089                 }
36090
36091                 list.append('li')
36092                     .text(lengthLabel + ': ' + displayLength(length));
36093
36094                 list.append('li')
36095                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
36096
36097
36098                 var toggle  = imperial ? 'imperial' : 'metric';
36099                 wrap.append('a')
36100                     .text(t('infobox.' + toggle))
36101                     .attr('href', '#')
36102                     .attr('class', 'button')
36103                     .on('click', function() {
36104                         d3.event.preventDefault();
36105                         imperial = !imperial;
36106                         redraw();
36107                     });
36108
36109             } else {
36110                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
36111
36112                 list.append('li')
36113                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
36114
36115                 list.append('li')
36116                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
36117             }
36118         }
36119
36120
36121         function toggle() {
36122             if (d3.event) d3.event.preventDefault();
36123
36124             hidden = !hidden;
36125
36126             if (hidden) {
36127                 wrap
36128                     .style('display', 'block')
36129                     .style('opacity', 1)
36130                     .transition()
36131                     .duration(200)
36132                     .style('opacity', 0)
36133                     .each('end', function() {
36134                         d3.select(this).style('display', 'none');
36135                     });
36136             } else {
36137                 wrap
36138                     .style('display', 'block')
36139                     .style('opacity', 0)
36140                     .transition()
36141                     .duration(200)
36142                     .style('opacity', 1);
36143
36144                 redraw();
36145             }
36146         }
36147
36148
36149         var wrap = selection.selectAll('.infobox')
36150             .data([0]);
36151
36152         wrap.enter()
36153             .append('div')
36154             .attr('class', 'infobox fillD2')
36155             .style('display', (hidden ? 'none' : 'block'));
36156
36157         context.map()
36158             .on('drawn.info', redraw);
36159
36160         redraw();
36161
36162         var keybinding = d3.keybinding('info')
36163             .on(key, toggle);
36164
36165         d3.select(document)
36166             .call(keybinding);
36167     }
36168
36169     return info;
36170 };
36171 iD.ui.Inspector = function(context) {
36172     var presetList = iD.ui.PresetList(context),
36173         entityEditor = iD.ui.EntityEditor(context),
36174         state = 'select',
36175         entityID,
36176         newFeature = false;
36177
36178     function inspector(selection) {
36179         presetList
36180             .entityID(entityID)
36181             .autofocus(newFeature)
36182             .on('choose', setPreset);
36183
36184         entityEditor
36185             .state(state)
36186             .entityID(entityID)
36187             .on('choose', showList);
36188
36189         var $wrap = selection.selectAll('.panewrap')
36190             .data([0]);
36191
36192         var $enter = $wrap.enter().append('div')
36193             .attr('class', 'panewrap');
36194
36195         $enter.append('div')
36196             .attr('class', 'preset-list-pane pane');
36197
36198         $enter.append('div')
36199             .attr('class', 'entity-editor-pane pane');
36200
36201         var $presetPane = $wrap.select('.preset-list-pane');
36202         var $editorPane = $wrap.select('.entity-editor-pane');
36203
36204         var graph = context.graph(),
36205             entity = context.entity(entityID),
36206             showEditor = state === 'hover' ||
36207                 entity.isUsed(graph) ||
36208                 entity.isHighwayIntersection(graph);
36209
36210         if (showEditor) {
36211             $wrap.style('right', '0%');
36212             $editorPane.call(entityEditor);
36213         } else {
36214             $wrap.style('right', '-100%');
36215             $presetPane.call(presetList);
36216         }
36217
36218         var $footer = selection.selectAll('.footer')
36219             .data([0]);
36220
36221         $footer.enter().append('div')
36222             .attr('class', 'footer');
36223
36224         selection.select('.footer')
36225             .call(iD.ui.ViewOnOSM(context)
36226                 .entityID(entityID));
36227
36228         function showList(preset) {
36229             $wrap.transition()
36230                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
36231
36232             $presetPane.call(presetList
36233                 .preset(preset)
36234                 .autofocus(true));
36235         }
36236
36237         function setPreset(preset) {
36238             $wrap.transition()
36239                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
36240
36241             $editorPane.call(entityEditor
36242                 .preset(preset));
36243         }
36244     }
36245
36246     inspector.state = function(_) {
36247         if (!arguments.length) return state;
36248         state = _;
36249         entityEditor.state(state);
36250         return inspector;
36251     };
36252
36253     inspector.entityID = function(_) {
36254         if (!arguments.length) return entityID;
36255         entityID = _;
36256         return inspector;
36257     };
36258
36259     inspector.newFeature = function(_) {
36260         if (!arguments.length) return newFeature;
36261         newFeature = _;
36262         return inspector;
36263     };
36264
36265     return inspector;
36266 };
36267 iD.ui.intro = function(context) {
36268     var step;
36269
36270     function intro(selection) {
36271
36272         function localizedName(id) {
36273             var features = {
36274                 n2140018997: 'city_hall',
36275                 n367813436: 'fire_department',
36276                 w203988286: 'memory_isle_park',
36277                 w203972937: 'riverwalk_trail',
36278                 w203972938: 'riverwalk_trail',
36279                 w203972940: 'riverwalk_trail',
36280                 w41785752: 'w_michigan_ave',
36281                 w134150789: 'w_michigan_ave',
36282                 w134150795: 'w_michigan_ave',
36283                 w134150800: 'w_michigan_ave',
36284                 w134150811: 'w_michigan_ave',
36285                 w134150802: 'e_michigan_ave',
36286                 w134150836: 'e_michigan_ave',
36287                 w41074896: 'e_michigan_ave',
36288                 w17965834: 'spring_st',
36289                 w203986457: 'scidmore_park',
36290                 w203049587: 'petting_zoo',
36291                 w17967397: 'n_andrews_st',
36292                 w17967315: 's_andrews_st',
36293                 w17967326: 'n_constantine_st',
36294                 w17966400: 's_constantine_st',
36295                 w170848823: 'rocky_river',
36296                 w170848824: 'rocky_river',
36297                 w170848331: 'rocky_river',
36298                 w17967752: 'railroad_dr',
36299                 w17965998: 'conrail_rr',
36300                 w134150845: 'conrail_rr',
36301                 w170989131: 'st_joseph_river',
36302                 w143497377: 'n_main_st',
36303                 w134150801: 's_main_st',
36304                 w134150830: 's_main_st',
36305                 w17966462: 's_main_st',
36306                 w17967734: 'water_st',
36307                 w17964996: 'foster_st',
36308                 w170848330: 'portage_river',
36309                 w17965351: 'flower_st',
36310                 w17965502: 'elm_st',
36311                 w17965402: 'walnut_st',
36312                 w17964793: 'morris_ave',
36313                 w17967444: 'east_st',
36314                 w17966984: 'portage_ave'
36315             };
36316             return features[id] && t('intro.graph.' + features[id]);
36317         }
36318
36319         context.enter(iD.modes.Browse(context));
36320
36321         // Save current map state
36322         var history = context.history().toJSON(),
36323             hash = window.location.hash,
36324             center = context.map().center(),
36325             zoom = context.map().zoom(),
36326             background = context.background().baseLayerSource(),
36327             opacity = d3.selectAll('#map .layer-background').style('opacity'),
36328             loadedTiles = context.connection().loadedTiles(),
36329             baseEntities = context.history().graph().base().entities,
36330             introGraph, name;
36331
36332         // Block saving
36333         context.inIntro(true);
36334
36335         // Load semi-real data used in intro
36336         context.connection().toggle(false).flush();
36337         context.history().reset();
36338
36339         introGraph = JSON.parse(iD.introGraph);
36340         for (var key in introGraph) {
36341             introGraph[key] = iD.Entity(introGraph[key]);
36342             name = localizedName(key);
36343             if (name) {
36344                 introGraph[key].tags.name = name;
36345             }
36346         }
36347         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
36348         context.background().bing();
36349
36350         d3.selectAll('#map .layer-background').style('opacity', 1);
36351
36352         var curtain = d3.curtain();
36353         selection.call(curtain);
36354
36355         function reveal(box, text, options) {
36356             options = options || {};
36357             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
36358             else curtain.reveal(box, '', '', options.duration);
36359         }
36360
36361         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
36362             var s = iD.ui.intro[step](context, reveal)
36363                 .on('done', function() {
36364                     entered.filter(function(d) {
36365                         return d.title === s.title;
36366                     }).classed('finished', true);
36367                     enter(steps[i + 1]);
36368                 });
36369             return s;
36370         });
36371
36372         steps[steps.length - 1].on('startEditing', function() {
36373             curtain.remove();
36374             navwrap.remove();
36375             d3.selectAll('#map .layer-background').style('opacity', opacity);
36376             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
36377             context.history().reset().merge(d3.values(baseEntities));
36378             context.background().baseLayerSource(background);
36379             if (history) context.history().fromJSON(history, false);
36380             context.map().centerZoom(center, zoom);
36381             window.location.replace(hash);
36382             context.inIntro(false);
36383         });
36384
36385         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
36386
36387         var buttonwrap = navwrap.append('div')
36388             .attr('class', 'joined')
36389             .selectAll('button.step');
36390
36391         var entered = buttonwrap
36392             .data(steps)
36393             .enter()
36394             .append('button')
36395             .attr('class', 'step')
36396             .on('click', enter);
36397
36398         entered
36399             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
36400
36401         entered
36402             .append('label')
36403             .text(function(d) { return t(d.title); });
36404
36405         enter(steps[0]);
36406
36407         function enter (newStep) {
36408             if (step) { step.exit(); }
36409
36410             context.enter(iD.modes.Browse(context));
36411
36412             step = newStep;
36413             step.enter();
36414
36415             entered.classed('active', function(d) {
36416                 return d.title === step.title;
36417             });
36418         }
36419
36420     }
36421     return intro;
36422 };
36423
36424 iD.ui.intro.pointBox = function(point, context) {
36425     var rect = context.surfaceRect();
36426     point = context.projection(point);
36427     return {
36428         left: point[0] + rect.left - 30,
36429         top: point[1] + rect.top - 50,
36430         width: 60,
36431         height: 70
36432     };
36433 };
36434
36435 iD.ui.intro.pad = function(box, padding, context) {
36436     if (box instanceof Array) {
36437         var rect = context.surfaceRect();
36438         box = context.projection(box);
36439         box = {
36440             left: box[0] + rect.left,
36441             top: box[1] + rect.top
36442         };
36443     }
36444     return {
36445         left: box.left - padding,
36446         top: box.top - padding,
36447         width: (box.width || 0) + 2 * padding,
36448         height: (box.width || 0) + 2 * padding
36449     };
36450 };
36451
36452 iD.ui.intro.icon = function(name, svgklass) {
36453     return '<svg class="icon ' + (svgklass || '') + '">' +
36454         '<use xlink:href="' + name + '"></use></svg>';
36455 };
36456 iD.ui.Lasso = function(context) {
36457     var group, polygon;
36458
36459     lasso.coordinates = [];
36460
36461     function lasso(selection) {
36462
36463         context.container().classed('lasso', true);
36464
36465         group = selection.append('g')
36466             .attr('class', 'lasso hide');
36467
36468         polygon = group.append('path')
36469             .attr('class', 'lasso-path');
36470
36471         group.call(iD.ui.Toggle(true));
36472
36473     }
36474
36475     function draw() {
36476         if (polygon) {
36477             polygon.data([lasso.coordinates])
36478                 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
36479         }
36480     }
36481
36482     lasso.extent = function () {
36483         return lasso.coordinates.reduce(function(extent, point) {
36484             return extent.extend(iD.geo.Extent(point));
36485         }, iD.geo.Extent());
36486     };
36487
36488     lasso.p = function(_) {
36489         if (!arguments.length) return lasso;
36490         lasso.coordinates.push(_);
36491         draw();
36492         return lasso;
36493     };
36494
36495     lasso.close = function() {
36496         if (group) {
36497             group.call(iD.ui.Toggle(false, function() {
36498                 d3.select(this).remove();
36499             }));
36500         }
36501         context.container().classed('lasso', false);
36502     };
36503
36504     return lasso;
36505 };
36506 iD.ui.Loading = function(context) {
36507     var message = '',
36508         blocking = false,
36509         modal;
36510
36511     var loading = function(selection) {
36512         modal = iD.ui.modal(selection, blocking);
36513
36514         var loadertext = modal.select('.content')
36515             .classed('loading-modal', true)
36516             .append('div')
36517             .attr('class', 'modal-section fillL');
36518
36519         loadertext.append('img')
36520             .attr('class', 'loader')
36521             .attr('src', context.imagePath('loader-white.gif'));
36522
36523         loadertext.append('h3')
36524             .text(message);
36525
36526         modal.select('button.close')
36527             .attr('class', 'hide');
36528
36529         return loading;
36530     };
36531
36532     loading.message = function(_) {
36533         if (!arguments.length) return message;
36534         message = _;
36535         return loading;
36536     };
36537
36538     loading.blocking = function(_) {
36539         if (!arguments.length) return blocking;
36540         blocking = _;
36541         return loading;
36542     };
36543
36544     loading.close = function() {
36545         modal.remove();
36546     };
36547
36548     return loading;
36549 };
36550 iD.ui.MapData = function(context) {
36551     var key = 'F',
36552         features = context.features().keys(),
36553         layers = context.layers(),
36554         fills = ['wireframe', 'partial', 'full'],
36555         fillDefault = context.storage('area-fill') || 'partial',
36556         fillSelected = fillDefault;
36557
36558
36559     function map_data(selection) {
36560
36561         function showsFeature(d) {
36562             return context.features().enabled(d);
36563         }
36564
36565         function autoHiddenFeature(d) {
36566             return context.features().autoHidden(d);
36567         }
36568
36569         function clickFeature(d) {
36570             context.features().toggle(d);
36571             update();
36572         }
36573
36574         function showsFill(d) {
36575             return fillSelected === d;
36576         }
36577
36578         function setFill(d) {
36579             _.each(fills, function(opt) {
36580                 context.surface().classed('fill-' + opt, Boolean(opt === d));
36581             });
36582
36583             fillSelected = d;
36584             if (d !== 'wireframe') {
36585                 fillDefault = d;
36586                 context.storage('area-fill', d);
36587             }
36588             update();
36589         }
36590
36591         function toggleLayer(which) {
36592             var layer = layers.layer(which);
36593             if (layer) {
36594                 layer.enabled(!layer.enabled());
36595                 update();
36596             }
36597         }
36598
36599         function clickGpx() {
36600             toggleLayer('gpx');
36601         }
36602
36603         function clickMapillaryImages() {
36604             toggleLayer('mapillary-images');
36605         }
36606
36607         function clickMapillarySigns() {
36608             toggleLayer('mapillary-signs');
36609         }
36610
36611
36612         function drawMapillaryItems(selection) {
36613             var mapillaryImages = layers.layer('mapillary-images'),
36614                 mapillarySigns = layers.layer('mapillary-signs'),
36615                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
36616                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
36617                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
36618                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
36619
36620             var mapillaryList = selection
36621                 .selectAll('.layer-list-mapillary')
36622                 .data([0]);
36623
36624             // Enter
36625             mapillaryList
36626                 .enter()
36627                 .append('ul')
36628                 .attr('class', 'layer-list layer-list-mapillary');
36629
36630             var mapillaryImageLayerItem = mapillaryList
36631                 .selectAll('.list-item-mapillary-images')
36632                 .data(supportsMapillaryImages ? [0] : []);
36633
36634             var enterImages = mapillaryImageLayerItem.enter()
36635                 .append('li')
36636                 .attr('class', 'list-item-mapillary-images');
36637
36638             var labelImages = enterImages.append('label')
36639                 .call(bootstrap.tooltip()
36640                     .title(t('mapillary_images.tooltip'))
36641                     .placement('top'));
36642
36643             labelImages.append('input')
36644                 .attr('type', 'checkbox')
36645                 .on('change', clickMapillaryImages);
36646
36647             labelImages.append('span')
36648                 .text(t('mapillary_images.title'));
36649
36650
36651             var mapillarySignLayerItem = mapillaryList
36652                 .selectAll('.list-item-mapillary-signs')
36653                 .data(supportsMapillarySigns ? [0] : []);
36654
36655             var enterSigns = mapillarySignLayerItem.enter()
36656                 .append('li')
36657                 .attr('class', 'list-item-mapillary-signs');
36658
36659             var labelSigns = enterSigns.append('label')
36660                 .call(bootstrap.tooltip()
36661                     .title(t('mapillary_signs.tooltip'))
36662                     .placement('top'));
36663
36664             labelSigns.append('input')
36665                 .attr('type', 'checkbox')
36666                 .on('change', clickMapillarySigns);
36667
36668             labelSigns.append('span')
36669                 .text(t('mapillary_signs.title'));
36670
36671             // Update
36672             mapillaryImageLayerItem
36673                 .classed('active', showsMapillaryImages)
36674                 .selectAll('input')
36675                 .property('checked', showsMapillaryImages);
36676
36677             mapillarySignLayerItem
36678                 .classed('active', showsMapillarySigns)
36679                 .selectAll('input')
36680                 .property('checked', showsMapillarySigns);
36681
36682             // Exit
36683             mapillaryImageLayerItem.exit()
36684                 .remove();
36685             mapillarySignLayerItem.exit()
36686                 .remove();
36687         }
36688
36689
36690         function drawGpxItem(selection) {
36691             var gpx = layers.layer('gpx'),
36692                 hasGpx = gpx && gpx.hasGpx(),
36693                 showsGpx = hasGpx && gpx.enabled();
36694
36695             var gpxLayerItem = selection
36696                 .selectAll('.layer-list-gpx')
36697                 .data(gpx ? [0] : []);
36698
36699             // Enter
36700             var enter = gpxLayerItem.enter()
36701                 .append('ul')
36702                 .attr('class', 'layer-list layer-list-gpx')
36703                 .append('li')
36704                 .classed('list-item-gpx', true);
36705
36706             enter.append('button')
36707                 .attr('class', 'list-item-gpx-extent')
36708                 .call(bootstrap.tooltip()
36709                     .title(t('gpx.zoom'))
36710                     .placement('left'))
36711                 .on('click', function() {
36712                     d3.event.preventDefault();
36713                     d3.event.stopPropagation();
36714                     gpx.fitZoom();
36715                 })
36716                 .call(iD.svg.Icon('#icon-search'));
36717
36718             enter.append('button')
36719                 .attr('class', 'list-item-gpx-browse')
36720                 .call(bootstrap.tooltip()
36721                     .title(t('gpx.browse'))
36722                     .placement('left'))
36723                 .on('click', function() {
36724                     d3.select(document.createElement('input'))
36725                         .attr('type', 'file')
36726                         .on('change', function() {
36727                             gpx.files(d3.event.target.files);
36728                         })
36729                         .node().click();
36730                 })
36731                 .call(iD.svg.Icon('#icon-geolocate'));
36732
36733             var labelGpx = enter.append('label')
36734                 .call(bootstrap.tooltip()
36735                     .title(t('gpx.drag_drop'))
36736                     .placement('top'));
36737
36738             labelGpx.append('input')
36739                 .attr('type', 'checkbox')
36740                 .on('change', clickGpx);
36741
36742             labelGpx.append('span')
36743                 .text(t('gpx.local_layer'));
36744
36745             // Update
36746             gpxLayerItem
36747                 .classed('active', showsGpx)
36748                 .selectAll('input')
36749                 .property('disabled', !hasGpx)
36750                 .property('checked', showsGpx);
36751
36752             // Exit
36753             gpxLayerItem.exit()
36754                 .remove();
36755         }
36756
36757
36758         function drawList(selection, data, type, name, change, active) {
36759             var items = selection.selectAll('li')
36760                 .data(data);
36761
36762             // Enter
36763             var enter = items.enter()
36764                 .append('li')
36765                 .attr('class', 'layer')
36766                 .call(bootstrap.tooltip()
36767                     .html(true)
36768                     .title(function(d) {
36769                         var tip = t(name + '.' + d + '.tooltip'),
36770                             key = (d === 'wireframe' ? 'W' : null);
36771
36772                         if (name === 'feature' && autoHiddenFeature(d)) {
36773                             tip += '<div>' + t('map_data.autohidden') + '</div>';
36774                         }
36775                         return iD.ui.tooltipHtml(tip, key);
36776                     })
36777                     .placement('top')
36778                 );
36779
36780             var label = enter.append('label');
36781
36782             label.append('input')
36783                 .attr('type', type)
36784                 .attr('name', name)
36785                 .on('change', change);
36786
36787             label.append('span')
36788                 .text(function(d) { return t(name + '.' + d + '.description'); });
36789
36790             // Update
36791             items
36792                 .classed('active', active)
36793                 .selectAll('input')
36794                 .property('checked', active)
36795                 .property('indeterminate', function(d) {
36796                     return (name === 'feature' && autoHiddenFeature(d));
36797                 });
36798
36799             // Exit
36800             items.exit()
36801                 .remove();
36802         }
36803
36804
36805         function update() {
36806             dataLayerContainer.call(drawMapillaryItems);
36807             dataLayerContainer.call(drawGpxItem);
36808
36809             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
36810
36811             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
36812         }
36813
36814         function hidePanel() {
36815             setVisible(false);
36816         }
36817
36818         function togglePanel() {
36819             if (d3.event) d3.event.preventDefault();
36820             tooltip.hide(button);
36821             setVisible(!button.classed('active'));
36822         }
36823
36824         function toggleWireframe() {
36825             if (d3.event) {
36826                 d3.event.preventDefault();
36827                 d3.event.stopPropagation();
36828             }
36829             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
36830             context.map().pan([0,0]);  // trigger a redraw
36831         }
36832
36833         function setVisible(show) {
36834             if (show !== shown) {
36835                 button.classed('active', show);
36836                 shown = show;
36837
36838                 if (show) {
36839                     update();
36840                     selection.on('mousedown.map_data-inside', function() {
36841                         return d3.event.stopPropagation();
36842                     });
36843                     content.style('display', 'block')
36844                         .style('right', '-300px')
36845                         .transition()
36846                         .duration(200)
36847                         .style('right', '0px');
36848                 } else {
36849                     content.style('display', 'block')
36850                         .style('right', '0px')
36851                         .transition()
36852                         .duration(200)
36853                         .style('right', '-300px')
36854                         .each('end', function() {
36855                             d3.select(this).style('display', 'none');
36856                         });
36857                     selection.on('mousedown.map_data-inside', null);
36858                 }
36859             }
36860         }
36861
36862
36863         var content = selection.append('div')
36864                 .attr('class', 'fillL map-overlay col3 content hide'),
36865             tooltip = bootstrap.tooltip()
36866                 .placement('left')
36867                 .html(true)
36868                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
36869             button = selection.append('button')
36870                 .attr('tabindex', -1)
36871                 .on('click', togglePanel)
36872                 .call(iD.svg.Icon('#icon-data', 'light'))
36873                 .call(tooltip),
36874             shown = false;
36875
36876         content.append('h4')
36877             .text(t('map_data.title'));
36878
36879
36880         // data layers
36881         content.append('a')
36882             .text(t('map_data.data_layers'))
36883             .attr('href', '#')
36884             .classed('hide-toggle', true)
36885             .classed('expanded', true)
36886             .on('click', function() {
36887                 var exp = d3.select(this).classed('expanded');
36888                 dataLayerContainer.style('display', exp ? 'none' : 'block');
36889                 d3.select(this).classed('expanded', !exp);
36890                 d3.event.preventDefault();
36891             });
36892
36893         var dataLayerContainer = content.append('div')
36894             .attr('class', 'data-data-layers')
36895             .style('display', 'block');
36896
36897
36898         // area fills
36899         content.append('a')
36900             .text(t('map_data.fill_area'))
36901             .attr('href', '#')
36902             .classed('hide-toggle', true)
36903             .classed('expanded', false)
36904             .on('click', function() {
36905                 var exp = d3.select(this).classed('expanded');
36906                 fillContainer.style('display', exp ? 'none' : 'block');
36907                 d3.select(this).classed('expanded', !exp);
36908                 d3.event.preventDefault();
36909             });
36910
36911         var fillContainer = content.append('div')
36912             .attr('class', 'data-area-fills')
36913             .style('display', 'none');
36914
36915         var fillList = fillContainer.append('ul')
36916             .attr('class', 'layer-list layer-fill-list');
36917
36918
36919         // feature filters
36920         content.append('a')
36921             .text(t('map_data.map_features'))
36922             .attr('href', '#')
36923             .classed('hide-toggle', true)
36924             .classed('expanded', false)
36925             .on('click', function() {
36926                 var exp = d3.select(this).classed('expanded');
36927                 featureContainer.style('display', exp ? 'none' : 'block');
36928                 d3.select(this).classed('expanded', !exp);
36929                 d3.event.preventDefault();
36930             });
36931
36932         var featureContainer = content.append('div')
36933             .attr('class', 'data-feature-filters')
36934             .style('display', 'none');
36935
36936         var featureList = featureContainer.append('ul')
36937             .attr('class', 'layer-list layer-feature-list');
36938
36939
36940         context.features()
36941             .on('change.map_data-update', update);
36942
36943         setFill(fillDefault);
36944
36945         var keybinding = d3.keybinding('features')
36946             .on(key, togglePanel)
36947             .on('W', toggleWireframe)
36948             .on('B', hidePanel)
36949             .on('H', hidePanel);
36950
36951         d3.select(document)
36952             .call(keybinding);
36953
36954         context.surface().on('mousedown.map_data-outside', hidePanel);
36955         context.container().on('mousedown.map_data-outside', hidePanel);
36956     }
36957
36958     return map_data;
36959 };
36960 iD.ui.MapInMap = function(context) {
36961     var key = '/';
36962
36963     function map_in_map(selection) {
36964         var backgroundLayer = iD.TileLayer(context),
36965             overlayLayers = {},
36966             projection = iD.geo.RawMercator(),
36967             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
36968             zoom = d3.behavior.zoom()
36969                 .scaleExtent([ztok(0.5), ztok(24)])
36970                 .on('zoom', zoomPan),
36971             transformed = false,
36972             panning = false,
36973             hidden = true,
36974             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
36975             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
36976
36977         function ztok(z) { return 256 * Math.pow(2, z); }
36978         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
36979
36980
36981         function startMouse() {
36982             context.surface().on('mouseup.map-in-map-outside', endMouse);
36983             context.container().on('mouseup.map-in-map-outside', endMouse);
36984
36985             tStart = tLast = tCurr = projection.translate();
36986             panning = true;
36987         }
36988
36989
36990         function zoomPan() {
36991             var e = d3.event.sourceEvent,
36992                 t = d3.event.translate,
36993                 k = d3.event.scale,
36994                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
36995                 zMini = ktoz(k);
36996
36997             // restrict minimap zoom to < (main zoom - 3)
36998             if (zMini > zMain - 3) {
36999                 zMini = zMain - 3;
37000                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
37001                 return;
37002             }
37003
37004             tCurr = t;
37005             kCurr = k;
37006             zDiff = zMain - zMini;
37007
37008             var scale = kCurr / kLast,
37009                 tX = (tCurr[0] / scale - tLast[0]) * scale,
37010                 tY = (tCurr[1] / scale - tLast[1]) * scale;
37011
37012             iD.util.setTransform(tiles, tX, tY, scale);
37013             iD.util.setTransform(svg, 0, 0, scale);
37014             transformed = true;
37015
37016             queueRedraw();
37017
37018             e.preventDefault();
37019             e.stopPropagation();
37020         }
37021
37022
37023         function endMouse() {
37024             context.surface().on('mouseup.map-in-map-outside', null);
37025             context.container().on('mouseup.map-in-map-outside', null);
37026
37027             updateProjection();
37028             panning = false;
37029
37030             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
37031                 var dMini = wrap.dimensions(),
37032                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
37033
37034                 context.map().center(projection.invert(cMini));
37035             }
37036         }
37037
37038
37039         function updateProjection() {
37040             var loc = context.map().center(),
37041                 dMini = wrap.dimensions(),
37042                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
37043                 tMain = context.projection.translate(),
37044                 kMain = context.projection.scale(),
37045                 zMain = ktoz(kMain * 2 * Math.PI),
37046                 zMini = Math.max(zMain - zDiff, 0.5),
37047                 kMini = ztok(zMini);
37048
37049             projection
37050                 .translate(tMain)
37051                 .scale(kMini / (2 * Math.PI));
37052
37053             var s = projection(loc),
37054                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
37055                 tMini = [
37056                     cMini[0] - s[0] + tMain[0] + mouse[0],
37057                     cMini[1] - s[1] + tMain[1] + mouse[1]
37058                 ];
37059
37060             projection
37061                 .translate(tMini)
37062                 .clipExtent([[0, 0], dMini]);
37063
37064             zoom
37065                 .center(cMini)
37066                 .translate(tMini)
37067                 .scale(kMini);
37068
37069             tLast = tCurr = tMini;
37070             kLast = kCurr = kMini;
37071
37072             if (transformed) {
37073                 iD.util.setTransform(tiles, 0, 0);
37074                 iD.util.setTransform(svg, 0, 0);
37075                 transformed = false;
37076             }
37077         }
37078
37079
37080         function redraw() {
37081             if (hidden) return;
37082
37083             updateProjection();
37084
37085             var dMini = wrap.dimensions(),
37086                 zMini = ktoz(projection.scale() * 2 * Math.PI);
37087
37088             // setup tile container
37089             tiles = wrap
37090                 .selectAll('.map-in-map-tiles')
37091                 .data([0]);
37092
37093             tiles
37094                 .enter()
37095                 .append('div')
37096                 .attr('class', 'map-in-map-tiles');
37097
37098             // redraw background
37099             backgroundLayer
37100                 .source(context.background().baseLayerSource())
37101                 .projection(projection)
37102                 .dimensions(dMini);
37103
37104             var background = tiles
37105                 .selectAll('.map-in-map-background')
37106                 .data([0]);
37107
37108             background.enter()
37109                 .append('div')
37110                 .attr('class', 'map-in-map-background');
37111
37112             background
37113                 .call(backgroundLayer);
37114
37115
37116             // redraw overlay
37117             var overlaySources = context.background().overlayLayerSources();
37118             var activeOverlayLayers = [];
37119             for (var i = 0; i < overlaySources.length; i++) {
37120                 if (overlaySources[i].validZoom(zMini)) {
37121                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer(context);
37122                     activeOverlayLayers.push(overlayLayers[i]
37123                         .source(overlaySources[i])
37124                         .projection(projection)
37125                         .dimensions(dMini));
37126                 }
37127             }
37128
37129             var overlay = tiles
37130                 .selectAll('.map-in-map-overlay')
37131                 .data([0]);
37132
37133             overlay.enter()
37134                 .append('div')
37135                 .attr('class', 'map-in-map-overlay');
37136
37137             var overlays = overlay
37138                 .selectAll('div')
37139                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
37140
37141             overlays.enter().append('div');
37142             overlays.each(function(layer) {
37143                 d3.select(this).call(layer);
37144             });
37145
37146             overlays.exit()
37147                 .remove();
37148
37149
37150             var gpx = tiles
37151                 .selectAll('.map-in-map-gpx')
37152                 .data(gpxLayer.enabled() ? [0] : []);
37153
37154             gpx.enter()
37155                 .append('svg')
37156                 .attr('class', 'map-in-map-gpx');
37157
37158             gpx.exit()
37159                 .remove();
37160
37161             gpx.call(gpxLayer);
37162
37163
37164             // redraw bounding box
37165             if (!panning) {
37166                 var getPath = d3.geo.path().projection(projection),
37167                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
37168
37169                 svg = wrap.selectAll('.map-in-map-svg')
37170                     .data([0]);
37171
37172                 svg.enter()
37173                     .append('svg')
37174                     .attr('class', 'map-in-map-svg');
37175
37176                 var path = svg.selectAll('.map-in-map-bbox')
37177                     .data([bbox]);
37178
37179                 path.enter()
37180                     .append('path')
37181                     .attr('class', 'map-in-map-bbox');
37182
37183                 path
37184                     .attr('d', getPath)
37185                     .classed('thick', function(d) { return getPath.area(d) < 30; });
37186             }
37187         }
37188
37189
37190         function queueRedraw() {
37191             clearTimeout(timeoutId);
37192             timeoutId = setTimeout(function() { redraw(); }, 300);
37193         }
37194
37195
37196         function toggle() {
37197             if (d3.event) d3.event.preventDefault();
37198
37199             hidden = !hidden;
37200
37201             var label = d3.select('.minimap-toggle');
37202             label.classed('active', !hidden)
37203                 .select('input').property('checked', !hidden);
37204
37205             if (hidden) {
37206                 wrap
37207                     .style('display', 'block')
37208                     .style('opacity', 1)
37209                     .transition()
37210                     .duration(200)
37211                     .style('opacity', 0)
37212                     .each('end', function() {
37213                         d3.select(this).style('display', 'none');
37214                     });
37215             } else {
37216                 wrap
37217                     .style('display', 'block')
37218                     .style('opacity', 0)
37219                     .transition()
37220                     .duration(200)
37221                     .style('opacity', 1);
37222
37223                 redraw();
37224             }
37225         }
37226
37227         iD.ui.MapInMap.toggle = toggle;
37228
37229         var wrap = selection.selectAll('.map-in-map')
37230             .data([0]);
37231
37232         wrap.enter()
37233             .append('div')
37234             .attr('class', 'map-in-map')
37235             .style('display', (hidden ? 'none' : 'block'))
37236             .on('mousedown.map-in-map', startMouse)
37237             .on('mouseup.map-in-map', endMouse)
37238             .call(zoom)
37239             .on('dblclick.zoom', null);
37240
37241         context.map()
37242             .on('drawn.map-in-map', function(drawn) {
37243                 if (drawn.full === true) redraw();
37244             });
37245
37246         redraw();
37247
37248         var keybinding = d3.keybinding('map-in-map')
37249             .on(key, toggle);
37250
37251         d3.select(document)
37252             .call(keybinding);
37253     }
37254
37255     return map_in_map;
37256 };
37257 iD.ui.modal = function(selection, blocking) {
37258     var keybinding = d3.keybinding('modal');
37259     var previous = selection.select('div.modal');
37260     var animate = previous.empty();
37261
37262     previous.transition()
37263         .duration(200)
37264         .style('opacity', 0)
37265         .remove();
37266
37267     var shaded = selection
37268         .append('div')
37269         .attr('class', 'shaded')
37270         .style('opacity', 0);
37271
37272     shaded.close = function() {
37273         shaded
37274             .transition()
37275             .duration(200)
37276             .style('opacity',0)
37277             .remove();
37278         modal
37279             .transition()
37280             .duration(200)
37281             .style('top','0px');
37282
37283         keybinding.off();
37284     };
37285
37286
37287     var modal = shaded.append('div')
37288         .attr('class', 'modal fillL col6');
37289
37290     if (!blocking) {
37291         shaded.on('click.remove-modal', function() {
37292             if (d3.event.target === this) {
37293                 shaded.close();
37294             }
37295         });
37296
37297         modal.append('button')
37298             .attr('class', 'close')
37299             .on('click', shaded.close)
37300             .call(iD.svg.Icon('#icon-close'));
37301
37302         keybinding
37303             .on('⌫', shaded.close)
37304             .on('⎋', shaded.close);
37305
37306         d3.select(document).call(keybinding);
37307     }
37308
37309     modal.append('div')
37310         .attr('class', 'content');
37311
37312     if (animate) {
37313         shaded.transition().style('opacity', 1);
37314     } else {
37315         shaded.style('opacity', 1);
37316     }
37317
37318     return shaded;
37319 };
37320 iD.ui.Modes = function(context) {
37321     var modes = [
37322         iD.modes.AddPoint(context),
37323         iD.modes.AddLine(context),
37324         iD.modes.AddArea(context)];
37325
37326     function editable() {
37327         return context.editable() && context.mode().id !== 'save';
37328     }
37329
37330     return function(selection) {
37331         var buttons = selection.selectAll('button.add-button')
37332             .data(modes);
37333
37334        buttons.enter().append('button')
37335            .attr('tabindex', -1)
37336            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
37337            .on('click.mode-buttons', function(mode) {
37338                if (mode.id === context.mode().id) {
37339                    context.enter(iD.modes.Browse(context));
37340                } else {
37341                    context.enter(mode);
37342                }
37343            })
37344            .call(bootstrap.tooltip()
37345                .placement('bottom')
37346                .html(true)
37347                .title(function(mode) {
37348                    return iD.ui.tooltipHtml(mode.description, mode.key);
37349                }));
37350
37351         context.map()
37352             .on('move.modes', _.debounce(update, 500));
37353
37354         context
37355             .on('enter.modes', update);
37356
37357         buttons.each(function(d) {
37358             d3.select(this)
37359                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
37360         });
37361
37362         buttons.append('span')
37363             .attr('class', 'label')
37364             .text(function(mode) { return mode.title; });
37365
37366         context.on('enter.editor', function(entered) {
37367             buttons.classed('active', function(mode) { return entered.button === mode.button; });
37368             context.container()
37369                 .classed('mode-' + entered.id, true);
37370         });
37371
37372         context.on('exit.editor', function(exited) {
37373             context.container()
37374                 .classed('mode-' + exited.id, false);
37375         });
37376
37377         var keybinding = d3.keybinding('mode-buttons');
37378
37379         modes.forEach(function(m) {
37380             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
37381         });
37382
37383         d3.select(document)
37384             .call(keybinding);
37385
37386         function update() {
37387             buttons.property('disabled', !editable());
37388         }
37389     };
37390 };
37391 iD.ui.Notice = function(context) {
37392     return function(selection) {
37393         var div = selection.append('div')
37394             .attr('class', 'notice');
37395
37396         var button = div.append('button')
37397             .attr('class', 'zoom-to notice')
37398             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
37399
37400         button
37401             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
37402             .append('span')
37403             .attr('class', 'label')
37404             .text(t('zoom_in_edit'));
37405
37406         function disableTooHigh() {
37407             div.style('display', context.editable() ? 'none' : 'block');
37408         }
37409
37410         context.map()
37411             .on('move.notice', _.debounce(disableTooHigh, 500));
37412
37413         disableTooHigh();
37414     };
37415 };
37416 iD.ui.PresetIcon = function() {
37417     var preset, geometry;
37418
37419     function presetIcon(selection) {
37420         selection.each(render);
37421     }
37422
37423     function render() {
37424         var selection = d3.select(this),
37425             p = preset.apply(this, arguments),
37426             geom = geometry.apply(this, arguments),
37427             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
37428             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
37429
37430         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
37431
37432         function tag_classes(p) {
37433             var s = '';
37434             for (var i in p.tags) {
37435                 s += ' tag-' + i;
37436                 if (p.tags[i] !== '*') {
37437                     s += ' tag-' + i + '-' + p.tags[i];
37438                 }
37439             }
37440             return s;
37441         }
37442
37443         var $fill = selection.selectAll('.preset-icon-fill')
37444             .data([0]);
37445
37446         $fill.enter().append('div');
37447
37448         $fill.attr('class', function() {
37449             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
37450         });
37451
37452         var $frame = selection.selectAll('.preset-icon-frame')
37453             .data([0]);
37454
37455         $frame.enter()
37456             .append('div')
37457             .call(iD.svg.Icon('#preset-icon-frame'));
37458
37459         $frame.attr('class', function() {
37460             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
37461         });
37462
37463
37464         var $icon = selection.selectAll('.preset-icon')
37465             .data([0]);
37466
37467         $icon.enter()
37468             .append('div')
37469             .attr('class', 'preset-icon')
37470             .call(iD.svg.Icon(''));
37471
37472         $icon
37473             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
37474
37475         $icon.selectAll('svg')
37476             .attr('class', function() {
37477                 return 'icon ' + icon + tag_classes(p);
37478             });
37479
37480         $icon.selectAll('use')       // workaround: maki parking-24 broken?
37481             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
37482     }
37483
37484     presetIcon.preset = function(_) {
37485         if (!arguments.length) return preset;
37486         preset = d3.functor(_);
37487         return presetIcon;
37488     };
37489
37490     presetIcon.geometry = function(_) {
37491         if (!arguments.length) return geometry;
37492         geometry = d3.functor(_);
37493         return presetIcon;
37494     };
37495
37496     return presetIcon;
37497 };
37498 iD.ui.preset = function(context) {
37499     var event = d3.dispatch('change'),
37500         state,
37501         fields,
37502         preset,
37503         tags,
37504         id;
37505
37506     function UIField(field, entity, show) {
37507         field = _.clone(field);
37508
37509         field.input = iD.ui.preset[field.type](field, context)
37510             .on('change', event.change);
37511
37512         if (field.input.entity) field.input.entity(entity);
37513
37514         field.keys = field.keys || [field.key];
37515
37516         field.show = show;
37517
37518         field.shown = function() {
37519             return field.id === 'name' || field.show || _.some(field.keys, function(key) { return !!tags[key]; });
37520         };
37521
37522         field.modified = function() {
37523             var original = context.graph().base().entities[entity.id];
37524             return _.some(field.keys, function(key) {
37525                 return original ? tags[key] !== original.tags[key] : tags[key];
37526             });
37527         };
37528
37529         field.revert = function() {
37530             var original = context.graph().base().entities[entity.id],
37531                 t = {};
37532             field.keys.forEach(function(key) {
37533                 t[key] = original ? original.tags[key] : undefined;
37534             });
37535             return t;
37536         };
37537
37538         field.present = function() {
37539             return _.some(field.keys, function(key) {
37540                 return tags[key];
37541             });
37542         };
37543
37544         field.remove = function() {
37545             var t = {};
37546             field.keys.forEach(function(key) {
37547                 t[key] = undefined;
37548             });
37549             return t;
37550         };
37551
37552         return field;
37553     }
37554
37555     function fieldKey(field) {
37556         return field.id;
37557     }
37558
37559     function presets(selection) {
37560         selection.call(iD.ui.Disclosure()
37561             .title(t('inspector.all_fields'))
37562             .expanded(context.storage('preset_fields.expanded') !== 'false')
37563             .on('toggled', toggled)
37564             .content(content));
37565
37566         function toggled(expanded) {
37567             context.storage('preset_fields.expanded', expanded);
37568         }
37569     }
37570
37571     function content(selection) {
37572         if (!fields) {
37573             var entity = context.entity(id),
37574                 geometry = context.geometry(id);
37575
37576             fields = [UIField(context.presets().field('name'), entity)];
37577
37578             preset.fields.forEach(function(field) {
37579                 if (field.matchGeometry(geometry)) {
37580                     fields.push(UIField(field, entity, true));
37581                 }
37582             });
37583
37584             if (entity.isHighwayIntersection(context.graph())) {
37585                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
37586             }
37587
37588             context.presets().universal().forEach(function(field) {
37589                 if (preset.fields.indexOf(field) < 0) {
37590                     fields.push(UIField(field, entity));
37591                 }
37592             });
37593         }
37594
37595         var shown = fields.filter(function(field) { return field.shown(); }),
37596             notShown = fields.filter(function(field) { return !field.shown(); });
37597
37598         var $form = selection.selectAll('.preset-form')
37599             .data([0]);
37600
37601         $form.enter().append('div')
37602             .attr('class', 'preset-form inspector-inner fillL3');
37603
37604         var $fields = $form.selectAll('.form-field')
37605             .data(shown, fieldKey);
37606
37607         // Enter
37608
37609         var $enter = $fields.enter()
37610             .append('div')
37611             .attr('class', function(field) {
37612                 return 'form-field form-field-' + field.id;
37613             });
37614
37615         var $label = $enter.append('label')
37616             .attr('class', 'form-label')
37617             .attr('for', function(field) { return 'preset-input-' + field.id; })
37618             .text(function(field) { return field.label(); });
37619
37620         var wrap = $label.append('div')
37621             .attr('class', 'form-label-button-wrap');
37622
37623         wrap.append('button')
37624             .attr('class', 'remove-icon')
37625             .attr('tabindex', -1)
37626             .call(iD.svg.Icon('#operation-delete'));
37627
37628         wrap.append('button')
37629             .attr('class', 'modified-icon')
37630             .attr('tabindex', -1)
37631             .call(iD.svg.Icon('#icon-undo'));
37632
37633         // Update
37634
37635         $fields.select('.form-label-button-wrap .remove-icon')
37636             .on('click', remove);
37637
37638         $fields.select('.modified-icon')
37639             .on('click', revert);
37640
37641         $fields
37642             .order()
37643             .classed('modified', function(field) {
37644                 return field.modified();
37645             })
37646             .classed('present', function(field) {
37647                 return field.present();
37648             })
37649             .each(function(field) {
37650                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
37651
37652                 if (state === 'hover') {
37653                     reference.showing(false);
37654                 }
37655
37656                 d3.select(this)
37657                     .call(field.input)
37658                     .selectAll('input')
37659                     .on('keydown', function() {
37660                         // if user presses enter, and combobox is not active, accept edits..
37661                         if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
37662                             context.enter(iD.modes.Browse(context));
37663                         }
37664                     })
37665                     .call(reference.body)
37666                     .select('.form-label-button-wrap')
37667                     .call(reference.button);
37668
37669                 field.input.tags(tags);
37670             });
37671
37672         $fields.exit()
37673             .remove();
37674
37675         notShown = notShown.map(function(field) {
37676             return {
37677                 title: field.label(),
37678                 value: field.label(),
37679                 field: field
37680             };
37681         });
37682
37683         var $more = selection.selectAll('.more-fields')
37684             .data((notShown.length > 0) ? [0] : []);
37685
37686         $more.enter().append('div')
37687             .attr('class', 'more-fields')
37688             .append('label')
37689                 .text(t('inspector.add_fields'));
37690
37691         var $input = $more.selectAll('.value')
37692             .data([0]);
37693
37694         $input.enter().append('input')
37695             .attr('class', 'value')
37696             .attr('type', 'text');
37697
37698         $input.value('')
37699             .attr('placeholder', function() {
37700                 var placeholder = [];
37701                 for (var field in notShown) {
37702                     placeholder.push(notShown[field].title);
37703                 }
37704                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
37705             })
37706             .call(d3.combobox().data(notShown)
37707                 .minItems(1)
37708                 .on('accept', show));
37709
37710         $more.exit()
37711             .remove();
37712
37713         $input.exit()
37714             .remove();
37715
37716         function show(field) {
37717             field = field.field;
37718             field.show = true;
37719             content(selection);
37720             field.input.focus();
37721         }
37722
37723         function revert(field) {
37724             d3.event.stopPropagation();
37725             d3.event.preventDefault();
37726             event.change(field.revert());
37727         }
37728
37729         function remove(field) {
37730             d3.event.stopPropagation();
37731             d3.event.preventDefault();
37732             event.change(field.remove());
37733         }
37734     }
37735
37736     presets.preset = function(_) {
37737         if (!arguments.length) return preset;
37738         if (preset && preset.id === _.id) return presets;
37739         preset = _;
37740         fields = null;
37741         return presets;
37742     };
37743
37744     presets.state = function(_) {
37745         if (!arguments.length) return state;
37746         state = _;
37747         return presets;
37748     };
37749
37750     presets.tags = function(_) {
37751         if (!arguments.length) return tags;
37752         tags = _;
37753         // Don't reset fields here.
37754         return presets;
37755     };
37756
37757     presets.entityID = function(_) {
37758         if (!arguments.length) return id;
37759         if (id === _) return presets;
37760         id = _;
37761         fields = null;
37762         return presets;
37763     };
37764
37765     return d3.rebind(presets, event, 'on');
37766 };
37767 iD.ui.PresetList = function(context) {
37768     var event = d3.dispatch('choose'),
37769         id,
37770         currentPreset,
37771         autofocus = false;
37772
37773     function presetList(selection) {
37774         var geometry = context.geometry(id),
37775             presets = context.presets().matchGeometry(geometry);
37776
37777         selection.html('');
37778
37779         var messagewrap = selection.append('div')
37780             .attr('class', 'header fillL cf');
37781
37782         var message = messagewrap.append('h3')
37783             .text(t('inspector.choose'));
37784
37785         if (context.entity(id).isUsed(context.graph())) {
37786             messagewrap.append('button')
37787                 .attr('class', 'preset-choose')
37788                 .on('click', function() { event.choose(currentPreset); })
37789                 .append('span')
37790                 .html('&#9658;');
37791         } else {
37792             messagewrap.append('button')
37793                 .attr('class', 'close')
37794                 .on('click', function() {
37795                     context.enter(iD.modes.Browse(context));
37796                 })
37797                 .call(iD.svg.Icon('#icon-close'));
37798         }
37799
37800         function keydown() {
37801             // hack to let delete shortcut work when search is autofocused
37802             if (search.property('value').length === 0 &&
37803                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
37804                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
37805                 d3.event.preventDefault();
37806                 d3.event.stopPropagation();
37807                 iD.operations.Delete([id], context)();
37808             } else if (search.property('value').length === 0 &&
37809                 (d3.event.ctrlKey || d3.event.metaKey) &&
37810                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
37811                 d3.event.preventDefault();
37812                 d3.event.stopPropagation();
37813                 context.undo();
37814             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
37815                 d3.select(this).on('keydown', null);
37816             }
37817         }
37818
37819         function keypress() {
37820             // enter
37821             var value = search.property('value');
37822             if (d3.event.keyCode === 13 && value.length) {
37823                 list.selectAll('.preset-list-item:first-child').datum().choose();
37824             }
37825         }
37826
37827         function inputevent() {
37828             var value = search.property('value');
37829             list.classed('filtered', value.length);
37830             if (value.length) {
37831                 var results = presets.search(value, geometry);
37832                 message.text(t('inspector.results', {
37833                     n: results.collection.length,
37834                     search: value
37835                 }));
37836                 list.call(drawList, results);
37837             } else {
37838                 list.call(drawList, context.presets().defaults(geometry, 36));
37839                 message.text(t('inspector.choose'));
37840             }
37841         }
37842
37843         var searchWrap = selection.append('div')
37844             .attr('class', 'search-header');
37845
37846         var search = searchWrap.append('input')
37847             .attr('class', 'preset-search-input')
37848             .attr('placeholder', t('inspector.search'))
37849             .attr('type', 'search')
37850             .on('keydown', keydown)
37851             .on('keypress', keypress)
37852             .on('input', inputevent);
37853
37854         searchWrap
37855             .call(iD.svg.Icon('#icon-search', 'pre-text'));
37856
37857         if (autofocus) {
37858             search.node().focus();
37859         }
37860
37861         var listWrap = selection.append('div')
37862             .attr('class', 'inspector-body');
37863
37864         var list = listWrap.append('div')
37865             .attr('class', 'preset-list fillL cf')
37866             .call(drawList, context.presets().defaults(geometry, 36));
37867     }
37868
37869     function drawList(list, presets) {
37870         var collection = presets.collection.map(function(preset) {
37871             return preset.members ? CategoryItem(preset) : PresetItem(preset);
37872         });
37873
37874         var items = list.selectAll('.preset-list-item')
37875             .data(collection, function(d) { return d.preset.id; });
37876
37877         items.enter().append('div')
37878             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
37879             .classed('current', function(item) { return item.preset === currentPreset; })
37880             .each(function(item) {
37881                 d3.select(this).call(item);
37882             })
37883             .style('opacity', 0)
37884             .transition()
37885             .style('opacity', 1);
37886
37887         items.order();
37888
37889         items.exit()
37890             .remove();
37891     }
37892
37893     function CategoryItem(preset) {
37894         var box, sublist, shown = false;
37895
37896         function item(selection) {
37897             var wrap = selection.append('div')
37898                 .attr('class', 'preset-list-button-wrap category col12');
37899
37900             wrap.append('button')
37901                 .attr('class', 'preset-list-button')
37902                 .classed('expanded', false)
37903                 .call(iD.ui.PresetIcon()
37904                     .geometry(context.geometry(id))
37905                     .preset(preset))
37906                 .on('click', function() {
37907                     var isExpanded = d3.select(this).classed('expanded');
37908                     var triangle = isExpanded ? '▶ ' :  '▼ ';
37909                     d3.select(this).classed('expanded', !isExpanded);
37910                     d3.select(this).selectAll('.label').text(triangle + preset.name());
37911                     item.choose();
37912                 })
37913                 .append('div')
37914                 .attr('class', 'label')
37915                 .text(function() {
37916                   return '▶ ' + preset.name();
37917                 });
37918
37919             box = selection.append('div')
37920                 .attr('class', 'subgrid col12')
37921                 .style('max-height', '0px')
37922                 .style('opacity', 0);
37923
37924             box.append('div')
37925                 .attr('class', 'arrow');
37926
37927             sublist = box.append('div')
37928                 .attr('class', 'preset-list fillL3 cf fl');
37929         }
37930
37931         item.choose = function() {
37932             if (!box || !sublist) return;
37933
37934             if (shown) {
37935                 shown = false;
37936                 box.transition()
37937                     .duration(200)
37938                     .style('opacity', '0')
37939                     .style('max-height', '0px')
37940                     .style('padding-bottom', '0px');
37941             } else {
37942                 shown = true;
37943                 sublist.call(drawList, preset.members);
37944                 box.transition()
37945                     .duration(200)
37946                     .style('opacity', '1')
37947                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
37948                     .style('padding-bottom', '20px');
37949             }
37950         };
37951
37952         item.preset = preset;
37953
37954         return item;
37955     }
37956
37957     function PresetItem(preset) {
37958         function item(selection) {
37959             var wrap = selection.append('div')
37960                 .attr('class', 'preset-list-button-wrap col12');
37961
37962             wrap.append('button')
37963                 .attr('class', 'preset-list-button')
37964                 .call(iD.ui.PresetIcon()
37965                     .geometry(context.geometry(id))
37966                     .preset(preset))
37967                 .on('click', item.choose)
37968                 .append('div')
37969                 .attr('class', 'label')
37970                 .text(preset.name());
37971
37972             wrap.call(item.reference.button);
37973             selection.call(item.reference.body);
37974         }
37975
37976         item.choose = function() {
37977             context.presets().choose(preset);
37978
37979             context.perform(
37980                 iD.actions.ChangePreset(id, currentPreset, preset),
37981                 t('operations.change_tags.annotation'));
37982
37983             event.choose(preset);
37984         };
37985
37986         item.help = function() {
37987             d3.event.stopPropagation();
37988             item.reference.toggle();
37989         };
37990
37991         item.preset = preset;
37992         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
37993
37994         return item;
37995     }
37996
37997     presetList.autofocus = function(_) {
37998         if (!arguments.length) return autofocus;
37999         autofocus = _;
38000         return presetList;
38001     };
38002
38003     presetList.entityID = function(_) {
38004         if (!arguments.length) return id;
38005         id = _;
38006         presetList.preset(context.presets().match(context.entity(id), context.graph()));
38007         return presetList;
38008     };
38009
38010     presetList.preset = function(_) {
38011         if (!arguments.length) return currentPreset;
38012         currentPreset = _;
38013         return presetList;
38014     };
38015
38016     return d3.rebind(presetList, event, 'on');
38017 };
38018 iD.ui.RadialMenu = function(context, operations) {
38019     var menu,
38020         center = [0, 0],
38021         tooltip;
38022
38023     var radialMenu = function(selection) {
38024         if (!operations.length)
38025             return;
38026
38027         selection.node().parentNode.focus();
38028
38029         function click(operation) {
38030             d3.event.stopPropagation();
38031             if (operation.disabled())
38032                 return;
38033             operation();
38034             radialMenu.close();
38035         }
38036
38037         menu = selection.append('g')
38038             .attr('class', 'radial-menu')
38039             .attr('transform', 'translate(' + center + ')')
38040             .attr('opacity', 0);
38041
38042         menu.transition()
38043             .attr('opacity', 1);
38044
38045         var r = 50,
38046             a = Math.PI / 4,
38047             a0 = -Math.PI / 4,
38048             a1 = a0 + (operations.length - 1) * a;
38049
38050         menu.append('path')
38051             .attr('class', 'radial-menu-background')
38052             .attr('d', 'M' + r * Math.sin(a0) + ',' +
38053                              r * Math.cos(a0) +
38054                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
38055                              (r * Math.sin(a1) + 1e-3) + ',' +
38056                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
38057             .attr('stroke-width', 50)
38058             .attr('stroke-linecap', 'round');
38059
38060         var button = menu.selectAll()
38061             .data(operations)
38062             .enter()
38063             .append('g')
38064             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
38065             .classed('disabled', function(d) { return d.disabled(); })
38066             .attr('transform', function(d, i) {
38067                 return 'translate(' + iD.geo.roundCoords([
38068                         r * Math.sin(a0 + i * a),
38069                         r * Math.cos(a0 + i * a)]).join(',') + ')';
38070             });
38071
38072         button.append('circle')
38073             .attr('r', 15)
38074             .on('click', click)
38075             .on('mousedown', mousedown)
38076             .on('mouseover', mouseover)
38077             .on('mouseout', mouseout);
38078
38079         button.append('use')
38080             .attr('transform', 'translate(-10,-10)')
38081             .attr('width', '20')
38082             .attr('height', '20')
38083             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
38084
38085         tooltip = d3.select(document.body)
38086             .append('div')
38087             .attr('class', 'tooltip-inner radial-menu-tooltip');
38088
38089         function mousedown() {
38090             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
38091         }
38092
38093         function mouseover(d, i) {
38094             var rect = context.surfaceRect(),
38095                 angle = a0 + i * a,
38096                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
38097                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
38098                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
38099                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
38100
38101             tooltip
38102                 .style('top', null)
38103                 .style('left', null)
38104                 .style('bottom', null)
38105                 .style('right', null)
38106                 .style('display', 'block')
38107                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
38108
38109             if (i === 0) {
38110                 tooltip
38111                     .style('right', right)
38112                     .style('top', top);
38113             } else if (i >= 4) {
38114                 tooltip
38115                     .style('left', left)
38116                     .style('bottom', bottom);
38117             } else {
38118                 tooltip
38119                     .style('left', left)
38120                     .style('top', top);
38121             }
38122         }
38123
38124         function mouseout() {
38125             tooltip.style('display', 'none');
38126         }
38127     };
38128
38129     radialMenu.close = function() {
38130         if (menu) {
38131             menu
38132                 .style('pointer-events', 'none')
38133                 .transition()
38134                 .attr('opacity', 0)
38135                 .remove();
38136         }
38137
38138         if (tooltip) {
38139             tooltip.remove();
38140         }
38141     };
38142
38143     radialMenu.center = function(_) {
38144         if (!arguments.length) return center;
38145         center = _;
38146         return radialMenu;
38147     };
38148
38149     return radialMenu;
38150 };
38151 iD.ui.RawMemberEditor = function(context) {
38152     var id;
38153
38154     function selectMember(d) {
38155         d3.event.preventDefault();
38156         context.enter(iD.modes.Select(context, [d.id]));
38157     }
38158
38159     function changeRole(d) {
38160         var role = d3.select(this).property('value');
38161         var member = {id: d.id, type: d.type, role: role};
38162         context.perform(
38163             iD.actions.ChangeMember(d.relation.id, member, d.index),
38164             t('operations.change_role.annotation'));
38165     }
38166
38167     function deleteMember(d) {
38168         context.perform(
38169             iD.actions.DeleteMember(d.relation.id, d.index),
38170             t('operations.delete_member.annotation'));
38171
38172         if (!context.hasEntity(d.relation.id)) {
38173             context.enter(iD.modes.Browse(context));
38174         }
38175     }
38176
38177     function rawMemberEditor(selection) {
38178         var entity = context.entity(id),
38179             memberships = [];
38180
38181         entity.members.forEach(function(member, index) {
38182             memberships.push({
38183                 index: index,
38184                 id: member.id,
38185                 type: member.type,
38186                 role: member.role,
38187                 relation: entity,
38188                 member: context.hasEntity(member.id)
38189             });
38190         });
38191
38192         selection.call(iD.ui.Disclosure()
38193             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
38194             .expanded(true)
38195             .on('toggled', toggled)
38196             .content(content));
38197
38198         function toggled(expanded) {
38199             if (expanded) {
38200                 selection.node().parentNode.scrollTop += 200;
38201             }
38202         }
38203
38204         function content($wrap) {
38205             var $list = $wrap.selectAll('.member-list')
38206                 .data([0]);
38207
38208             $list.enter().append('ul')
38209                 .attr('class', 'member-list');
38210
38211             var $items = $list.selectAll('li')
38212                 .data(memberships, function(d) {
38213                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
38214                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
38215                 });
38216
38217             var $enter = $items.enter().append('li')
38218                 .attr('class', 'member-row form-field')
38219                 .classed('member-incomplete', function(d) { return !d.member; });
38220
38221             $enter.each(function(d) {
38222                 if (d.member) {
38223                     var $label = d3.select(this).append('label')
38224                         .attr('class', 'form-label')
38225                         .append('a')
38226                         .attr('href', '#')
38227                         .on('click', selectMember);
38228
38229                     $label.append('span')
38230                         .attr('class', 'member-entity-type')
38231                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
38232
38233                     $label.append('span')
38234                         .attr('class', 'member-entity-name')
38235                         .text(function(d) { return iD.util.displayName(d.member); });
38236
38237                 } else {
38238                     d3.select(this).append('label')
38239                         .attr('class', 'form-label')
38240                         .text(t('inspector.incomplete'));
38241                 }
38242             });
38243
38244             $enter.append('input')
38245                 .attr('class', 'member-role')
38246                 .property('type', 'text')
38247                 .attr('maxlength', 255)
38248                 .attr('placeholder', t('inspector.role'))
38249                 .property('value', function(d) { return d.role; })
38250                 .on('change', changeRole);
38251
38252             $enter.append('button')
38253                 .attr('tabindex', -1)
38254                 .attr('class', 'remove button-input-action member-delete minor')
38255                 .on('click', deleteMember)
38256                 .call(iD.svg.Icon('#operation-delete'));
38257
38258             $items.exit()
38259                 .remove();
38260         }
38261     }
38262
38263     rawMemberEditor.entityID = function(_) {
38264         if (!arguments.length) return id;
38265         id = _;
38266         return rawMemberEditor;
38267     };
38268
38269     return rawMemberEditor;
38270 };
38271 iD.ui.RawMembershipEditor = function(context) {
38272     var id, showBlank;
38273
38274     function selectRelation(d) {
38275         d3.event.preventDefault();
38276         context.enter(iD.modes.Select(context, [d.relation.id]));
38277     }
38278
38279     function changeRole(d) {
38280         var role = d3.select(this).property('value');
38281         context.perform(
38282             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
38283             t('operations.change_role.annotation'));
38284     }
38285
38286     function addMembership(d, role) {
38287         showBlank = false;
38288
38289         if (d.relation) {
38290             context.perform(
38291                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
38292                 t('operations.add_member.annotation'));
38293
38294         } else {
38295             var relation = iD.Relation();
38296
38297             context.perform(
38298                 iD.actions.AddEntity(relation),
38299                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
38300                 t('operations.add.annotation.relation'));
38301
38302             context.enter(iD.modes.Select(context, [relation.id]));
38303         }
38304     }
38305
38306     function deleteMembership(d) {
38307         context.perform(
38308             iD.actions.DeleteMember(d.relation.id, d.index),
38309             t('operations.delete_member.annotation'));
38310     }
38311
38312     function relations(q) {
38313         var newRelation = {
38314                 relation: null,
38315                 value: t('inspector.new_relation')
38316             },
38317             result = [],
38318             graph = context.graph();
38319
38320         context.intersects(context.extent()).forEach(function(entity) {
38321             if (entity.type !== 'relation' || entity.id === id)
38322                 return;
38323
38324             var presetName = context.presets().match(entity, graph).name(),
38325                 entityName = iD.util.displayName(entity) || '';
38326
38327             var value = presetName + ' ' + entityName;
38328             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
38329                 return;
38330
38331             result.push({
38332                 relation: entity,
38333                 value: value
38334             });
38335         });
38336
38337         result.sort(function(a, b) {
38338             return iD.Relation.creationOrder(a.relation, b.relation);
38339         });
38340
38341         // Dedupe identical names by appending relation id - see #2891
38342         var dupeGroups = _(result)
38343             .groupBy('value')
38344             .filter(function(v) { return v.length > 1; })
38345             .value();
38346
38347         dupeGroups.forEach(function(group) {
38348             group.forEach(function(obj) {
38349                 obj.value += ' ' + obj.relation.id;
38350             });
38351         });
38352
38353         result.unshift(newRelation);
38354
38355         return result;
38356     }
38357
38358     function rawMembershipEditor(selection) {
38359         var entity = context.entity(id),
38360             memberships = [];
38361
38362         context.graph().parentRelations(entity).forEach(function(relation) {
38363             relation.members.forEach(function(member, index) {
38364                 if (member.id === entity.id) {
38365                     memberships.push({relation: relation, member: member, index: index});
38366                 }
38367             });
38368         });
38369
38370         selection.call(iD.ui.Disclosure()
38371             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
38372             .expanded(true)
38373             .on('toggled', toggled)
38374             .content(content));
38375
38376         function toggled(expanded) {
38377             if (expanded) {
38378                 selection.node().parentNode.scrollTop += 200;
38379             }
38380         }
38381
38382         function content($wrap) {
38383             var $list = $wrap.selectAll('.member-list')
38384                 .data([0]);
38385
38386             $list.enter().append('ul')
38387                 .attr('class', 'member-list');
38388
38389             var $items = $list.selectAll('li.member-row-normal')
38390                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
38391
38392             var $enter = $items.enter().append('li')
38393                 .attr('class', 'member-row member-row-normal form-field');
38394
38395             var $label = $enter.append('label')
38396                 .attr('class', 'form-label')
38397                 .append('a')
38398                 .attr('href', '#')
38399                 .on('click', selectRelation);
38400
38401             $label.append('span')
38402                 .attr('class', 'member-entity-type')
38403                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
38404
38405             $label.append('span')
38406                 .attr('class', 'member-entity-name')
38407                 .text(function(d) { return iD.util.displayName(d.relation); });
38408
38409             $enter.append('input')
38410                 .attr('class', 'member-role')
38411                 .property('type', 'text')
38412                 .attr('maxlength', 255)
38413                 .attr('placeholder', t('inspector.role'))
38414                 .property('value', function(d) { return d.member.role; })
38415                 .on('change', changeRole);
38416
38417             $enter.append('button')
38418                 .attr('tabindex', -1)
38419                 .attr('class', 'remove button-input-action member-delete minor')
38420                 .on('click', deleteMembership)
38421                 .call(iD.svg.Icon('#operation-delete'));
38422
38423             $items.exit()
38424                 .remove();
38425
38426             if (showBlank) {
38427                 var $new = $list.selectAll('.member-row-new')
38428                     .data([0]);
38429
38430                 $enter = $new.enter().append('li')
38431                     .attr('class', 'member-row member-row-new form-field');
38432
38433                 $enter.append('input')
38434                     .attr('type', 'text')
38435                     .attr('class', 'member-entity-input')
38436                     .call(d3.combobox()
38437                         .minItems(1)
38438                         .fetcher(function(value, callback) {
38439                             callback(relations(value));
38440                         })
38441                         .on('accept', function(d) {
38442                             addMembership(d, $new.select('.member-role').property('value'));
38443                         }));
38444
38445                 $enter.append('input')
38446                     .attr('class', 'member-role')
38447                     .property('type', 'text')
38448                     .attr('maxlength', 255)
38449                     .attr('placeholder', t('inspector.role'))
38450                     .on('change', changeRole);
38451
38452                 $enter.append('button')
38453                     .attr('tabindex', -1)
38454                     .attr('class', 'remove button-input-action member-delete minor')
38455                     .on('click', deleteMembership)
38456                     .call(iD.svg.Icon('#operation-delete'));
38457
38458             } else {
38459                 $list.selectAll('.member-row-new')
38460                     .remove();
38461             }
38462
38463             var $add = $wrap.selectAll('.add-relation')
38464                 .data([0]);
38465
38466             $add.enter()
38467                 .append('button')
38468                 .attr('class', 'add-relation')
38469                 .call(iD.svg.Icon('#icon-plus', 'light'));
38470
38471             $wrap.selectAll('.add-relation')
38472                 .on('click', function() {
38473                     showBlank = true;
38474                     content($wrap);
38475                     $list.selectAll('.member-entity-input').node().focus();
38476                 });
38477         }
38478     }
38479
38480     rawMembershipEditor.entityID = function(_) {
38481         if (!arguments.length) return id;
38482         id = _;
38483         return rawMembershipEditor;
38484     };
38485
38486     return rawMembershipEditor;
38487 };
38488 iD.ui.RawTagEditor = function(context) {
38489     var event = d3.dispatch('change'),
38490         showBlank = false,
38491         state,
38492         preset,
38493         tags,
38494         id;
38495
38496     function rawTagEditor(selection) {
38497         var count = Object.keys(tags).filter(function(d) { return d; }).length;
38498
38499         selection.call(iD.ui.Disclosure()
38500             .title(t('inspector.all_tags') + ' (' + count + ')')
38501             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
38502             .on('toggled', toggled)
38503             .content(content));
38504
38505         function toggled(expanded) {
38506             context.storage('raw_tag_editor.expanded', expanded);
38507             if (expanded) {
38508                 selection.node().parentNode.scrollTop += 200;
38509             }
38510         }
38511     }
38512
38513     function content($wrap) {
38514         var entries = d3.entries(tags);
38515
38516         if (!entries.length || showBlank) {
38517             showBlank = false;
38518             entries.push({key: '', value: ''});
38519         }
38520
38521         var $list = $wrap.selectAll('.tag-list')
38522             .data([0]);
38523
38524         $list.enter().append('ul')
38525             .attr('class', 'tag-list');
38526
38527         var $newTag = $wrap.selectAll('.add-tag')
38528             .data([0]);
38529
38530         $newTag.enter()
38531             .append('button')
38532             .attr('class', 'add-tag')
38533             .call(iD.svg.Icon('#icon-plus', 'light'));
38534
38535         $newTag.on('click', addTag);
38536
38537         var $items = $list.selectAll('li')
38538             .data(entries, function(d) { return d.key; });
38539
38540         // Enter
38541
38542         var $enter = $items.enter().append('li')
38543             .attr('class', 'tag-row cf');
38544
38545         $enter.append('div')
38546             .attr('class', 'key-wrap')
38547             .append('input')
38548             .property('type', 'text')
38549             .attr('class', 'key')
38550             .attr('maxlength', 255);
38551
38552         $enter.append('div')
38553             .attr('class', 'input-wrap-position')
38554             .append('input')
38555             .property('type', 'text')
38556             .attr('class', 'value')
38557             .attr('maxlength', 255);
38558
38559         $enter.append('button')
38560             .attr('tabindex', -1)
38561             .attr('class', 'remove minor')
38562             .call(iD.svg.Icon('#operation-delete'));
38563
38564         if (context.taginfo()) {
38565             $enter.each(bindTypeahead);
38566         }
38567
38568         // Update
38569
38570         $items.order();
38571
38572         $items.each(function(tag) {
38573             var isRelation = (context.entity(id).type === 'relation'),
38574                 reference;
38575             if (isRelation && tag.key === 'type')
38576                 reference = iD.ui.TagReference({rtype: tag.value}, context);
38577             else
38578                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
38579
38580             if (state === 'hover') {
38581                 reference.showing(false);
38582             }
38583
38584             d3.select(this)
38585                 .call(reference.button)
38586                 .call(reference.body);
38587         });
38588
38589         $items.select('input.key')
38590             .attr('title', function(d) { return d.key; })
38591             .value(function(d) { return d.key; })
38592             .on('blur', keyChange)
38593             .on('change', keyChange);
38594
38595         $items.select('input.value')
38596             .attr('title', function(d) { return d.value; })
38597             .value(function(d) { return d.value; })
38598             .on('blur', valueChange)
38599             .on('change', valueChange)
38600             .on('keydown.push-more', pushMore);
38601
38602         $items.select('button.remove')
38603             .on('click', removeTag);
38604
38605         $items.exit()
38606             .each(unbind)
38607             .remove();
38608
38609         function pushMore() {
38610             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
38611                 $list.selectAll('li:last-child input.value').node() === this) {
38612                 addTag();
38613             }
38614         }
38615
38616         function bindTypeahead() {
38617             var row = d3.select(this),
38618                 key = row.selectAll('input.key'),
38619                 value = row.selectAll('input.value');
38620
38621             function sort(value, data) {
38622                 var sameletter = [],
38623                     other = [];
38624                 for (var i = 0; i < data.length; i++) {
38625                     if (data[i].value.substring(0, value.length) === value) {
38626                         sameletter.push(data[i]);
38627                     } else {
38628                         other.push(data[i]);
38629                     }
38630                 }
38631                 return sameletter.concat(other);
38632             }
38633
38634             key.call(d3.combobox()
38635                 .fetcher(function(value, callback) {
38636                     context.taginfo().keys({
38637                         debounce: true,
38638                         geometry: context.geometry(id),
38639                         query: value
38640                     }, function(err, data) {
38641                         if (!err) callback(sort(value, data));
38642                     });
38643                 }));
38644
38645             value.call(d3.combobox()
38646                 .fetcher(function(value, callback) {
38647                     context.taginfo().values({
38648                         debounce: true,
38649                         key: key.value(),
38650                         geometry: context.geometry(id),
38651                         query: value
38652                     }, function(err, data) {
38653                         if (!err) callback(sort(value, data));
38654                     });
38655                 }));
38656         }
38657
38658         function unbind() {
38659             var row = d3.select(this);
38660
38661             row.selectAll('input.key')
38662                 .call(d3.combobox.off);
38663
38664             row.selectAll('input.value')
38665                 .call(d3.combobox.off);
38666         }
38667
38668         function keyChange(d) {
38669             var kOld = d.key,
38670                 kNew = this.value.trim(),
38671                 tag = {};
38672
38673             if (kNew && kNew !== kOld) {
38674                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
38675                     base = match[1],
38676                     suffix = +(match[2] || 1);
38677                 while (tags[kNew]) {  // rename key if already in use
38678                     kNew = base + '_' + suffix++;
38679                 }
38680             }
38681             tag[kOld] = undefined;
38682             tag[kNew] = d.value;
38683             d.key = kNew; // Maintain DOM identity through the subsequent update.
38684             this.value = kNew;
38685             event.change(tag);
38686         }
38687
38688         function valueChange(d) {
38689             var tag = {};
38690             tag[d.key] = this.value;
38691             event.change(tag);
38692         }
38693
38694         function removeTag(d) {
38695             var tag = {};
38696             tag[d.key] = undefined;
38697             event.change(tag);
38698             d3.select(this.parentNode).remove();
38699         }
38700
38701         function addTag() {
38702             // Wrapped in a setTimeout in case it's being called from a blur
38703             // handler. Without the setTimeout, the call to `content` would
38704             // wipe out the pending value change.
38705             setTimeout(function() {
38706                 showBlank = true;
38707                 content($wrap);
38708                 $list.selectAll('li:last-child input.key').node().focus();
38709             }, 0);
38710         }
38711     }
38712
38713     rawTagEditor.state = function(_) {
38714         if (!arguments.length) return state;
38715         state = _;
38716         return rawTagEditor;
38717     };
38718
38719     rawTagEditor.preset = function(_) {
38720         if (!arguments.length) return preset;
38721         preset = _;
38722         return rawTagEditor;
38723     };
38724
38725     rawTagEditor.tags = function(_) {
38726         if (!arguments.length) return tags;
38727         tags = _;
38728         return rawTagEditor;
38729     };
38730
38731     rawTagEditor.entityID = function(_) {
38732         if (!arguments.length) return id;
38733         id = _;
38734         return rawTagEditor;
38735     };
38736
38737     return d3.rebind(rawTagEditor, event, 'on');
38738 };
38739 iD.ui.Restore = function(context) {
38740     return function(selection) {
38741         if (!context.history().lock() || !context.history().restorableChanges())
38742             return;
38743
38744         var modal = iD.ui.modal(selection, true);
38745
38746         modal.select('.modal')
38747             .attr('class', 'modal fillL col6');
38748
38749         var introModal = modal.select('.content');
38750
38751         introModal.attr('class','cf');
38752
38753         introModal.append('div')
38754             .attr('class', 'modal-section')
38755             .append('h3')
38756             .text(t('restore.heading'));
38757
38758         introModal.append('div')
38759             .attr('class','modal-section')
38760             .append('p')
38761             .text(t('restore.description'));
38762
38763         var buttonWrap = introModal.append('div')
38764             .attr('class', 'modal-actions cf');
38765
38766         var restore = buttonWrap.append('button')
38767             .attr('class', 'restore col6')
38768             .text(t('restore.restore'))
38769             .on('click', function() {
38770                 context.history().restore();
38771                 modal.remove();
38772             });
38773
38774         buttonWrap.append('button')
38775             .attr('class', 'reset col6')
38776             .text(t('restore.reset'))
38777             .on('click', function() {
38778                 context.history().clearSaved();
38779                 modal.remove();
38780             });
38781
38782         restore.node().focus();
38783     };
38784 };
38785 iD.ui.Save = function(context) {
38786     var history = context.history(),
38787         key = iD.ui.cmd('⌘S');
38788
38789
38790     function saving() {
38791         return context.mode().id === 'save';
38792     }
38793
38794     function save() {
38795         d3.event.preventDefault();
38796         if (!context.inIntro() && !saving() && history.hasChanges()) {
38797             context.enter(iD.modes.Save(context));
38798         }
38799     }
38800
38801     function getBackground(numChanges) {
38802         var step;
38803         if (numChanges === 0) {
38804             return null;
38805         } else if (numChanges <= 50) {
38806             step = numChanges / 50;
38807             return d3.interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
38808         } else {
38809             step = Math.min((numChanges - 50) / 50, 1.0);
38810             return d3.interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
38811         }
38812     }
38813
38814     return function(selection) {
38815         var tooltip = bootstrap.tooltip()
38816             .placement('bottom')
38817             .html(true)
38818             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
38819
38820         var button = selection.append('button')
38821             .attr('class', 'save col12 disabled')
38822             .attr('tabindex', -1)
38823             .on('click', save)
38824             .call(tooltip);
38825
38826         button.append('span')
38827             .attr('class', 'label')
38828             .text(t('save.title'));
38829
38830         button.append('span')
38831             .attr('class', 'count')
38832             .text('0');
38833
38834         var keybinding = d3.keybinding('undo-redo')
38835             .on(key, save, true);
38836
38837         d3.select(document)
38838             .call(keybinding);
38839
38840         var numChanges = 0;
38841
38842         context.history().on('change.save', function() {
38843             var _ = history.difference().summary().length;
38844             if (_ === numChanges)
38845                 return;
38846             numChanges = _;
38847
38848             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
38849                     'save.help' : 'save.no_changes'), key));
38850
38851             var background = getBackground(numChanges);
38852
38853             button
38854                 .classed('disabled', numChanges === 0)
38855                 .classed('has-count', numChanges > 0)
38856                 .style('background', background);
38857
38858             button.select('span.count')
38859                 .text(numChanges)
38860                 .style('background', background)
38861                 .style('border-color', background);
38862         });
38863
38864         context.on('enter.save', function() {
38865             button.property('disabled', saving());
38866             if (saving()) button.call(tooltip.hide);
38867         });
38868     };
38869 };
38870 iD.ui.Scale = function(context) {
38871     var projection = context.projection,
38872         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
38873         maxLength = 180,
38874         tickHeight = 8;
38875
38876     function scaleDefs(loc1, loc2) {
38877         var lat = (loc2[1] + loc1[1]) / 2,
38878             conversion = (imperial ? 3.28084 : 1),
38879             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
38880             scale = { dist: 0, px: 0, text: '' },
38881             buckets, i, val, dLon;
38882
38883         if (imperial) {
38884             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
38885         } else {
38886             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
38887         }
38888
38889         // determine a user-friendly endpoint for the scale
38890         for (i = 0; i < buckets.length; i++) {
38891             val = buckets[i];
38892             if (dist >= val) {
38893                 scale.dist = Math.floor(dist / val) * val;
38894                 break;
38895             }
38896         }
38897
38898         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
38899         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
38900
38901         if (imperial) {
38902             if (scale.dist >= 5280) {
38903                 scale.dist /= 5280;
38904                 scale.text = String(scale.dist) + ' mi';
38905             } else {
38906                 scale.text = String(scale.dist) + ' ft';
38907             }
38908         } else {
38909             if (scale.dist >= 1000) {
38910                 scale.dist /= 1000;
38911                 scale.text = String(scale.dist) + ' km';
38912             } else {
38913                 scale.text = String(scale.dist) + ' m';
38914             }
38915         }
38916
38917         return scale;
38918     }
38919
38920     function update(selection) {
38921         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
38922         var dims = context.map().dimensions(),
38923             loc1 = projection.invert([0, dims[1]]),
38924             loc2 = projection.invert([maxLength, dims[1]]),
38925             scale = scaleDefs(loc1, loc2);
38926
38927         selection.select('#scalepath')
38928             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
38929
38930         selection.select('#scaletext')
38931             .attr('x', scale.px + 8)
38932             .attr('y', tickHeight)
38933             .text(scale.text);
38934     }
38935
38936
38937     return function(selection) {
38938         function switchUnits() {
38939             imperial = !imperial;
38940             selection.call(update);
38941         }
38942
38943         var g = selection.append('svg')
38944             .attr('id', 'scale')
38945             .on('click', switchUnits)
38946             .append('g')
38947             .attr('transform', 'translate(10,11)');
38948
38949         g.append('path').attr('id', 'scalepath');
38950         g.append('text').attr('id', 'scaletext');
38951
38952         selection.call(update);
38953
38954         context.map().on('move.scale', function() {
38955             update(selection);
38956         });
38957     };
38958 };
38959 iD.ui.SelectionList = function(context, selectedIDs) {
38960
38961     function selectEntity(entity) {
38962         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
38963     }
38964
38965
38966     function selectionList(selection) {
38967         selection.classed('selection-list-pane', true);
38968
38969         var header = selection.append('div')
38970             .attr('class', 'header fillL cf');
38971
38972         header.append('h3')
38973             .text(t('inspector.multiselect'));
38974
38975         var listWrap = selection.append('div')
38976             .attr('class', 'inspector-body');
38977
38978         var list = listWrap.append('div')
38979             .attr('class', 'feature-list cf');
38980
38981         context.history().on('change.selection-list', drawList);
38982         drawList();
38983
38984         function drawList() {
38985             var entities = selectedIDs
38986                 .map(function(id) { return context.hasEntity(id); })
38987                 .filter(function(entity) { return entity; });
38988
38989             var items = list.selectAll('.feature-list-item')
38990                 .data(entities, iD.Entity.key);
38991
38992             var enter = items.enter().append('button')
38993                 .attr('class', 'feature-list-item')
38994                 .on('click', selectEntity);
38995
38996             // Enter
38997             var label = enter.append('div')
38998                 .attr('class', 'label')
38999                 .call(iD.svg.Icon('', 'pre-text'));
39000
39001             label.append('span')
39002                 .attr('class', 'entity-type');
39003
39004             label.append('span')
39005                 .attr('class', 'entity-name');
39006
39007             // Update
39008             items.selectAll('use')
39009                 .attr('href', function() {
39010                     var entity = this.parentNode.parentNode.__data__;
39011                     return '#icon-' + context.geometry(entity.id);
39012                 });
39013
39014             items.selectAll('.entity-type')
39015                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
39016
39017             items.selectAll('.entity-name')
39018                 .text(function(entity) { return iD.util.displayName(entity); });
39019
39020             // Exit
39021             items.exit()
39022                 .remove();
39023         }
39024     }
39025
39026     return selectionList;
39027
39028 };
39029 iD.ui.Sidebar = function(context) {
39030     var inspector = iD.ui.Inspector(context),
39031         current;
39032
39033     function sidebar(selection) {
39034         var featureListWrap = selection.append('div')
39035             .attr('class', 'feature-list-pane')
39036             .call(iD.ui.FeatureList(context));
39037
39038         selection.call(iD.ui.Notice(context));
39039
39040         var inspectorWrap = selection.append('div')
39041             .attr('class', 'inspector-hidden inspector-wrap fr');
39042
39043         function hover(id) {
39044             if (!current && context.hasEntity(id)) {
39045                 featureListWrap.classed('inspector-hidden', true);
39046                 inspectorWrap.classed('inspector-hidden', false)
39047                     .classed('inspector-hover', true);
39048
39049                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
39050                     inspector
39051                         .state('hover')
39052                         .entityID(id);
39053
39054                     inspectorWrap.call(inspector);
39055                 }
39056             } else if (!current) {
39057                 featureListWrap.classed('inspector-hidden', false);
39058                 inspectorWrap.classed('inspector-hidden', true);
39059                 inspector.state('hide');
39060             }
39061         }
39062
39063         sidebar.hover = _.throttle(hover, 200);
39064
39065         sidebar.select = function(id, newFeature) {
39066             if (!current && id) {
39067                 featureListWrap.classed('inspector-hidden', true);
39068                 inspectorWrap.classed('inspector-hidden', false)
39069                     .classed('inspector-hover', false);
39070
39071                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
39072                     inspector
39073                         .state('select')
39074                         .entityID(id)
39075                         .newFeature(newFeature);
39076
39077                     inspectorWrap.call(inspector);
39078                 }
39079             } else if (!current) {
39080                 featureListWrap.classed('inspector-hidden', false);
39081                 inspectorWrap.classed('inspector-hidden', true);
39082                 inspector.state('hide');
39083             }
39084         };
39085
39086         sidebar.show = function(component) {
39087             featureListWrap.classed('inspector-hidden', true);
39088             inspectorWrap.classed('inspector-hidden', true);
39089             if (current) current.remove();
39090             current = selection.append('div')
39091                 .attr('class', 'sidebar-component')
39092                 .call(component);
39093         };
39094
39095         sidebar.hide = function() {
39096             featureListWrap.classed('inspector-hidden', false);
39097             inspectorWrap.classed('inspector-hidden', true);
39098             if (current) current.remove();
39099             current = null;
39100         };
39101     }
39102
39103     sidebar.hover = function() {};
39104     sidebar.hover.cancel = function() {};
39105     sidebar.select = function() {};
39106     sidebar.show = function() {};
39107     sidebar.hide = function() {};
39108
39109     return sidebar;
39110 };
39111 iD.ui.SourceSwitch = function(context) {
39112     var keys;
39113
39114     function click() {
39115         d3.event.preventDefault();
39116
39117         if (context.history().hasChanges() &&
39118             !window.confirm(t('source_switch.lose_changes'))) return;
39119
39120         var live = d3.select(this)
39121             .classed('live');
39122
39123         context.connection()
39124             .switch(live ? keys[1] : keys[0]);
39125
39126         context.enter(iD.modes.Browse(context));
39127         context.flush();
39128
39129         d3.select(this)
39130             .text(live ? t('source_switch.dev') : t('source_switch.live'))
39131             .classed('live', !live);
39132     }
39133
39134     var sourceSwitch = function(selection) {
39135         selection.append('a')
39136             .attr('href', '#')
39137             .text(t('source_switch.live'))
39138             .classed('live', true)
39139             .attr('tabindex', -1)
39140             .on('click', click);
39141     };
39142
39143     sourceSwitch.keys = function(_) {
39144         if (!arguments.length) return keys;
39145         keys = _;
39146         return sourceSwitch;
39147     };
39148
39149     return sourceSwitch;
39150 };
39151 iD.ui.Spinner = function(context) {
39152     var connection = context.connection();
39153
39154     return function(selection) {
39155         var img = selection.append('img')
39156             .attr('src', context.imagePath('loader-black.gif'))
39157             .style('opacity', 0);
39158
39159         connection.on('loading.spinner', function() {
39160             img.transition()
39161                 .style('opacity', 1);
39162         });
39163
39164         connection.on('loaded.spinner', function() {
39165             img.transition()
39166                 .style('opacity', 0);
39167         });
39168     };
39169 };
39170 iD.ui.Splash = function(context) {
39171     return function(selection) {
39172         if (context.storage('sawSplash'))
39173              return;
39174
39175         context.storage('sawSplash', true);
39176
39177         var modal = iD.ui.modal(selection);
39178
39179         modal.select('.modal')
39180             .attr('class', 'modal-splash modal col6');
39181
39182         var introModal = modal.select('.content')
39183             .append('div')
39184             .attr('class', 'fillL');
39185
39186         introModal.append('div')
39187             .attr('class','modal-section cf')
39188             .append('h3').text(t('splash.welcome'));
39189
39190         introModal.append('div')
39191             .attr('class','modal-section')
39192             .append('p')
39193             .html(t('splash.text', {
39194                 version: iD.version,
39195                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
39196                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
39197             }));
39198
39199         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
39200
39201         buttons.append('button')
39202             .attr('class', 'col6 walkthrough')
39203             .text(t('splash.walkthrough'))
39204             .on('click', function() {
39205                 d3.select(document.body).call(iD.ui.intro(context));
39206                 modal.close();
39207             });
39208
39209         buttons.append('button')
39210             .attr('class', 'col6 start')
39211             .text(t('splash.start'))
39212             .on('click', modal.close);
39213
39214         modal.select('button.close').attr('class','hide');
39215
39216     };
39217 };
39218 iD.ui.Status = function(context) {
39219     var connection = context.connection(),
39220         errCount = 0;
39221
39222     return function(selection) {
39223
39224         function update() {
39225
39226             connection.status(function(err, apiStatus) {
39227
39228                 selection.html('');
39229
39230                 if (err && errCount++ < 2) return;
39231
39232                 if (err) {
39233                     selection.text(t('status.error'));
39234
39235                 } else if (apiStatus === 'readonly') {
39236                     selection.text(t('status.readonly'));
39237
39238                 } else if (apiStatus === 'offline') {
39239                     selection.text(t('status.offline'));
39240                 }
39241
39242                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
39243                 if (!err) errCount = 0;
39244
39245             });
39246         }
39247
39248         connection.on('auth', function() { update(selection); });
39249         window.setInterval(update, 90000);
39250         update(selection);
39251     };
39252 };
39253 iD.ui.Success = function(context) {
39254     var dispatch = d3.dispatch('cancel'),
39255         changeset;
39256
39257     function success(selection) {
39258         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
39259             ' ' + context.connection().changesetURL(changeset.id);
39260
39261         var header = selection.append('div')
39262             .attr('class', 'header fillL');
39263
39264         header.append('button')
39265             .attr('class', 'fr')
39266             .on('click', function() { dispatch.cancel(); })
39267             .call(iD.svg.Icon('#icon-close'));
39268
39269         header.append('h3')
39270             .text(t('success.just_edited'));
39271
39272         var body = selection.append('div')
39273             .attr('class', 'body save-success fillL');
39274
39275         body.append('p')
39276             .html(t('success.help_html'));
39277
39278         body.append('a')
39279             .attr('class', 'details')
39280             .attr('target', '_blank')
39281             .attr('tabindex', -1)
39282             .call(iD.svg.Icon('#icon-out-link', 'inline'))
39283             .attr('href', t('success.help_link_url'))
39284             .append('span')
39285             .text(t('success.help_link_text'));
39286
39287         var changesetURL = context.connection().changesetURL(changeset.id);
39288
39289         body.append('a')
39290             .attr('class', 'button col12 osm')
39291             .attr('target', '_blank')
39292             .attr('href', changesetURL)
39293             .text(t('success.view_on_osm'));
39294
39295         var sharing = {
39296             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
39297             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
39298             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
39299         };
39300
39301         body.selectAll('.button.social')
39302             .data(d3.entries(sharing))
39303             .enter()
39304             .append('a')
39305             .attr('class', 'button social col4')
39306             .attr('target', '_blank')
39307             .attr('href', function(d) { return d.value; })
39308             .call(bootstrap.tooltip()
39309                 .title(function(d) { return t('success.' + d.key); })
39310                 .placement('bottom'))
39311             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
39312     }
39313
39314     success.changeset = function(_) {
39315         if (!arguments.length) return changeset;
39316         changeset = _;
39317         return success;
39318     };
39319
39320     return d3.rebind(success, dispatch, 'on');
39321 };
39322 iD.ui.TagReference = function(tag, context) {
39323     var tagReference = {},
39324         button,
39325         body,
39326         loaded,
39327         showing;
39328
39329     function findLocal(data) {
39330         var locale = iD.detect().locale.toLowerCase(),
39331             localized;
39332
39333         localized = _.find(data, function(d) {
39334             return d.lang.toLowerCase() === locale;
39335         });
39336         if (localized) return localized;
39337
39338         // try the non-regional version of a language, like
39339         // 'en' if the language is 'en-US'
39340         if (locale.indexOf('-') !== -1) {
39341             var first = locale.split('-')[0];
39342             localized = _.find(data, function(d) {
39343                 return d.lang.toLowerCase() === first;
39344             });
39345             if (localized) return localized;
39346         }
39347
39348         // finally fall back to english
39349         return _.find(data, function(d) {
39350             return d.lang.toLowerCase() === 'en';
39351         });
39352     }
39353
39354     function load(param) {
39355         button.classed('tag-reference-loading', true);
39356
39357         context.taginfo().docs(param, function show(err, data) {
39358             var docs;
39359             if (!err && data) {
39360                 docs = findLocal(data);
39361             }
39362
39363             body.html('');
39364
39365             if (!docs || !docs.description) {
39366                 if (param.hasOwnProperty('value')) {
39367                     load(_.omit(param, 'value'));   // retry with key only
39368                 } else {
39369                     body.append('p').text(t('inspector.no_documentation_key'));
39370                     done();
39371                 }
39372                 return;
39373             }
39374
39375             if (docs.image && docs.image.thumb_url_prefix) {
39376                 body
39377                     .append('img')
39378                     .attr('class', 'wiki-image')
39379                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
39380                     .on('load', function() { done(); })
39381                     .on('error', function() { d3.select(this).remove(); done(); });
39382             } else {
39383                 done();
39384             }
39385
39386             body
39387                 .append('p')
39388                 .text(docs.description);
39389
39390             body
39391                 .append('a')
39392                 .attr('target', '_blank')
39393                 .attr('tabindex', -1)
39394                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
39395                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
39396                 .append('span')
39397                 .text(t('inspector.reference'));
39398         });
39399     }
39400
39401     function done() {
39402         loaded = true;
39403
39404         button.classed('tag-reference-loading', false);
39405
39406         body.transition()
39407             .duration(200)
39408             .style('max-height', '200px')
39409             .style('opacity', '1');
39410
39411         showing = true;
39412     }
39413
39414     function hide(selection) {
39415         selection = selection || body.transition().duration(200);
39416
39417         selection
39418             .style('max-height', '0px')
39419             .style('opacity', '0');
39420
39421         showing = false;
39422     }
39423
39424     tagReference.button = function(selection) {
39425         button = selection.selectAll('.tag-reference-button')
39426             .data([0]);
39427
39428         button.enter()
39429             .append('button')
39430             .attr('class', 'tag-reference-button')
39431             .attr('tabindex', -1)
39432             .call(iD.svg.Icon('#icon-inspect'));
39433
39434         button.on('click', function () {
39435             d3.event.stopPropagation();
39436             d3.event.preventDefault();
39437             if (showing) {
39438                 hide();
39439             } else if (loaded) {
39440                 done();
39441             } else {
39442                 if (context.taginfo()) {
39443                     load(tag);
39444                 }
39445             }
39446         });
39447     };
39448
39449     tagReference.body = function(selection) {
39450         body = selection.selectAll('.tag-reference-body')
39451             .data([0]);
39452
39453         body.enter().append('div')
39454             .attr('class', 'tag-reference-body cf')
39455             .style('max-height', '0')
39456             .style('opacity', '0');
39457
39458         if (showing === false) {
39459             hide(body);
39460         }
39461     };
39462
39463     tagReference.showing = function(_) {
39464         if (!arguments.length) return showing;
39465         showing = _;
39466         return tagReference;
39467     };
39468
39469     return tagReference;
39470 };
39471 // toggles the visibility of ui elements, using a combination of the
39472 // hide class, which sets display=none, and a d3 transition for opacity.
39473 // this will cause blinking when called repeatedly, so check that the
39474 // value actually changes between calls.
39475 iD.ui.Toggle = function(show, callback) {
39476     return function(selection) {
39477         selection
39478             .style('opacity', show ? 0 : 1)
39479             .classed('hide', false)
39480             .transition()
39481             .style('opacity', show ? 1 : 0)
39482             .each('end', function() {
39483                 d3.select(this)
39484                     .classed('hide', !show)
39485                     .style('opacity', null);
39486                 if (callback) callback.apply(this);
39487             });
39488     };
39489 };
39490 iD.ui.UndoRedo = function(context) {
39491     var commands = [{
39492         id: 'undo',
39493         cmd: iD.ui.cmd('⌘Z'),
39494         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
39495         annotation: function() { return context.history().undoAnnotation(); }
39496     }, {
39497         id: 'redo',
39498         cmd: iD.ui.cmd('⌘⇧Z'),
39499         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
39500         annotation: function() { return context.history().redoAnnotation(); }
39501     }];
39502
39503     function saving() {
39504         return context.mode().id === 'save';
39505     }
39506
39507     return function(selection) {
39508         var tooltip = bootstrap.tooltip()
39509             .placement('bottom')
39510             .html(true)
39511             .title(function (d) {
39512                 return iD.ui.tooltipHtml(d.annotation() ?
39513                     t(d.id + '.tooltip', {action: d.annotation()}) :
39514                     t(d.id + '.nothing'), d.cmd);
39515             });
39516
39517         var buttons = selection.selectAll('button')
39518             .data(commands)
39519             .enter().append('button')
39520             .attr('class', 'col6 disabled')
39521             .on('click', function(d) { return d.action(); })
39522             .call(tooltip);
39523
39524         buttons.each(function(d) {
39525             d3.select(this)
39526                 .call(iD.svg.Icon('#icon-' + d.id));
39527         });
39528
39529         var keybinding = d3.keybinding('undo')
39530             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
39531             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
39532
39533         d3.select(document)
39534             .call(keybinding);
39535
39536         context.history()
39537             .on('change.undo_redo', update);
39538
39539         context
39540             .on('enter.undo_redo', update);
39541
39542         function update() {
39543             buttons
39544                 .property('disabled', saving())
39545                 .classed('disabled', function(d) { return !d.annotation(); })
39546                 .each(function() {
39547                     var selection = d3.select(this);
39548                     if (selection.property('tooltipVisible')) {
39549                         selection.call(tooltip.show);
39550                     }
39551                 });
39552         }
39553     };
39554 };
39555 iD.ui.ViewOnOSM = function(context) {
39556     var id;
39557
39558     function viewOnOSM(selection) {
39559         var entity = context.entity(id);
39560
39561         selection.style('display', entity.isNew() ? 'none' : null);
39562
39563         var $link = selection.selectAll('.view-on-osm')
39564             .data([0]);
39565
39566         $link.enter()
39567             .append('a')
39568             .attr('class', 'view-on-osm')
39569             .attr('target', '_blank')
39570             .call(iD.svg.Icon('#icon-out-link', 'inline'))
39571             .append('span')
39572             .text(t('inspector.view_on_osm'));
39573
39574         $link
39575             .attr('href', context.connection().entityURL(entity));
39576     }
39577
39578     viewOnOSM.entityID = function(_) {
39579         if (!arguments.length) return id;
39580         id = _;
39581         return viewOnOSM;
39582     };
39583
39584     return viewOnOSM;
39585 };
39586 iD.ui.Zoom = function(context) {
39587     var zooms = [{
39588         id: 'zoom-in',
39589         icon: 'plus',
39590         title: t('zoom.in'),
39591         action: context.zoomIn,
39592         key: '+'
39593     }, {
39594         id: 'zoom-out',
39595         icon: 'minus',
39596         title: t('zoom.out'),
39597         action: context.zoomOut,
39598         key: '-'
39599     }];
39600
39601     function zoomIn() {
39602         d3.event.preventDefault();
39603         if (!context.inIntro()) context.zoomIn();
39604     }
39605
39606     function zoomOut() {
39607         d3.event.preventDefault();
39608         if (!context.inIntro()) context.zoomOut();
39609     }
39610
39611     function zoomInFurther() {
39612         d3.event.preventDefault();
39613         if (!context.inIntro()) context.zoomInFurther();
39614     }
39615
39616     function zoomOutFurther() {
39617         d3.event.preventDefault();
39618         if (!context.inIntro()) context.zoomOutFurther();
39619     }
39620
39621
39622     return function(selection) {
39623         var button = selection.selectAll('button')
39624             .data(zooms)
39625             .enter().append('button')
39626             .attr('tabindex', -1)
39627             .attr('class', function(d) { return d.id; })
39628             .on('click.editor', function(d) { d.action(); })
39629             .call(bootstrap.tooltip()
39630                 .placement('left')
39631                 .html(true)
39632                 .title(function(d) {
39633                     return iD.ui.tooltipHtml(d.title, d.key);
39634                 }));
39635
39636         button.each(function(d) {
39637             d3.select(this)
39638                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
39639         });
39640
39641         var keybinding = d3.keybinding('zoom');
39642
39643         _.each(['=','ffequals','plus','ffplus'], function(key) {
39644             keybinding.on(key, zoomIn);
39645             keybinding.on('⇧' + key, zoomIn);
39646             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
39647             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
39648         });
39649         _.each(['-','ffminus','_','dash'], function(key) {
39650             keybinding.on(key, zoomOut);
39651             keybinding.on('⇧' + key, zoomOut);
39652             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
39653             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
39654         });
39655
39656         d3.select(document)
39657             .call(keybinding);
39658     };
39659 };
39660 iD.ui.preset.access = function(field) {
39661     var dispatch = d3.dispatch('change'),
39662         items;
39663
39664     function access(selection) {
39665         var wrap = selection.selectAll('.preset-input-wrap')
39666             .data([0]);
39667
39668         wrap.enter().append('div')
39669             .attr('class', 'cf preset-input-wrap')
39670             .append('ul');
39671
39672         items = wrap.select('ul').selectAll('li')
39673             .data(field.keys);
39674
39675         // Enter
39676
39677         var enter = items.enter().append('li')
39678             .attr('class', function(d) { return 'cf preset-access-' + d; });
39679
39680         enter.append('span')
39681             .attr('class', 'col6 label preset-label-access')
39682             .attr('for', function(d) { return 'preset-input-access-' + d; })
39683             .text(function(d) { return field.t('types.' + d); });
39684
39685         enter.append('div')
39686             .attr('class', 'col6 preset-input-access-wrap')
39687             .append('input')
39688             .attr('type', 'text')
39689             .attr('class', 'preset-input-access')
39690             .attr('id', function(d) { return 'preset-input-access-' + d; })
39691             .each(function(d) {
39692                 d3.select(this)
39693                     .call(d3.combobox()
39694                         .data(access.options(d)));
39695             });
39696
39697         // Update
39698
39699         wrap.selectAll('.preset-input-access')
39700             .on('change', change)
39701             .on('blur', change);
39702     }
39703
39704     function change(d) {
39705         var tag = {};
39706         tag[d] = d3.select(this).value() || undefined;
39707         dispatch.change(tag);
39708     }
39709
39710     access.options = function(type) {
39711         var options = ['no', 'permissive', 'private', 'destination'];
39712
39713         if (type !== 'access') {
39714             options.unshift('yes');
39715             options.push('designated');
39716
39717             if (type === 'bicycle') {
39718                 options.push('dismount');
39719             }
39720         }
39721
39722         return options.map(function(option) {
39723             return {
39724                 title: field.t('options.' + option + '.description'),
39725                 value: option
39726             };
39727         });
39728     };
39729
39730     var placeholders = {
39731         footway: {
39732             foot: 'designated',
39733             motor_vehicle: 'no'
39734         },
39735         steps: {
39736             foot: 'yes',
39737             motor_vehicle: 'no',
39738             bicycle: 'no',
39739             horse: 'no'
39740         },
39741         pedestrian: {
39742             foot: 'yes',
39743             motor_vehicle: 'no'
39744         },
39745         cycleway: {
39746             motor_vehicle: 'no',
39747             bicycle: 'designated'
39748         },
39749         bridleway: {
39750             motor_vehicle: 'no',
39751             horse: 'designated'
39752         },
39753         path: {
39754             foot: 'yes',
39755             motor_vehicle: 'no',
39756             bicycle: 'yes',
39757             horse: 'yes'
39758         },
39759         motorway: {
39760             foot: 'no',
39761             motor_vehicle: 'yes',
39762             bicycle: 'no',
39763             horse: 'no'
39764         },
39765         trunk: {
39766             motor_vehicle: 'yes'
39767         },
39768         primary: {
39769             foot: 'yes',
39770             motor_vehicle: 'yes',
39771             bicycle: 'yes',
39772             horse: 'yes'
39773         },
39774         secondary: {
39775             foot: 'yes',
39776             motor_vehicle: 'yes',
39777             bicycle: 'yes',
39778             horse: 'yes'
39779         },
39780         tertiary: {
39781             foot: 'yes',
39782             motor_vehicle: 'yes',
39783             bicycle: 'yes',
39784             horse: 'yes'
39785         },
39786         residential: {
39787             foot: 'yes',
39788             motor_vehicle: 'yes',
39789             bicycle: 'yes',
39790             horse: 'yes'
39791         },
39792         unclassified: {
39793             foot: 'yes',
39794             motor_vehicle: 'yes',
39795             bicycle: 'yes',
39796             horse: 'yes'
39797         },
39798         service: {
39799             foot: 'yes',
39800             motor_vehicle: 'yes',
39801             bicycle: 'yes',
39802             horse: 'yes'
39803         },
39804         motorway_link: {
39805             foot: 'no',
39806             motor_vehicle: 'yes',
39807             bicycle: 'no',
39808             horse: 'no'
39809         },
39810         trunk_link: {
39811             motor_vehicle: 'yes'
39812         },
39813         primary_link: {
39814             foot: 'yes',
39815             motor_vehicle: 'yes',
39816             bicycle: 'yes',
39817             horse: 'yes'
39818         },
39819         secondary_link: {
39820             foot: 'yes',
39821             motor_vehicle: 'yes',
39822             bicycle: 'yes',
39823             horse: 'yes'
39824         },
39825         tertiary_link: {
39826             foot: 'yes',
39827             motor_vehicle: 'yes',
39828             bicycle: 'yes',
39829             horse: 'yes'
39830         }
39831     };
39832
39833     access.tags = function(tags) {
39834         items.selectAll('.preset-input-access')
39835             .value(function(d) { return tags[d] || ''; })
39836             .attr('placeholder', function() {
39837                 return tags.access ? tags.access : field.placeholder();
39838             });
39839
39840         // items.selectAll('#preset-input-access-access')
39841         //     .attr('placeholder', 'yes');
39842
39843         _.forEach(placeholders[tags.highway], function(v, k) {
39844             items.selectAll('#preset-input-access-' + k)
39845                 .attr('placeholder', function() { return (tags.access || v); });
39846         });
39847     };
39848
39849     access.focus = function() {
39850         items.selectAll('.preset-input-access')
39851             .node().focus();
39852     };
39853
39854     return d3.rebind(access, dispatch, 'on');
39855 };
39856 iD.ui.preset.address = function(field, context) {
39857     var dispatch = d3.dispatch('init', 'change'),
39858         wrap,
39859         entity,
39860         isInitialized;
39861
39862     var widths = {
39863         housenumber: 1/3,
39864         street: 2/3,
39865         city: 2/3,
39866         state: 1/4,
39867         postcode: 1/3
39868     };
39869
39870     function getStreets() {
39871         var extent = entity.extent(context.graph()),
39872             l = extent.center(),
39873             box = iD.geo.Extent(l).padByMeters(200);
39874
39875         return context.intersects(box)
39876             .filter(isAddressable)
39877             .map(function(d) {
39878                 var loc = context.projection([
39879                     (extent[0][0] + extent[1][0]) / 2,
39880                     (extent[0][1] + extent[1][1]) / 2]),
39881                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
39882                 return {
39883                     title: d.tags.name,
39884                     value: d.tags.name,
39885                     dist: choice.distance
39886                 };
39887             }).sort(function(a, b) {
39888                 return a.dist - b.dist;
39889             });
39890
39891         function isAddressable(d) {
39892             return d.tags.highway && d.tags.name && d.type === 'way';
39893         }
39894     }
39895
39896     function getCities() {
39897         var extent = entity.extent(context.graph()),
39898             l = extent.center(),
39899             box = iD.geo.Extent(l).padByMeters(200);
39900
39901         return context.intersects(box)
39902             .filter(isAddressable)
39903             .map(function(d) {
39904                 return {
39905                     title: d.tags['addr:city'] || d.tags.name,
39906                     value: d.tags['addr:city'] || d.tags.name,
39907                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
39908                 };
39909             }).sort(function(a, b) {
39910                 return a.dist - b.dist;
39911             });
39912
39913         function isAddressable(d) {
39914             if (d.tags.name &&
39915                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
39916                 return true;
39917
39918             if (d.tags.place && d.tags.name && (
39919                     d.tags.place === 'city' ||
39920                     d.tags.place === 'town' ||
39921                     d.tags.place === 'village'))
39922                 return true;
39923
39924             if (d.tags['addr:city']) return true;
39925
39926             return false;
39927         }
39928     }
39929
39930     function getPostCodes() {
39931         var extent = entity.extent(context.graph()),
39932             l = extent.center(),
39933             box = iD.geo.Extent(l).padByMeters(200);
39934
39935         return context.intersects(box)
39936             .filter(isAddressable)
39937             .map(function(d) {
39938                 return {
39939                     title: d.tags['addr:postcode'],
39940                     value: d.tags['addr:postcode'],
39941                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
39942                 };
39943             }).sort(function(a, b) {
39944                 return a.dist - b.dist;
39945             });
39946
39947         function isAddressable(d) {
39948             return d.tags['addr:postcode'];
39949         }
39950     }
39951
39952     function address(selection) {
39953         isInitialized = false;
39954
39955         wrap = selection.selectAll('.preset-input-wrap')
39956             .data([0]);
39957
39958         // Enter
39959
39960         wrap.enter()
39961             .append('div')
39962             .attr('class', 'preset-input-wrap');
39963
39964         var center = entity.extent(context.graph()).center(),
39965             addressFormat;
39966
39967         iD.services.nominatim().countryCode(center, function (err, countryCode) {
39968             addressFormat = _.find(iD.data.addressFormats, function (a) {
39969                 return a && a.countryCodes && _.includes(a.countryCodes, countryCode);
39970             }) || _.first(iD.data.addressFormats);
39971
39972             function row(r) {
39973                 // Normalize widths.
39974                 var total = _.reduce(r, function(sum, field) {
39975                     return sum + (widths[field] || 0.5);
39976                 }, 0);
39977
39978                 return r.map(function (field) {
39979                     return {
39980                         id: field,
39981                         width: (widths[field] || 0.5) / total
39982                     };
39983                 });
39984             }
39985
39986             wrap.selectAll('div')
39987                 .data(addressFormat.format)
39988                 .enter()
39989                 .append('div')
39990                 .attr('class', 'addr-row')
39991                 .selectAll('input')
39992                 .data(row)
39993                 .enter()
39994                 .append('input')
39995                 .property('type', 'text')
39996                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
39997                 .attr('class', function (d) { return 'addr-' + d.id; })
39998                 .style('width', function (d) { return d.width * 100 + '%'; });
39999
40000             // Update
40001
40002             wrap.selectAll('.addr-street')
40003                 .call(d3.combobox()
40004                     .fetcher(function(value, callback) {
40005                         callback(getStreets());
40006                     }));
40007
40008             wrap.selectAll('.addr-city')
40009                 .call(d3.combobox()
40010                     .fetcher(function(value, callback) {
40011                         callback(getCities());
40012                     }));
40013
40014             wrap.selectAll('.addr-postcode')
40015                 .call(d3.combobox()
40016                     .fetcher(function(value, callback) {
40017                         callback(getPostCodes());
40018                     }));
40019
40020             wrap.selectAll('input')
40021                 .on('blur', change())
40022                 .on('change', change());
40023
40024             wrap.selectAll('input:not(.combobox-input)')
40025                 .on('input', change(true));
40026
40027             dispatch.init();
40028             isInitialized = true;
40029         });
40030     }
40031
40032     function change(onInput) {
40033         return function() {
40034             var tags = {};
40035
40036             wrap.selectAll('input')
40037                 .each(function (field) {
40038                     tags['addr:' + field.id] = this.value || undefined;
40039                 });
40040
40041             dispatch.change(tags, onInput);
40042         };
40043     }
40044
40045     function updateTags(tags) {
40046         wrap.selectAll('input')
40047             .value(function (field) {
40048                 return tags['addr:' + field.id] || '';
40049             });
40050     }
40051
40052     address.entity = function(_) {
40053         if (!arguments.length) return entity;
40054         entity = _;
40055         return address;
40056     };
40057
40058     address.tags = function(tags) {
40059         if (isInitialized) {
40060             updateTags(tags);
40061         } else {
40062             dispatch.on('init', function () {
40063                 updateTags(tags);
40064             });
40065         }
40066     };
40067
40068     address.focus = function() {
40069         var node = wrap.selectAll('input').node();
40070         if (node) node.focus();
40071     };
40072
40073     return d3.rebind(address, dispatch, 'on');
40074 };
40075 iD.ui.preset.check =
40076 iD.ui.preset.defaultcheck = function(field) {
40077     var dispatch = d3.dispatch('change'),
40078         options = field.strings && field.strings.options,
40079         values = [],
40080         texts = [],
40081         entity, value, box, text, label;
40082
40083     if (options) {
40084         for (var k in options) {
40085             values.push(k === 'undefined' ? undefined : k);
40086             texts.push(field.t('options.' + k, { 'default': options[k] }));
40087         }
40088     } else {
40089         values = [undefined, 'yes'];
40090         texts = [t('inspector.unknown'), t('inspector.check.yes')];
40091         if (field.type === 'check') {
40092             values.push('no');
40093             texts.push(t('inspector.check.no'));
40094         }
40095     }
40096
40097     var check = function(selection) {
40098         // hack: pretend oneway field is a oneway_yes field
40099         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
40100         if (field.id === 'oneway') {
40101             for (var key in entity.tags) {
40102                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
40103                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
40104                     break;
40105                 }
40106             }
40107         }
40108
40109         selection.classed('checkselect', 'true');
40110
40111         label = selection.selectAll('.preset-input-wrap')
40112             .data([0]);
40113
40114         var enter = label.enter().append('label')
40115             .attr('class', 'preset-input-wrap');
40116
40117         enter.append('input')
40118             .property('indeterminate', field.type === 'check')
40119             .attr('type', 'checkbox')
40120             .attr('id', 'preset-input-' + field.id);
40121
40122         enter.append('span')
40123             .text(texts[0])
40124             .attr('class', 'value');
40125
40126         box = label.select('input')
40127             .on('click', function() {
40128                 var t = {};
40129                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
40130                 dispatch.change(t);
40131                 d3.event.stopPropagation();
40132             });
40133
40134         text = label.select('span.value');
40135     };
40136
40137     check.entity = function(_) {
40138         if (!arguments.length) return entity;
40139         entity = _;
40140         return check;
40141     };
40142
40143     check.tags = function(tags) {
40144         value = tags[field.key];
40145         box.property('indeterminate', field.type === 'check' && !value);
40146         box.property('checked', value === 'yes');
40147         text.text(texts[values.indexOf(value)]);
40148         label.classed('set', !!value);
40149     };
40150
40151     check.focus = function() {
40152         box.node().focus();
40153     };
40154
40155     return d3.rebind(check, dispatch, 'on');
40156 };
40157 iD.ui.preset.combo =
40158 iD.ui.preset.typeCombo =
40159 iD.ui.preset.multiCombo = function(field, context) {
40160     var dispatch = d3.dispatch('change'),
40161         isMulti = (field.type === 'multiCombo'),
40162         optstrings = field.strings && field.strings.options,
40163         optarray = field.options,
40164         snake_case = (field.snake_case || (field.snake_case === undefined)),
40165         combobox = d3.combobox().minItems(isMulti ? 1 : 2),
40166         comboData = [],
40167         multiData = [],
40168         container,
40169         input,
40170         entity;
40171
40172     // ensure multiCombo field.key ends with a ':'
40173     if (isMulti && field.key.match(/:$/) === null) {
40174         field.key += ':';
40175     }
40176
40177
40178     function snake(s) {
40179         return s.replace(/\s+/g, '_');
40180     }
40181
40182     function unsnake(s) {
40183         return s.replace(/_+/g, ' ');
40184     }
40185
40186     function clean(s) {
40187         return s.split(';')
40188             .map(function(s) { return s.trim(); })
40189             .join(';');
40190     }
40191
40192
40193     // returns the tag value for a display value
40194     // (for multiCombo, dval should be the key suffix, not the entire key)
40195     function tagValue(dval) {
40196         dval = clean(dval || '');
40197
40198         if (optstrings) {
40199             var match = _.find(comboData, function(o) {
40200                 return o.key && clean(o.value) === dval;
40201             });
40202             if (match) {
40203                 return match.key;
40204             }
40205         }
40206
40207         if (field.type === 'typeCombo' && !dval) {
40208             return 'yes';
40209         }
40210
40211         return (snake_case ? snake(dval) : dval) || undefined;
40212     }
40213
40214
40215     // returns the display value for a tag value
40216     // (for multiCombo, tval should be the key suffix, not the entire key)
40217     function displayValue(tval) {
40218         tval = tval || '';
40219
40220         if (optstrings) {
40221             var match = _.find(comboData, function(o) { return o.key === tval && o.value; });
40222             if (match) {
40223                 return match.value;
40224             }
40225         }
40226
40227         if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
40228             return '';
40229         }
40230
40231         return snake_case ? unsnake(tval) : tval;
40232     }
40233
40234
40235     function objectDifference(a, b) {
40236         return _.reject(a, function(d1) {
40237             return _.some(b, function(d2) { return d1.value === d2.value; });
40238         });
40239     }
40240
40241
40242     function initCombo(selection, attachTo) {
40243         if (optstrings) {
40244             selection.attr('readonly', 'readonly');
40245             selection.call(combobox, attachTo);
40246             setStaticValues(setPlaceholder);
40247
40248         } else if (optarray) {
40249             selection.call(combobox, attachTo);
40250             setStaticValues(setPlaceholder);
40251
40252         } else if (context.taginfo()) {
40253             selection.call(combobox.fetcher(setTaginfoValues), attachTo);
40254             setTaginfoValues('', setPlaceholder);
40255         }
40256     }
40257
40258
40259     function setStaticValues(callback) {
40260         if (!(optstrings || optarray)) return;
40261
40262         if (optstrings) {
40263             comboData = Object.keys(optstrings).map(function(k) {
40264                 var v = field.t('options.' + k, { 'default': optstrings[k] });
40265                 return {
40266                     key: k,
40267                     value: v,
40268                     title: v
40269                 };
40270             });
40271
40272         } else if (optarray) {
40273             comboData = optarray.map(function(k) {
40274                 var v = snake_case ? unsnake(k) : k;
40275                 return {
40276                     key: k,
40277                     value: v,
40278                     title: v
40279                 };
40280             });
40281         }
40282
40283         combobox.data(objectDifference(comboData, multiData));
40284         if (callback) callback(comboData);
40285     }
40286
40287
40288     function setTaginfoValues(q, callback) {
40289         var fn = isMulti ? 'multikeys' : 'values';
40290         context.taginfo()[fn]({
40291             debounce: true,
40292             key: field.key,
40293             geometry: context.geometry(entity.id),
40294             query: (isMulti ? field.key : '') + q
40295         }, function(err, data) {
40296             if (err) return;
40297             comboData = _.map(data, 'value').map(function(k) {
40298                 if (isMulti) k = k.replace(field.key, '');
40299                 var v = snake_case ? unsnake(k) : k;
40300                 return {
40301                     key: k,
40302                     value: v,
40303                     title: v
40304                 };
40305             });
40306             comboData = objectDifference(comboData, multiData);
40307             if (callback) callback(comboData);
40308         });
40309     }
40310
40311
40312     function setPlaceholder(d) {
40313         var ph;
40314         if (isMulti) {
40315             ph = field.placeholder() || t('inspector.add');
40316         } else {
40317             var vals = _.map(d, 'value').filter(function(s) { return s.length < 20; }),
40318                 placeholders = vals.length > 1 ? vals : _.map(d, 'key');
40319             ph = field.placeholder() || placeholders.slice(0, 3).join(', ');
40320         }
40321
40322         input.attr('placeholder', ph + '…');
40323     }
40324
40325
40326     function change() {
40327         var val = tagValue(input.value()),
40328             t = {};
40329
40330         if (isMulti) {
40331             if (!val) return;
40332             container.classed('active', false);
40333             input.value('');
40334             field.keys.push(field.key + val);
40335             t[field.key + val] = 'yes';
40336             window.setTimeout(function() { input.node().focus(); }, 10);
40337
40338         } else {
40339             t[field.key] = val;
40340         }
40341
40342         dispatch.change(t);
40343     }
40344
40345
40346     function removeMultikey(d) {
40347         d3.event.stopPropagation();
40348         var t = {};
40349         t[d.key] = undefined;
40350         dispatch.change(t);
40351     }
40352
40353
40354     function combo(selection) {
40355         if (isMulti) {
40356             container = selection.selectAll('ul').data([0]);
40357
40358             container.enter()
40359                 .append('ul')
40360                 .attr('class', 'form-field-multicombo')
40361                 .on('click', function() {
40362                     window.setTimeout(function() { input.node().focus(); }, 10);
40363                 });
40364
40365         } else {
40366             container = selection;
40367         }
40368
40369         input = container.selectAll('input')
40370             .data([0]);
40371
40372         input.enter()
40373             .append('input')
40374             .attr('type', 'text')
40375             .attr('id', 'preset-input-' + field.id)
40376             .call(initCombo, selection);
40377
40378         input
40379             .on('change', change)
40380             .on('blur', change);
40381
40382         if (isMulti) {
40383             combobox
40384                 .on('accept', function() {
40385                     input.node().blur();
40386                     input.node().focus();
40387                 });
40388
40389             input
40390                 .on('focus', function() { container.classed('active', true); });
40391         }
40392     }
40393
40394
40395     combo.tags = function(tags) {
40396         if (isMulti) {
40397             multiData = [];
40398
40399             // Build multiData array containing keys already set..
40400             Object.keys(tags).forEach(function(key) {
40401                 if (key.indexOf(field.key) !== 0 || tags[key].toLowerCase() !== 'yes') return;
40402
40403                 var suffix = key.substring(field.key.length);
40404                 multiData.push({
40405                     key: key,
40406                     value: displayValue(suffix)
40407                 });
40408             });
40409
40410             // Set keys for form-field modified (needed for undo and reset buttons)..
40411             field.keys = _.map(multiData, 'key');
40412
40413             // Exclude existing multikeys from combo options..
40414             var available = objectDifference(comboData, multiData);
40415             combobox.data(available);
40416
40417             // Hide "Add" button if this field uses fixed set of
40418             // translateable optstrings and they're all currently used..
40419             container.selectAll('.combobox-input, .combobox-caret')
40420                 .classed('hide', optstrings && !available.length);
40421
40422
40423             // Render chips
40424             var chips = container.selectAll('.chips').data(multiData);
40425
40426             var enter = chips.enter()
40427                 .insert('li', 'input')
40428                 .attr('class', 'chips');
40429
40430             enter.append('span');
40431             enter.append('a');
40432
40433             chips.select('span')
40434                 .text(function(d) { return d.value; });
40435
40436             chips.select('a')
40437                 .on('click', removeMultikey)
40438                 .attr('class', 'remove')
40439                 .text('×');
40440
40441             chips.exit()
40442                 .remove();
40443
40444         } else {
40445             input.value(displayValue(tags[field.key]));
40446         }
40447     };
40448
40449
40450     combo.focus = function() {
40451         input.node().focus();
40452     };
40453
40454
40455     combo.entity = function(_) {
40456         if (!arguments.length) return entity;
40457         entity = _;
40458         return combo;
40459     };
40460
40461
40462     return d3.rebind(combo, dispatch, 'on');
40463 };
40464 iD.ui.preset.cycleway = function(field) {
40465     var dispatch = d3.dispatch('change'),
40466         items;
40467
40468     function cycleway(selection) {
40469         var wrap = selection.selectAll('.preset-input-wrap')
40470             .data([0]);
40471
40472         wrap.enter().append('div')
40473             .attr('class', 'cf preset-input-wrap')
40474             .append('ul');
40475
40476         items = wrap.select('ul').selectAll('li')
40477             .data(field.keys);
40478
40479         // Enter
40480
40481         var enter = items.enter().append('li')
40482             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
40483
40484         enter.append('span')
40485             .attr('class', 'col6 label preset-label-cycleway')
40486             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
40487             .text(function(d) { return field.t('types.' + d); });
40488
40489         enter.append('div')
40490             .attr('class', 'col6 preset-input-cycleway-wrap')
40491             .append('input')
40492             .attr('type', 'text')
40493             .attr('class', 'preset-input-cycleway')
40494             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
40495             .each(function(d) {
40496                 d3.select(this)
40497                     .call(d3.combobox()
40498                         .data(cycleway.options(d)));
40499             });
40500
40501         // Update
40502
40503         wrap.selectAll('.preset-input-cycleway')
40504             .on('change', change)
40505             .on('blur', change);
40506     }
40507
40508     function change() {
40509         var inputs = d3.selectAll('.preset-input-cycleway')[0],
40510             left = d3.select(inputs[0]).value(),
40511             right = d3.select(inputs[1]).value(),
40512             tag = {};
40513         if (left === 'none' || left === '') { left = undefined; }
40514         if (right === 'none' || right === '') { right = undefined; }
40515
40516         // Always set both left and right as changing one can affect the other
40517         tag = {
40518             cycleway: undefined,
40519             'cycleway:left': left,
40520             'cycleway:right': right
40521         };
40522
40523         // If the left and right tags match, use the cycleway tag to tag both
40524         // sides the same way
40525         if (left === right) {
40526             tag = {
40527                 cycleway: left,
40528                 'cycleway:left': undefined,
40529                 'cycleway:right': undefined
40530             };
40531         }
40532
40533         dispatch.change(tag);
40534     }
40535
40536     cycleway.options = function() {
40537         return d3.keys(field.strings.options).map(function(option) {
40538             return {
40539                 title: field.t('options.' + option + '.description'),
40540                 value: option
40541             };
40542         });
40543     };
40544
40545     cycleway.tags = function(tags) {
40546         items.selectAll('.preset-input-cycleway')
40547             .value(function(d) {
40548                 // If cycleway is set, always return that
40549                 if (tags.cycleway) {
40550                     return tags.cycleway;
40551                 }
40552                 return tags[d] || '';
40553             })
40554             .attr('placeholder', field.placeholder());
40555     };
40556
40557     cycleway.focus = function() {
40558         items.selectAll('.preset-input-cycleway')
40559             .node().focus();
40560     };
40561
40562     return d3.rebind(cycleway, dispatch, 'on');
40563 };
40564 iD.ui.preset.text =
40565 iD.ui.preset.number =
40566 iD.ui.preset.tel =
40567 iD.ui.preset.email =
40568 iD.ui.preset.url = function(field, context) {
40569
40570     var dispatch = d3.dispatch('change'),
40571         input,
40572         entity;
40573
40574     function i(selection) {
40575         var fieldId = 'preset-input-' + field.id;
40576
40577         input = selection.selectAll('input')
40578             .data([0]);
40579
40580         input.enter().append('input')
40581             .attr('type', field.type)
40582             .attr('id', fieldId)
40583             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
40584
40585         input
40586             .on('input', change(true))
40587             .on('blur', change())
40588             .on('change', change());
40589
40590         if (field.type === 'tel') {
40591             var center = entity.extent(context.graph()).center();
40592             iD.services.nominatim().countryCode(center, function (err, countryCode) {
40593                 if (err || !iD.data.phoneFormats[countryCode]) return;
40594                 selection.selectAll('#' + fieldId)
40595                     .attr('placeholder', iD.data.phoneFormats[countryCode]);
40596             });
40597
40598         } else if (field.type === 'number') {
40599             input.attr('type', 'text');
40600
40601             var spinControl = selection.selectAll('.spin-control')
40602                 .data([0]);
40603
40604             var enter = spinControl.enter().append('div')
40605                 .attr('class', 'spin-control');
40606
40607             enter.append('button')
40608                 .datum(1)
40609                 .attr('class', 'increment')
40610                 .attr('tabindex', -1);
40611
40612             enter.append('button')
40613                 .datum(-1)
40614                 .attr('class', 'decrement')
40615                 .attr('tabindex', -1);
40616
40617             spinControl.selectAll('button')
40618                 .on('click', function(d) {
40619                     d3.event.preventDefault();
40620                     var num = parseInt(input.node().value || 0, 10);
40621                     if (!isNaN(num)) input.node().value = num + d;
40622                     change()();
40623                 });
40624         }
40625     }
40626
40627     function change(onInput) {
40628         return function() {
40629             var t = {};
40630             t[field.key] = input.value() || undefined;
40631             dispatch.change(t, onInput);
40632         };
40633     }
40634
40635     i.entity = function(_) {
40636         if (!arguments.length) return entity;
40637         entity = _;
40638         return i;
40639     };
40640
40641     i.tags = function(tags) {
40642         input.value(tags[field.key] || '');
40643     };
40644
40645     i.focus = function() {
40646         var node = input.node();
40647         if (node) node.focus();
40648     };
40649
40650     return d3.rebind(i, dispatch, 'on');
40651 };
40652 iD.ui.preset.localized = function(field, context) {
40653     var dispatch = d3.dispatch('change', 'input'),
40654         wikipedia = iD.services.wikipedia(),
40655         input, localizedInputs, wikiTitles,
40656         entity;
40657
40658     function i(selection) {
40659         input = selection.selectAll('.localized-main')
40660             .data([0]);
40661
40662         input.enter().append('input')
40663             .attr('type', 'text')
40664             .attr('id', 'preset-input-' + field.id)
40665             .attr('class', 'localized-main')
40666             .attr('placeholder', field.placeholder());
40667
40668         if (field.id === 'name') {
40669             var preset = context.presets().match(entity, context.graph());
40670             input.call(d3.combobox().fetcher(
40671                 iD.util.SuggestNames(preset, iD.data.suggestions)
40672             ));
40673         }
40674
40675         input
40676             .on('input', change(true))
40677             .on('blur', change())
40678             .on('change', change());
40679
40680         var translateButton = selection.selectAll('.localized-add')
40681             .data([0]);
40682
40683         translateButton.enter()
40684             .append('button')
40685             .attr('class', 'button-input-action localized-add minor')
40686             .attr('tabindex', -1)
40687             .call(iD.svg.Icon('#icon-plus'))
40688             .call(bootstrap.tooltip()
40689                 .title(t('translate.translate'))
40690                 .placement('left'));
40691
40692         translateButton
40693             .on('click', addNew);
40694
40695         localizedInputs = selection.selectAll('.localized-wrap')
40696             .data([0]);
40697
40698         localizedInputs.enter().append('div')
40699             .attr('class', 'localized-wrap');
40700     }
40701
40702     function addNew() {
40703         d3.event.preventDefault();
40704         var data = localizedInputs.selectAll('div.entry').data();
40705         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
40706         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
40707         var isLangEn = defaultLang.indexOf('en') > -1;
40708         if (isLangEn || langExists) {
40709           defaultLang = '';
40710         }
40711         data.push({ lang: defaultLang, value: '' });
40712         localizedInputs.call(render, data);
40713     }
40714
40715     function change(onInput) {
40716         return function() {
40717             var t = {};
40718             t[field.key] = d3.select(this).value() || undefined;
40719             dispatch.change(t, onInput);
40720         };
40721     }
40722
40723     function key(lang) { return field.key + ':' + lang; }
40724
40725     function changeLang(d) {
40726         var lang = d3.select(this).value(),
40727             t = {},
40728             language = _.find(iD.data.wikipedia, function(d) {
40729                 return d[0].toLowerCase() === lang.toLowerCase() ||
40730                     d[1].toLowerCase() === lang.toLowerCase();
40731             });
40732
40733         if (language) lang = language[2];
40734
40735         if (d.lang && d.lang !== lang) {
40736             t[key(d.lang)] = undefined;
40737         }
40738
40739         var value = d3.select(this.parentNode)
40740             .selectAll('.localized-value')
40741             .value();
40742
40743         if (lang && value) {
40744             t[key(lang)] = value;
40745         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
40746             t[key(lang)] = wikiTitles[d.lang];
40747         }
40748
40749         d.lang = lang;
40750         dispatch.change(t);
40751     }
40752
40753     function changeValue(d) {
40754         if (!d.lang) return;
40755         var t = {};
40756         t[key(d.lang)] = d3.select(this).value() || undefined;
40757         dispatch.change(t);
40758     }
40759
40760     function fetcher(value, cb) {
40761         var v = value.toLowerCase();
40762
40763         cb(iD.data.wikipedia.filter(function(d) {
40764             return d[0].toLowerCase().indexOf(v) >= 0 ||
40765             d[1].toLowerCase().indexOf(v) >= 0 ||
40766             d[2].toLowerCase().indexOf(v) >= 0;
40767         }).map(function(d) {
40768             return { value: d[1] };
40769         }));
40770     }
40771
40772     function render(selection, data) {
40773         var wraps = selection.selectAll('div.entry').
40774             data(data, function(d) { return d.lang; });
40775
40776         var innerWrap = wraps.enter()
40777             .insert('div', ':first-child');
40778
40779         innerWrap.attr('class', 'entry')
40780             .each(function() {
40781                 var wrap = d3.select(this);
40782                 var langcombo = d3.combobox().fetcher(fetcher).minItems(0);
40783
40784                 var label = wrap.append('label')
40785                     .attr('class','form-label')
40786                     .text(t('translate.localized_translation_label'))
40787                     .attr('for','localized-lang');
40788
40789                 label.append('button')
40790                     .attr('class', 'minor remove')
40791                     .on('click', function(d){
40792                         d3.event.preventDefault();
40793                         var t = {};
40794                         t[key(d.lang)] = undefined;
40795                         dispatch.change(t);
40796                         d3.select(this.parentNode.parentNode)
40797                             .style('top','0')
40798                             .style('max-height','240px')
40799                             .transition()
40800                             .style('opacity', '0')
40801                             .style('max-height','0px')
40802                             .remove();
40803                     })
40804                     .call(iD.svg.Icon('#operation-delete'));
40805
40806                 wrap.append('input')
40807                     .attr('class', 'localized-lang')
40808                     .attr('type', 'text')
40809                     .attr('placeholder',t('translate.localized_translation_language'))
40810                     .on('blur', changeLang)
40811                     .on('change', changeLang)
40812                     .call(langcombo);
40813
40814                 wrap.append('input')
40815                     .on('blur', changeValue)
40816                     .on('change', changeValue)
40817                     .attr('type', 'text')
40818                     .attr('placeholder', t('translate.localized_translation_name'))
40819                     .attr('class', 'localized-value');
40820             });
40821
40822         innerWrap
40823             .style('margin-top', '0px')
40824             .style('max-height', '0px')
40825             .style('opacity', '0')
40826             .transition()
40827             .duration(200)
40828             .style('margin-top', '10px')
40829             .style('max-height', '240px')
40830             .style('opacity', '1')
40831             .each('end', function() {
40832                 d3.select(this)
40833                     .style('max-height', '')
40834                     .style('overflow', 'visible');
40835             });
40836
40837         wraps.exit()
40838             .transition()
40839             .duration(200)
40840             .style('max-height','0px')
40841             .style('opacity', '0')
40842             .style('top','-10px')
40843             .remove();
40844
40845         var entry = selection.selectAll('.entry');
40846
40847         entry.select('.localized-lang')
40848             .value(function(d) {
40849                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
40850                 return lang ? lang[1] : d.lang;
40851             });
40852
40853         entry.select('.localized-value')
40854             .value(function(d) { return d.value; });
40855     }
40856
40857     i.tags = function(tags) {
40858         // Fetch translations from wikipedia
40859         if (tags.wikipedia && !wikiTitles) {
40860             wikiTitles = {};
40861             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
40862             if (wm && wm[0] && wm[1]) {
40863                 wikipedia.translations(wm[1], wm[2], function(d) {
40864                     wikiTitles = d;
40865                 });
40866             }
40867         }
40868
40869         input.value(tags[field.key] || '');
40870
40871         var postfixed = [], k, m;
40872         for (k in tags) {
40873             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
40874             if (m && m[1] === field.key && m[2]) {
40875                 postfixed.push({ lang: m[2], value: tags[k] });
40876             }
40877         }
40878
40879         localizedInputs.call(render, postfixed.reverse());
40880     };
40881
40882     i.focus = function() {
40883         input.node().focus();
40884     };
40885
40886     i.entity = function(_) {
40887         entity = _;
40888     };
40889
40890     return d3.rebind(i, dispatch, 'on');
40891 };
40892 iD.ui.preset.maxspeed = function(field, context) {
40893     var dispatch = d3.dispatch('change'),
40894         entity,
40895         imperial,
40896         unitInput,
40897         combobox,
40898         input;
40899
40900     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
40901         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
40902
40903     function maxspeed(selection) {
40904         combobox = d3.combobox();
40905         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
40906
40907         input = selection.selectAll('#preset-input-' + field.id)
40908             .data([0]);
40909
40910         input.enter().append('input')
40911             .attr('type', 'text')
40912             .attr('id', 'preset-input-' + field.id)
40913             .attr('placeholder', field.placeholder());
40914
40915         input
40916             .call(combobox)
40917             .on('change', change)
40918             .on('blur', change);
40919
40920         var childNodes = context.graph().childNodes(context.entity(entity.id)),
40921             loc = childNodes[~~(childNodes.length/2)].loc;
40922
40923         imperial = _.some(iD.data.imperial.features, function(f) {
40924             return _.some(f.geometry.coordinates, function(d) {
40925                 return iD.geo.pointInPolygon(loc, d[0]);
40926             });
40927         });
40928
40929         unitInput = selection.selectAll('input.maxspeed-unit')
40930             .data([0]);
40931
40932         unitInput.enter().append('input')
40933             .attr('type', 'text')
40934             .attr('class', 'maxspeed-unit');
40935
40936         unitInput
40937             .on('blur', changeUnits)
40938             .on('change', changeUnits)
40939             .call(unitCombobox);
40940
40941         function changeUnits() {
40942             imperial = unitInput.value() === 'mph';
40943             unitInput.value(imperial ? 'mph' : 'km/h');
40944             setSuggestions();
40945             change();
40946         }
40947
40948     }
40949
40950     function setSuggestions() {
40951         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
40952         unitInput.value(imperial ? 'mph' : 'km/h');
40953     }
40954
40955     function comboValues(d) {
40956         return {
40957             value: d.toString(),
40958             title: d.toString()
40959         };
40960     }
40961
40962     function change() {
40963         var tag = {},
40964             value = input.value();
40965
40966         if (!value) {
40967             tag[field.key] = undefined;
40968         } else if (isNaN(value) || !imperial) {
40969             tag[field.key] = value;
40970         } else {
40971             tag[field.key] = value + ' mph';
40972         }
40973
40974         dispatch.change(tag);
40975     }
40976
40977     maxspeed.tags = function(tags) {
40978         var value = tags[field.key];
40979
40980         if (value && value.indexOf('mph') >= 0) {
40981             value = parseInt(value, 10);
40982             imperial = true;
40983         } else if (value) {
40984             imperial = false;
40985         }
40986
40987         setSuggestions();
40988
40989         input.value(value || '');
40990     };
40991
40992     maxspeed.focus = function() {
40993         input.node().focus();
40994     };
40995
40996     maxspeed.entity = function(_) {
40997         entity = _;
40998     };
40999
41000     return d3.rebind(maxspeed, dispatch, 'on');
41001 };
41002 iD.ui.preset.radio = function(field) {
41003     var dispatch = d3.dispatch('change'),
41004         labels, radios, placeholder;
41005
41006     function radio(selection) {
41007         selection.classed('preset-radio', true);
41008
41009         var wrap = selection.selectAll('.preset-input-wrap')
41010             .data([0]);
41011
41012         var buttonWrap = wrap.enter().append('div')
41013             .attr('class', 'preset-input-wrap toggle-list');
41014
41015         buttonWrap.append('span')
41016             .attr('class', 'placeholder');
41017
41018         placeholder = selection.selectAll('.placeholder');
41019
41020         labels = wrap.selectAll('label')
41021             .data(field.options || field.keys);
41022
41023         var enter = labels.enter().append('label');
41024
41025         enter.append('input')
41026             .attr('type', 'radio')
41027             .attr('name', field.id)
41028             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
41029             .attr('checked', false);
41030
41031         enter.append('span')
41032             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
41033
41034         radios = labels.selectAll('input')
41035             .on('change', change);
41036     }
41037
41038     function change() {
41039         var t = {};
41040         if (field.key) t[field.key] = undefined;
41041         radios.each(function(d) {
41042             var active = d3.select(this).property('checked');
41043             if (field.key) {
41044                 if (active) t[field.key] = d;
41045             } else {
41046                 t[d] = active ? 'yes' : undefined;
41047             }
41048         });
41049         dispatch.change(t);
41050     }
41051
41052     radio.tags = function(tags) {
41053         function checked(d) {
41054             if (field.key) {
41055                 return tags[field.key] === d;
41056             } else {
41057                 return !!(tags[d] && tags[d] !== 'no');
41058             }
41059         }
41060
41061         labels.classed('active', checked);
41062         radios.property('checked', checked);
41063         var selection = radios.filter(function() { return this.checked; });
41064         if (selection.empty()) {
41065             placeholder.text(t('inspector.none'));
41066         } else {
41067             placeholder.text(selection.attr('value'));
41068         }
41069     };
41070
41071     radio.focus = function() {
41072         radios.node().focus();
41073     };
41074
41075     return d3.rebind(radio, dispatch, 'on');
41076 };
41077 iD.ui.preset.restrictions = function(field, context) {
41078     var dispatch = d3.dispatch('change'),
41079         hover = iD.behavior.Hover(context),
41080         vertexID,
41081         fromNodeID;
41082
41083
41084     function restrictions(selection) {
41085         // if form field is hidden or has detached from dom, clean up.
41086         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
41087             selection.call(restrictions.off);
41088             return;
41089         }
41090
41091         var wrap = selection.selectAll('.preset-input-wrap')
41092             .data([0]);
41093
41094         var enter = wrap.enter()
41095             .append('div')
41096             .attr('class', 'preset-input-wrap');
41097
41098         enter
41099             .append('div')
41100             .attr('class', 'restriction-help');
41101
41102
41103         var intersection = iD.geo.Intersection(context.graph(), vertexID),
41104             graph = intersection.graph,
41105             vertex = graph.entity(vertexID),
41106             filter = d3.functor(true),
41107             extent = iD.geo.Extent(),
41108             projection = iD.geo.RawMercator();
41109
41110         var d = wrap.dimensions(),
41111             c = [d[0] / 2, d[1] / 2],
41112             z = 24;
41113
41114         projection
41115             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
41116
41117         var s = projection(vertex.loc);
41118
41119         projection
41120             .translate([c[0] - s[0], c[1] - s[1]])
41121             .clipExtent([[0, 0], d]);
41122
41123         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
41124             drawVertices = iD.svg.Vertices(projection, context),
41125             drawLines = iD.svg.Lines(projection, context),
41126             drawTurns = iD.svg.Turns(projection, context);
41127
41128         enter
41129             .call(drawLayers)
41130             .selectAll('.surface')
41131             .call(hover);
41132
41133
41134         var surface = wrap.selectAll('.surface');
41135
41136         surface
41137             .dimensions(d)
41138             .call(drawVertices, graph, [vertex], filter, extent, z)
41139             .call(drawLines, graph, intersection.ways, filter)
41140             .call(drawTurns, graph, intersection.turns(fromNodeID));
41141
41142         surface
41143             .on('click.restrictions', click)
41144             .on('mouseover.restrictions', mouseover)
41145             .on('mouseout.restrictions', mouseout);
41146
41147         surface
41148             .selectAll('.selected')
41149             .classed('selected', false);
41150
41151         if (fromNodeID) {
41152             surface
41153                 .selectAll('.' + intersection.highways[fromNodeID].id)
41154                 .classed('selected', true);
41155         }
41156
41157         mouseout();
41158
41159         context.history()
41160             .on('change.restrictions', render);
41161
41162         d3.select(window)
41163             .on('resize.restrictions', function() {
41164                 wrap.dimensions(null);
41165                 render();
41166             });
41167
41168         function click() {
41169             var datum = d3.event.target.__data__;
41170             if (datum instanceof iD.Entity) {
41171                 fromNodeID = intersection.adjacentNodeId(datum.id);
41172                 render();
41173             } else if (datum instanceof iD.geo.Turn) {
41174                 if (datum.restriction) {
41175                     context.perform(
41176                         iD.actions.UnrestrictTurn(datum, projection),
41177                         t('operations.restriction.annotation.delete'));
41178                 } else {
41179                     context.perform(
41180                         iD.actions.RestrictTurn(datum, projection),
41181                         t('operations.restriction.annotation.create'));
41182                 }
41183             }
41184         }
41185
41186         function mouseover() {
41187             var datum = d3.event.target.__data__;
41188             if (datum instanceof iD.geo.Turn) {
41189                 var graph = context.graph(),
41190                     presets = context.presets(),
41191                     preset;
41192
41193                 if (datum.restriction) {
41194                     preset = presets.match(graph.entity(datum.restriction), graph);
41195                 } else {
41196                     preset = presets.item('type/restriction/' +
41197                         iD.geo.inferRestriction(
41198                             graph,
41199                             datum.from,
41200                             datum.via,
41201                             datum.to,
41202                             projection));
41203                 }
41204
41205                 wrap.selectAll('.restriction-help')
41206                     .text(t('operations.restriction.help.' +
41207                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
41208                         {restriction: preset.name()}));
41209             }
41210         }
41211
41212         function mouseout() {
41213             wrap.selectAll('.restriction-help')
41214                 .text(t('operations.restriction.help.' +
41215                     (fromNodeID ? 'toggle' : 'select')));
41216         }
41217
41218         function render() {
41219             if (context.hasEntity(vertexID)) {
41220                 restrictions(selection);
41221             }
41222         }
41223     }
41224
41225     restrictions.entity = function(_) {
41226         if (!vertexID || vertexID !== _.id) {
41227             fromNodeID = null;
41228             vertexID = _.id;
41229         }
41230     };
41231
41232     restrictions.tags = function() {};
41233     restrictions.focus = function() {};
41234
41235     restrictions.off = function(selection) {
41236         selection.selectAll('.surface')
41237             .call(hover.off)
41238             .on('click.restrictions', null)
41239             .on('mouseover.restrictions', null)
41240             .on('mouseout.restrictions', null);
41241
41242         context.history()
41243             .on('change.restrictions', null);
41244
41245         d3.select(window)
41246             .on('resize.restrictions', null);
41247     };
41248
41249     return d3.rebind(restrictions, dispatch, 'on');
41250 };
41251 iD.ui.preset.textarea = function(field) {
41252     var dispatch = d3.dispatch('change'),
41253         input;
41254
41255     function i(selection) {
41256         input = selection.selectAll('textarea')
41257             .data([0]);
41258
41259         input.enter().append('textarea')
41260             .attr('id', 'preset-input-' + field.id)
41261             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
41262             .attr('maxlength', 255);
41263
41264         input
41265             .on('input', change(true))
41266             .on('blur', change())
41267             .on('change', change());
41268     }
41269
41270     function change(onInput) {
41271         return function() {
41272             var t = {};
41273             t[field.key] = input.value() || undefined;
41274             dispatch.change(t, onInput);
41275         };
41276     }
41277
41278     i.tags = function(tags) {
41279         input.value(tags[field.key] || '');
41280     };
41281
41282     i.focus = function() {
41283         input.node().focus();
41284     };
41285
41286     return d3.rebind(i, dispatch, 'on');
41287 };
41288 iD.ui.preset.wikipedia = function(field, context) {
41289     var dispatch = d3.dispatch('change'),
41290         wikipedia = iD.services.wikipedia(),
41291         link, entity, lang, title;
41292
41293     function i(selection) {
41294         var langcombo = d3.combobox()
41295             .fetcher(function(value, cb) {
41296                 var v = value.toLowerCase();
41297
41298                 cb(iD.data.wikipedia.filter(function(d) {
41299                     return d[0].toLowerCase().indexOf(v) >= 0 ||
41300                         d[1].toLowerCase().indexOf(v) >= 0 ||
41301                         d[2].toLowerCase().indexOf(v) >= 0;
41302                 }).map(function(d) {
41303                     return { value: d[1] };
41304                 }));
41305             });
41306
41307         var titlecombo = d3.combobox()
41308             .fetcher(function(value, cb) {
41309
41310                 if (!value) value = context.entity(entity.id).tags.name || '';
41311                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
41312
41313                 searchfn(language()[2], value, function(query, data) {
41314                     cb(data.map(function(d) {
41315                         return { value: d };
41316                     }));
41317                 });
41318             });
41319
41320         lang = selection.selectAll('input.wiki-lang')
41321             .data([0]);
41322
41323         lang.enter().append('input')
41324             .attr('type', 'text')
41325             .attr('class', 'wiki-lang')
41326             .attr('placeholder', t('translate.localized_translation_language'))
41327             .value('English');
41328
41329         lang
41330             .call(langcombo)
41331             .on('blur', changeLang)
41332             .on('change', changeLang);
41333
41334         title = selection.selectAll('input.wiki-title')
41335             .data([0]);
41336
41337         title.enter().append('input')
41338             .attr('type', 'text')
41339             .attr('class', 'wiki-title')
41340             .attr('id', 'preset-input-' + field.id);
41341
41342         title
41343             .call(titlecombo)
41344             .on('blur', change)
41345             .on('change', change);
41346
41347         link = selection.selectAll('a.wiki-link')
41348             .data([0]);
41349
41350         link.enter().append('a')
41351             .attr('class', 'wiki-link button-input-action minor')
41352             .attr('tabindex', -1)
41353             .attr('target', '_blank')
41354             .call(iD.svg.Icon('#icon-out-link', 'inline'));
41355     }
41356
41357     function language() {
41358         var value = lang.value().toLowerCase();
41359         var locale = iD.detect().locale.toLowerCase();
41360         var localeLanguage;
41361         return _.find(iD.data.wikipedia, function(d) {
41362             if (d[2] === locale) localeLanguage = d;
41363             return d[0].toLowerCase() === value ||
41364                 d[1].toLowerCase() === value ||
41365                 d[2] === value;
41366         }) || localeLanguage || ['English', 'English', 'en'];
41367     }
41368
41369     function changeLang() {
41370         lang.value(language()[1]);
41371         change();
41372     }
41373
41374     function change() {
41375         var value = title.value(),
41376             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
41377             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
41378             anchor;
41379
41380         if (l) {
41381             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
41382             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
41383             if (m[3]) {
41384                 try {
41385                     // Best-effort `anchordecode:` implementation
41386                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
41387                 } catch (e) {
41388                     anchor = decodeURIComponent(m[3]);
41389                 }
41390                 value += '#' + anchor.replace(/_/g, ' ');
41391             }
41392             value = value.slice(0, 1).toUpperCase() + value.slice(1);
41393             lang.value(l[1]);
41394             title.value(value);
41395         }
41396
41397         var t = {};
41398         t[field.key] = value ? language()[2] + ':' + value : undefined;
41399         dispatch.change(t);
41400     }
41401
41402     i.tags = function(tags) {
41403         var value = tags[field.key] || '',
41404             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
41405             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
41406             anchor = m && m[3];
41407
41408         // value in correct format
41409         if (l) {
41410             lang.value(l[1]);
41411             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
41412             if (anchor) {
41413                 try {
41414                     // Best-effort `anchorencode:` implementation
41415                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
41416                 } catch (e) {
41417                     anchor = anchor.replace(/ /g, '_');
41418                 }
41419             }
41420             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
41421                       m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
41422
41423         // unrecognized value format
41424         } else {
41425             title.value(value);
41426             if (value && value !== '') {
41427                 lang.value('');
41428             }
41429             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
41430         }
41431     };
41432
41433     i.entity = function(_) {
41434         entity = _;
41435     };
41436
41437     i.focus = function() {
41438         title.node().focus();
41439     };
41440
41441     return d3.rebind(i, dispatch, 'on');
41442 };
41443 iD.ui.intro.area = function(context, reveal) {
41444     var event = d3.dispatch('done'),
41445         timeout;
41446
41447     var step = {
41448         title: 'intro.areas.title'
41449     };
41450
41451     step.enter = function() {
41452         var playground = [-85.63552, 41.94159],
41453             corner = [-85.63565411045074, 41.9417715536927];
41454         context.map().centerZoom(playground, 19);
41455         reveal('button.add-area',
41456             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
41457             { tooltipClass: 'intro-areas-add' });
41458
41459         context.on('enter.intro', addArea);
41460
41461         function addArea(mode) {
41462             if (mode.id !== 'add-area') return;
41463             context.on('enter.intro', drawArea);
41464
41465             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
41466             var pointBox = iD.ui.intro.pad(corner, padding, context);
41467             reveal(pointBox, t('intro.areas.corner'));
41468
41469             context.map().on('move.intro', function() {
41470                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
41471                 pointBox = iD.ui.intro.pad(corner, padding, context);
41472                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
41473             });
41474         }
41475
41476         function drawArea(mode) {
41477             if (mode.id !== 'draw-area') return;
41478             context.on('enter.intro', enterSelect);
41479
41480             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
41481             var pointBox = iD.ui.intro.pad(playground, padding, context);
41482             reveal(pointBox, t('intro.areas.place'));
41483
41484             context.map().on('move.intro', function() {
41485                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
41486                 pointBox = iD.ui.intro.pad(playground, padding, context);
41487                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
41488             });
41489         }
41490
41491         function enterSelect(mode) {
41492             if (mode.id !== 'select') return;
41493             context.map().on('move.intro', null);
41494             context.on('enter.intro', null);
41495
41496             timeout = setTimeout(function() {
41497                 reveal('.preset-search-input',
41498                     t('intro.areas.search',
41499                     { name: context.presets().item('leisure/playground').name() }));
41500                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
41501             }, 500);
41502         }
41503
41504         function keySearch() {
41505             var first = d3.select('.preset-list-item:first-child');
41506             if (first.classed('preset-leisure-playground')) {
41507                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
41508                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
41509                 d3.select('.preset-search-input').on('keyup.intro', null);
41510             }
41511         }
41512
41513         function selectedPreset() {
41514             reveal('.pane',
41515                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41516             context.on('exit.intro', event.done);
41517         }
41518     };
41519
41520     step.exit = function() {
41521         window.clearTimeout(timeout);
41522         context.on('enter.intro', null);
41523         context.on('exit.intro', null);
41524         context.history().on('change.intro', null);
41525         context.map().on('move.intro', null);
41526         d3.select('.preset-search-input').on('keyup.intro', null);
41527     };
41528
41529     return d3.rebind(step, event, 'on');
41530 };
41531 iD.ui.intro.line = function(context, reveal) {
41532     var event = d3.dispatch('done'),
41533         timeouts = [];
41534
41535     var step = {
41536         title: 'intro.lines.title'
41537     };
41538
41539     function timeout(f, t) {
41540         timeouts.push(window.setTimeout(f, t));
41541     }
41542
41543     function eventCancel() {
41544         d3.event.stopPropagation();
41545         d3.event.preventDefault();
41546     }
41547
41548     step.enter = function() {
41549         var centroid = [-85.62830, 41.95699];
41550         var midpoint = [-85.62975395449628, 41.95787501510204];
41551         var start = [-85.6297754121684, 41.95805253325314];
41552         var intersection = [-85.62974496187628, 41.95742515554585];
41553
41554         context.map().centerZoom(start, 18);
41555         reveal('button.add-line',
41556             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
41557             { tooltipClass: 'intro-lines-add' });
41558
41559         context.on('enter.intro', addLine);
41560
41561         function addLine(mode) {
41562             if (mode.id !== 'add-line') return;
41563             context.on('enter.intro', drawLine);
41564
41565             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
41566             var pointBox = iD.ui.intro.pad(start, padding, context);
41567             reveal(pointBox, t('intro.lines.start'));
41568
41569             context.map().on('move.intro', function() {
41570                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
41571                 pointBox = iD.ui.intro.pad(start, padding, context);
41572                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
41573             });
41574         }
41575
41576         function drawLine(mode) {
41577             if (mode.id !== 'draw-line') return;
41578             context.history().on('change.intro', addIntersection);
41579             context.on('enter.intro', retry);
41580
41581             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
41582             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
41583             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
41584
41585             context.map().on('move.intro', function() {
41586                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
41587                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
41588                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
41589             });
41590         }
41591
41592         // ended line before creating intersection
41593         function retry(mode) {
41594             if (mode.id !== 'select') return;
41595             var pointBox = iD.ui.intro.pad(intersection, 30, context),
41596                 ids = mode.selectedIDs();
41597             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
41598             d3.select(window).on('mousedown.intro', eventCancel, true);
41599
41600             timeout(function() {
41601                 context.replace(iD.actions.DeleteMultiple(ids));
41602                 step.exit();
41603                 step.enter();
41604             }, 3000);
41605         }
41606
41607         function addIntersection(changes) {
41608             if ( _.some(changes.created(), function(d) {
41609                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
41610             })) {
41611                 context.history().on('change.intro', null);
41612                 context.on('enter.intro', enterSelect);
41613
41614                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
41615                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
41616                 reveal(pointBox, t('intro.lines.finish'));
41617
41618                 context.map().on('move.intro', function() {
41619                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
41620                     pointBox = iD.ui.intro.pad(centroid, padding, context);
41621                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
41622                 });
41623             }
41624         }
41625
41626         function enterSelect(mode) {
41627             if (mode.id !== 'select') return;
41628             context.map().on('move.intro', null);
41629             context.on('enter.intro', null);
41630             d3.select('#curtain').style('pointer-events', 'all');
41631
41632             presetCategory();
41633         }
41634
41635         function presetCategory() {
41636             timeout(function() {
41637                 d3.select('#curtain').style('pointer-events', 'none');
41638                 var road = d3.select('.preset-category-road .preset-list-button');
41639                 reveal(road.node(), t('intro.lines.road'));
41640                 road.one('click.intro', roadCategory);
41641             }, 500);
41642         }
41643
41644         function roadCategory() {
41645             timeout(function() {
41646                 var grid = d3.select('.subgrid');
41647                 reveal(grid.node(), t('intro.lines.residential'));
41648                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
41649                     .one('click.intro', retryPreset);
41650                 grid.selectAll('.preset-highway-residential .preset-list-button')
41651                     .one('click.intro', roadDetails);
41652             }, 500);
41653         }
41654
41655         // selected wrong road type
41656         function retryPreset() {
41657             timeout(function() {
41658                 var preset = d3.select('.entity-editor-pane .preset-list-button');
41659                 reveal(preset.node(), t('intro.lines.wrong_preset'));
41660                 preset.one('click.intro', presetCategory);
41661             }, 500);
41662         }
41663
41664         function roadDetails() {
41665             reveal('.pane',
41666                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41667             context.on('exit.intro', event.done);
41668         }
41669
41670     };
41671
41672     step.exit = function() {
41673         d3.select(window).on('mousedown.intro', null, true);
41674         d3.select('#curtain').style('pointer-events', 'none');
41675         timeouts.forEach(window.clearTimeout);
41676         context.on('enter.intro', null);
41677         context.on('exit.intro', null);
41678         context.map().on('move.intro', null);
41679         context.history().on('change.intro', null);
41680     };
41681
41682     return d3.rebind(step, event, 'on');
41683 };
41684 iD.ui.intro.navigation = function(context, reveal) {
41685     var event = d3.dispatch('done'),
41686         timeouts = [];
41687
41688     var step = {
41689         title: 'intro.navigation.title'
41690     };
41691
41692     function set(f, t) {
41693         timeouts.push(window.setTimeout(f, t));
41694     }
41695
41696     function eventCancel() {
41697         d3.event.stopPropagation();
41698         d3.event.preventDefault();
41699     }
41700
41701     step.enter = function() {
41702         var rect = context.surfaceRect(),
41703             map = {
41704                 left: rect.left + 10,
41705                 top: rect.top + 70,
41706                 width: rect.width - 70,
41707                 height: rect.height - 170
41708             };
41709
41710         context.map().centerZoom([-85.63591, 41.94285], 19);
41711
41712         reveal(map, t('intro.navigation.drag'));
41713
41714         context.map().on('move.intro', _.debounce(function() {
41715             context.map().on('move.intro', null);
41716             townhall();
41717             context.on('enter.intro', inspectTownHall);
41718         }, 400));
41719
41720         function townhall() {
41721             var hall = [-85.63645945147184, 41.942986488012565];
41722
41723             var point = context.projection(hall);
41724             if (point[0] < 0 || point[0] > rect.width ||
41725                 point[1] < 0 || point[1] > rect.height) {
41726                 context.map().center(hall);
41727             }
41728
41729             var box = iD.ui.intro.pointBox(hall, context);
41730             reveal(box, t('intro.navigation.select'));
41731
41732             context.map().on('move.intro', function() {
41733                 var box = iD.ui.intro.pointBox(hall, context);
41734                 reveal(box, t('intro.navigation.select'), {duration: 0});
41735             });
41736         }
41737
41738         function inspectTownHall(mode) {
41739             if (mode.id !== 'select') return;
41740             context.on('enter.intro', null);
41741             context.map().on('move.intro', null);
41742             set(function() {
41743                 reveal('.entity-editor-pane',
41744                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
41745                 context.on('exit.intro', streetSearch);
41746             }, 700);
41747         }
41748
41749         function streetSearch() {
41750             context.on('exit.intro', null);
41751             reveal('.search-header input',
41752                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
41753             d3.select('.search-header input').on('keyup.intro', searchResult);
41754         }
41755
41756         function searchResult() {
41757             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
41758                 firstName = first.select('.entity-name'),
41759                 name = t('intro.graph.spring_st');
41760
41761             if (!firstName.empty() && firstName.text() === name) {
41762                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
41763                 context.on('exit.intro', selectedStreet);
41764                 d3.select('.search-header input')
41765                     .on('keydown.intro', eventCancel, true)
41766                     .on('keyup.intro', null);
41767             }
41768         }
41769
41770         function selectedStreet() {
41771             var springSt = [-85.63585099140167, 41.942506848938926];
41772             context.map().center(springSt);
41773             context.on('exit.intro', event.done);
41774             set(function() {
41775                 reveal('.entity-editor-pane',
41776                     t('intro.navigation.chosen', {
41777                         name: t('intro.graph.spring_st'),
41778                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
41779                     }));
41780             }, 400);
41781         }
41782     };
41783
41784     step.exit = function() {
41785         timeouts.forEach(window.clearTimeout);
41786         context.map().on('move.intro', null);
41787         context.on('enter.intro', null);
41788         context.on('exit.intro', null);
41789         d3.select('.search-header input')
41790             .on('keydown.intro', null)
41791             .on('keyup.intro', null);
41792     };
41793
41794     return d3.rebind(step, event, 'on');
41795 };
41796 iD.ui.intro.point = function(context, reveal) {
41797     var event = d3.dispatch('done'),
41798         timeouts = [];
41799
41800     var step = {
41801         title: 'intro.points.title'
41802     };
41803
41804     function setTimeout(f, t) {
41805         timeouts.push(window.setTimeout(f, t));
41806     }
41807
41808     function eventCancel() {
41809         d3.event.stopPropagation();
41810         d3.event.preventDefault();
41811     }
41812
41813     step.enter = function() {
41814         context.map().centerZoom([-85.63279, 41.94394], 19);
41815         reveal('button.add-point',
41816             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
41817             { tooltipClass: 'intro-points-add' });
41818
41819         var corner = [-85.632481,41.944094];
41820
41821         context.on('enter.intro', addPoint);
41822
41823         function addPoint(mode) {
41824             if (mode.id !== 'add-point') return;
41825             context.on('enter.intro', enterSelect);
41826
41827             var pointBox = iD.ui.intro.pad(corner, 150, context);
41828             reveal(pointBox, t('intro.points.place'));
41829
41830             context.map().on('move.intro', function() {
41831                 pointBox = iD.ui.intro.pad(corner, 150, context);
41832                 reveal(pointBox, t('intro.points.place'), {duration: 0});
41833             });
41834         }
41835
41836         function enterSelect(mode) {
41837             if (mode.id !== 'select') return;
41838             context.map().on('move.intro', null);
41839             context.on('enter.intro', null);
41840
41841             setTimeout(function() {
41842                 reveal('.preset-search-input',
41843                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
41844                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
41845             }, 500);
41846         }
41847
41848         function keySearch() {
41849             var first = d3.select('.preset-list-item:first-child');
41850             if (first.classed('preset-amenity-cafe')) {
41851                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
41852                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
41853                 d3.select('.preset-search-input')
41854                     .on('keydown.intro', eventCancel, true)
41855                     .on('keyup.intro', null);
41856             }
41857         }
41858
41859         function selectedPreset() {
41860             setTimeout(function() {
41861                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
41862                 context.history().on('change.intro', closeEditor);
41863                 context.on('exit.intro', selectPoint);
41864             }, 400);
41865         }
41866
41867         function closeEditor() {
41868             d3.select('.preset-search-input').on('keydown.intro', null);
41869             context.history().on('change.intro', null);
41870             reveal('.entity-editor-pane',
41871                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41872         }
41873
41874         function selectPoint() {
41875             context.on('exit.intro', null);
41876             context.history().on('change.intro', null);
41877             context.on('enter.intro', enterReselect);
41878
41879             var pointBox = iD.ui.intro.pad(corner, 150, context);
41880             reveal(pointBox, t('intro.points.reselect'));
41881
41882             context.map().on('move.intro', function() {
41883                 pointBox = iD.ui.intro.pad(corner, 150, context);
41884                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
41885             });
41886         }
41887
41888         function enterReselect(mode) {
41889             if (mode.id !== 'select') return;
41890             context.map().on('move.intro', null);
41891             context.on('enter.intro', null);
41892
41893             setTimeout(function() {
41894                 reveal('.entity-editor-pane',
41895                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41896                 context.on('exit.intro', deletePoint);
41897             }, 500);
41898         }
41899
41900         function deletePoint() {
41901             context.on('exit.intro', null);
41902             context.on('enter.intro', enterDelete);
41903
41904             var pointBox = iD.ui.intro.pad(corner, 150, context);
41905             reveal(pointBox, t('intro.points.reselect_delete'));
41906
41907             context.map().on('move.intro', function() {
41908                 pointBox = iD.ui.intro.pad(corner, 150, context);
41909                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
41910             });
41911         }
41912
41913         function enterDelete(mode) {
41914             if (mode.id !== 'select') return;
41915             context.map().on('move.intro', null);
41916             context.on('enter.intro', null);
41917             context.on('exit.intro', deletePoint);
41918             context.map().on('move.intro', deletePoint);
41919             context.history().on('change.intro', deleted);
41920
41921             setTimeout(function() {
41922                 var node = d3.select('.radial-menu-item-delete').node();
41923                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
41924                 reveal(pointBox,
41925                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
41926             }, 300);
41927         }
41928
41929         function deleted(changed) {
41930             if (changed.deleted().length) event.done();
41931         }
41932
41933     };
41934
41935     step.exit = function() {
41936         timeouts.forEach(window.clearTimeout);
41937         context.on('exit.intro', null);
41938         context.on('enter.intro', null);
41939         context.map().on('move.intro', null);
41940         context.history().on('change.intro', null);
41941         d3.select('.preset-search-input')
41942             .on('keyup.intro', null)
41943             .on('keydown.intro', null);
41944     };
41945
41946     return d3.rebind(step, event, 'on');
41947 };
41948 iD.ui.intro.startEditing = function(context, reveal) {
41949     var event = d3.dispatch('done', 'startEditing'),
41950         modal,
41951         timeouts = [];
41952
41953     var step = {
41954         title: 'intro.startediting.title'
41955     };
41956
41957     function timeout(f, t) {
41958         timeouts.push(window.setTimeout(f, t));
41959     }
41960
41961     step.enter = function() {
41962         reveal('.map-control.help-control',
41963             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
41964
41965         timeout(function() {
41966             reveal('#bar button.save', t('intro.startediting.save'));
41967         }, 5000);
41968
41969         timeout(function() {
41970             reveal('#surface');
41971         }, 10000);
41972
41973         timeout(function() {
41974             modal = iD.ui.modal(context.container());
41975
41976             modal.select('.modal')
41977                 .attr('class', 'modal-splash modal col6');
41978
41979             modal.selectAll('.close').remove();
41980
41981             var startbutton = modal.select('.content')
41982                 .attr('class', 'fillL')
41983                     .append('button')
41984                         .attr('class', 'modal-section huge-modal-button')
41985                         .on('click', function() {
41986                             modal.remove();
41987                         });
41988
41989                 startbutton.append('div')
41990                     .attr('class','illustration');
41991                 startbutton.append('h2')
41992                     .text(t('intro.startediting.start'));
41993
41994             event.startEditing();
41995         }, 10500);
41996     };
41997
41998     step.exit = function() {
41999         if (modal) modal.remove();
42000         timeouts.forEach(window.clearTimeout);
42001     };
42002
42003     return d3.rebind(step, event, 'on');
42004 };
42005 iD.presets = function() {
42006
42007     // an iD.presets.Collection with methods for
42008     // loading new data and returning defaults
42009
42010     var all = iD.presets.Collection([]),
42011         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
42012         fields = {},
42013         universal = [],
42014         recent = iD.presets.Collection([]);
42015
42016     // Index of presets by (geometry, tag key).
42017     var index = {
42018         point: {},
42019         vertex: {},
42020         line: {},
42021         area: {},
42022         relation: {}
42023     };
42024
42025     all.match = function(entity, resolver) {
42026         var geometry = entity.geometry(resolver),
42027             geometryMatches = index[geometry],
42028             best = -1,
42029             match;
42030
42031         for (var k in entity.tags) {
42032             var keyMatches = geometryMatches[k];
42033             if (!keyMatches) continue;
42034
42035             for (var i = 0; i < keyMatches.length; i++) {
42036                 var score = keyMatches[i].matchScore(entity);
42037                 if (score > best) {
42038                     best = score;
42039                     match = keyMatches[i];
42040                 }
42041             }
42042         }
42043
42044         return match || all.item(geometry);
42045     };
42046
42047     // Because of the open nature of tagging, iD will never have a complete
42048     // list of tags used in OSM, so we want it to have logic like "assume
42049     // that a closed way with an amenity tag is an area, unless the amenity
42050     // is one of these specific types". This function computes a structure
42051     // that allows testing of such conditions, based on the presets designated
42052     // as as supporting (or not supporting) the area geometry.
42053     //
42054     // The returned object L is a whitelist/blacklist of tags. A closed way
42055     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
42056     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
42057     // and the subkeys form the blacklist.
42058     all.areaKeys = function() {
42059         var areaKeys = {},
42060             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
42061             presets = _.reject(all.collection, 'suggestion');
42062
42063         // whitelist
42064         presets.forEach(function(d) {
42065             for (var key in d.tags) break;
42066             if (!key) return;
42067             if (ignore.indexOf(key) !== -1) return;
42068
42069             if (d.geometry.indexOf('area') !== -1) {
42070                 areaKeys[key] = areaKeys[key] || {};
42071             }
42072         });
42073
42074         // blacklist
42075         presets.forEach(function(d) {
42076             for (var key in d.tags) break;
42077             if (!key) return;
42078             if (ignore.indexOf(key) !== -1) return;
42079
42080             var value = d.tags[key];
42081             if (d.geometry.indexOf('area') === -1 &&
42082                 d.geometry.indexOf('line') !== -1 &&
42083                 key in areaKeys && value !== '*') {
42084                 areaKeys[key][value] = true;
42085             }
42086         });
42087
42088         return areaKeys;
42089     };
42090
42091     all.load = function(d) {
42092
42093         if (d.fields) {
42094             _.forEach(d.fields, function(d, id) {
42095                 fields[id] = iD.presets.Field(id, d);
42096                 if (d.universal) universal.push(fields[id]);
42097             });
42098         }
42099
42100         if (d.presets) {
42101             _.forEach(d.presets, function(d, id) {
42102                 all.collection.push(iD.presets.Preset(id, d, fields));
42103             });
42104         }
42105
42106         if (d.categories) {
42107             _.forEach(d.categories, function(d, id) {
42108                 all.collection.push(iD.presets.Category(id, d, all));
42109             });
42110         }
42111
42112         if (d.defaults) {
42113             var getItem = _.bind(all.item, all);
42114             defaults = {
42115                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
42116                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
42117                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
42118                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
42119                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
42120             };
42121         }
42122
42123         for (var i = 0; i < all.collection.length; i++) {
42124             var preset = all.collection[i],
42125                 geometry = preset.geometry;
42126
42127             for (var j = 0; j < geometry.length; j++) {
42128                 var g = index[geometry[j]];
42129                 for (var k in preset.tags) {
42130                     (g[k] = g[k] || []).push(preset);
42131                 }
42132             }
42133         }
42134
42135         return all;
42136     };
42137
42138     all.field = function(id) {
42139         return fields[id];
42140     };
42141
42142     all.universal = function() {
42143         return universal;
42144     };
42145
42146     all.defaults = function(geometry, n) {
42147         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
42148             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
42149         return iD.presets.Collection(_.uniq(rec.concat(def).concat(all.item(geometry))));
42150     };
42151
42152     all.choose = function(preset) {
42153         if (!preset.isFallback()) {
42154             recent = iD.presets.Collection(_.uniq([preset].concat(recent.collection)));
42155         }
42156         return all;
42157     };
42158
42159     return all;
42160 };
42161 iD.presets.Category = function(id, category, all) {
42162     category = _.clone(category);
42163
42164     category.id = id;
42165
42166     category.members = iD.presets.Collection(category.members.map(function(id) {
42167         return all.item(id);
42168     }));
42169
42170     category.matchGeometry = function(geometry) {
42171         return category.geometry.indexOf(geometry) >= 0;
42172     };
42173
42174     category.matchScore = function() { return -1; };
42175
42176     category.name = function() {
42177         return t('presets.categories.' + id + '.name', {'default': id});
42178     };
42179
42180     category.terms = function() {
42181         return [];
42182     };
42183
42184     return category;
42185 };
42186 iD.presets.Collection = function(collection) {
42187
42188     var maxSearchResults = 50,
42189         maxSuggestionResults = 10;
42190
42191     var presets = {
42192
42193         collection: collection,
42194
42195         item: function(id) {
42196             return _.find(collection, function(d) {
42197                 return d.id === id;
42198             });
42199         },
42200
42201         matchGeometry: function(geometry) {
42202             return iD.presets.Collection(collection.filter(function(d) {
42203                 return d.matchGeometry(geometry);
42204             }));
42205         },
42206
42207         search: function(value, geometry) {
42208             if (!value) return this;
42209
42210             value = value.toLowerCase();
42211
42212             var searchable = _.filter(collection, function(a) {
42213                     return a.searchable !== false && a.suggestion !== true;
42214                 }),
42215                 suggestions = _.filter(collection, function(a) {
42216                     return a.suggestion === true;
42217                 });
42218
42219             function leading(a) {
42220                 var index = a.indexOf(value);
42221                 return index === 0 || a[index - 1] === ' ';
42222             }
42223
42224             // matches value to preset.name
42225             var leading_name = _.filter(searchable, function(a) {
42226                     return leading(a.name().toLowerCase());
42227                 }).sort(function(a, b) {
42228                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
42229                     if (i === 0) return a.name().length - b.name().length;
42230                     else return i;
42231                 });
42232
42233             // matches value to preset.terms values
42234             var leading_terms = _.filter(searchable, function(a) {
42235                     return _.some(a.terms() || [], leading);
42236                 });
42237
42238             // matches value to preset.tags values
42239             var leading_tag_values = _.filter(searchable, function(a) {
42240                     return _.some(_.without(_.values(a.tags || {}), '*'), leading);
42241                 });
42242
42243
42244             // finds close matches to value in preset.name
42245             var levenstein_name = searchable.map(function(a) {
42246                     return {
42247                         preset: a,
42248                         dist: iD.util.editDistance(value, a.name().toLowerCase())
42249                     };
42250                 }).filter(function(a) {
42251                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
42252                 }).sort(function(a, b) {
42253                     return a.dist - b.dist;
42254                 }).map(function(a) {
42255                     return a.preset;
42256                 });
42257
42258             // finds close matches to value in preset.terms
42259             var leventstein_terms = _.filter(searchable, function(a) {
42260                     return _.some(a.terms() || [], function(b) {
42261                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
42262                     });
42263                 });
42264
42265             function suggestionName(name) {
42266                 var nameArray = name.split(' - ');
42267                 if (nameArray.length > 1) {
42268                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
42269                 }
42270                 return name.toLowerCase();
42271             }
42272
42273             var leading_suggestions = _.filter(suggestions, function(a) {
42274                     return leading(suggestionName(a.name()));
42275                 }).sort(function(a, b) {
42276                     a = suggestionName(a.name());
42277                     b = suggestionName(b.name());
42278                     var i = a.indexOf(value) - b.indexOf(value);
42279                     if (i === 0) return a.length - b.length;
42280                     else return i;
42281                 });
42282
42283             var leven_suggestions = suggestions.map(function(a) {
42284                     return {
42285                         preset: a,
42286                         dist: iD.util.editDistance(value, suggestionName(a.name()))
42287                     };
42288                 }).filter(function(a) {
42289                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
42290                 }).sort(function(a, b) {
42291                     return a.dist - b.dist;
42292                 }).map(function(a) {
42293                     return a.preset;
42294                 });
42295
42296             var other = presets.item(geometry);
42297
42298             var results = leading_name.concat(
42299                             leading_terms,
42300                             leading_tag_values,
42301                             leading_suggestions.slice(0, maxSuggestionResults+5),
42302                             levenstein_name,
42303                             leventstein_terms,
42304                             leven_suggestions.slice(0, maxSuggestionResults)
42305                         ).slice(0, maxSearchResults-1);
42306
42307             return iD.presets.Collection(_.uniq(
42308                     results.concat(other)
42309                 ));
42310         }
42311     };
42312
42313     return presets;
42314 };
42315 iD.presets.Field = function(id, field) {
42316     field = _.clone(field);
42317
42318     field.id = id;
42319
42320     field.matchGeometry = function(geometry) {
42321         return !field.geometry || field.geometry === geometry;
42322     };
42323
42324     field.t = function(scope, options) {
42325         return t('presets.fields.' + id + '.' + scope, options);
42326     };
42327
42328     field.label = function() {
42329         return field.t('label', {'default': id});
42330     };
42331
42332     var placeholder = field.placeholder;
42333     field.placeholder = function() {
42334         return field.t('placeholder', {'default': placeholder});
42335     };
42336
42337     return field;
42338 };
42339 iD.presets.Preset = function(id, preset, fields) {
42340     preset = _.clone(preset);
42341
42342     preset.id = id;
42343     preset.fields = (preset.fields || []).map(getFields);
42344     preset.geometry = (preset.geometry || []);
42345
42346     function getFields(f) {
42347         return fields[f];
42348     }
42349
42350     preset.matchGeometry = function(geometry) {
42351         return preset.geometry.indexOf(geometry) >= 0;
42352     };
42353
42354     var matchScore = preset.matchScore || 1;
42355     preset.matchScore = function(entity) {
42356         var tags = preset.tags,
42357             score = 0;
42358
42359         for (var t in tags) {
42360             if (entity.tags[t] === tags[t]) {
42361                 score += matchScore;
42362             } else if (tags[t] === '*' && t in entity.tags) {
42363                 score += matchScore / 2;
42364             } else {
42365                 return -1;
42366             }
42367         }
42368
42369         return score;
42370     };
42371
42372     preset.t = function(scope, options) {
42373         return t('presets.presets.' + id + '.' + scope, options);
42374     };
42375
42376     var name = preset.name;
42377     preset.name = function() {
42378         if (preset.suggestion) {
42379             id = id.split('/');
42380             id = id[0] + '/' + id[1];
42381             return name + ' - ' + t('presets.presets.' + id + '.name');
42382         }
42383         return preset.t('name', {'default': name});
42384     };
42385
42386     preset.terms = function() {
42387         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
42388     };
42389
42390     preset.isFallback = function() {
42391         var tagCount = Object.keys(preset.tags).length;
42392         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
42393     };
42394
42395     preset.reference = function(geometry) {
42396         var key = Object.keys(preset.tags)[0],
42397             value = preset.tags[key];
42398
42399         if (geometry === 'relation' && key === 'type') {
42400             return { rtype: value };
42401         } else if (value === '*') {
42402             return { key: key };
42403         } else {
42404             return { key: key, value: value };
42405         }
42406     };
42407
42408     var removeTags = preset.removeTags || preset.tags;
42409     preset.removeTags = function(tags, geometry) {
42410         tags = _.omit(tags, _.keys(removeTags));
42411
42412         for (var f in preset.fields) {
42413             var field = preset.fields[f];
42414             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
42415                 delete tags[field.key];
42416             }
42417         }
42418
42419         delete tags.area;
42420         return tags;
42421     };
42422
42423     var applyTags = preset.addTags || preset.tags;
42424     preset.applyTags = function(tags, geometry) {
42425         var k;
42426
42427         tags = _.clone(tags);
42428
42429         for (k in applyTags) {
42430             if (applyTags[k] === '*') {
42431                 tags[k] = 'yes';
42432             } else {
42433                 tags[k] = applyTags[k];
42434             }
42435         }
42436
42437         // Add area=yes if necessary.
42438         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
42439         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
42440         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
42441         if (geometry === 'area') {
42442             var needsAreaTag = true;
42443             if (preset.geometry.indexOf('line') === -1) {
42444                 for (k in applyTags) {
42445                     if (k in iD.areaKeys) {
42446                         needsAreaTag = false;
42447                         break;
42448                     }
42449                 }
42450             }
42451             if (needsAreaTag) {
42452                 tags.area = 'yes';
42453             }
42454         }
42455
42456         for (var f in preset.fields) {
42457             var field = preset.fields[f];
42458             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
42459                 tags[field.key] = field.default;
42460             }
42461         }
42462
42463         return tags;
42464     };
42465
42466     return preset;
42467 };
42468 iD.validations = {};
42469 iD.validations.DeprecatedTag = function() {
42470
42471     var validation = function(changes) {
42472         var warnings = [];
42473         for (var i = 0; i < changes.created.length; i++) {
42474             var change = changes.created[i],
42475                 deprecatedTags = change.deprecatedTags();
42476
42477             if (!_.isEmpty(deprecatedTags)) {
42478                 var tags = iD.util.tagText({ tags: deprecatedTags });
42479                 warnings.push({
42480                     id: 'deprecated_tags',
42481                     message: t('validations.deprecated_tags', { tags: tags }),
42482                     entity: change
42483                 });
42484             }
42485         }
42486         return warnings;
42487     };
42488
42489     return validation;
42490 };
42491 iD.validations.ManyDeletions = function() {
42492     var threshold = 100;
42493
42494     var validation = function(changes) {
42495         var warnings = [];
42496         if (changes.deleted.length > threshold) {
42497             warnings.push({
42498                 id: 'many_deletions',
42499                 message: t('validations.many_deletions', { n: changes.deleted.length })
42500             });
42501         }
42502         return warnings;
42503     };
42504
42505     return validation;
42506 };
42507 iD.validations.MissingTag = function() {
42508
42509     // Slightly stricter check than Entity#isUsed (#3091)
42510     function hasTags(entity, graph) {
42511         return _.without(Object.keys(entity.tags), 'area', 'name').length > 0 ||
42512             graph.parentRelations(entity).length > 0;
42513     }
42514
42515     var validation = function(changes, graph) {
42516         var warnings = [];
42517         for (var i = 0; i < changes.created.length; i++) {
42518             var change = changes.created[i],
42519                 geometry = change.geometry(graph);
42520
42521             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !hasTags(change, graph)) {
42522                 warnings.push({
42523                     id: 'missing_tag',
42524                     message: t('validations.untagged_' + geometry),
42525                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
42526                     entity: change
42527                 });
42528             }
42529         }
42530         return warnings;
42531     };
42532
42533     return validation;
42534 };
42535 iD.validations.TagSuggestsArea = function() {
42536
42537     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
42538     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
42539     function tagSuggestsArea(tags) {
42540         if (_.isEmpty(tags)) return false;
42541
42542         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
42543         for (var i = 0; i < presence.length; i++) {
42544             if (tags[presence[i]] !== undefined) {
42545                 return presence[i] + '=' + tags[presence[i]];
42546             }
42547         }
42548
42549         if (tags.building && tags.building === 'yes') return 'building=yes';
42550     }
42551
42552     var validation = function(changes, graph) {
42553         var warnings = [];
42554         for (var i = 0; i < changes.created.length; i++) {
42555             var change = changes.created[i],
42556                 geometry = change.geometry(graph),
42557                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
42558
42559             if (suggestion) {
42560                 warnings.push({
42561                     id: 'tag_suggests_area',
42562                     message: t('validations.tag_suggests_area', { tag: suggestion }),
42563                     entity: change
42564                 });
42565             }
42566         }
42567         return warnings;
42568     };
42569
42570     return validation;
42571 };
42572 })();
42573 window.locale = { _current: 'en' };
42574
42575 locale.current = function(_) {
42576     if (!arguments.length) return locale._current;
42577     if (locale[_] !== undefined) locale._current = _;
42578     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
42579     return locale;
42580 };
42581
42582 function t(s, o, loc) {
42583     loc = loc || locale._current;
42584
42585     var path = s.split(".").reverse(),
42586         rep = locale[loc];
42587
42588     while (rep !== undefined && path.length) rep = rep[path.pop()];
42589
42590     if (rep !== undefined) {
42591         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
42592         return rep;
42593     }
42594
42595     if (loc !== 'en') {
42596         return t(s, o, 'en');
42597     }
42598
42599     if (o && 'default' in o) {
42600         return o['default'];
42601     }
42602
42603     var missing = 'Missing ' + loc + ' translation: ' + s;
42604     if (typeof console !== "undefined") console.error(missing);
42605
42606     return missing;
42607 }
42608 iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]},"n185964961":{"id":"n185964961","loc":[-85.6406588,41.942601]},"n185964962":{"id":"n185964962","loc":[-85.6394548,41.94261]},"n185970607":{"id":"n185970607","loc":[-85.641094,41.94006]},"n185970614":{"id":"n185970614","loc":[-85.641825,41.941316]},"n185970616":{"id":"n185970616","loc":[-85.641838,41.941556]},"n185973650":{"id":"n185973650","loc":[-85.639918,41.940064]},"n185973660":{"id":"n185973660","loc":[-85.640645,41.941339]},"n185973659":{"id":"n185973659","loc":[-85.6406115,41.9400658]},"n185974479":{"id":"n185974479","loc":[-85.639402,41.941344]},"n185974481":{"id":"n185974481","loc":[-85.643071,41.941288]},"n185976259":{"id":"n185976259","loc":[-85.642213,41.940043]},"n185976261":{"id":"n185976261","loc":[-85.643056,41.94001]},"n185964959":{"id":"n185964959","loc":[-85.6431031,41.9425754]},"n185964960":{"id":"n185964960","loc":[-85.6418749,41.9425864]},"n185981481":{"id":"n185981481","loc":[-85.6386827,41.9400828]},"n185981482":{"id":"n185981482","loc":[-85.6393664,41.9400854]},"n2138493844":{"id":"n2138493844","loc":[-85.6427969,41.940522]},"n2138493845":{"id":"n2138493845","loc":[-85.6425891,41.9405228]},"n2138493846":{"id":"n2138493846","loc":[-85.6425868,41.9402875]},"n2138493847":{"id":"n2138493847","loc":[-85.6427969,41.9402858]},"n2138493848":{"id":"n2138493848","loc":[-85.6425708,41.9405234]},"n2138493849":{"id":"n2138493849","loc":[-85.642568,41.9402855]},"n2138493850":{"id":"n2138493850","loc":[-85.6423157,41.9402886]},"n2138493851":{"id":"n2138493851","loc":[-85.6423212,41.9404362]},"n2138493852":{"id":"n2138493852","loc":[-85.6422923,41.9404578]},"n2138493853":{"id":"n2138493853","loc":[-85.6422868,41.9404834]},"n2138493854":{"id":"n2138493854","loc":[-85.6423226,41.9405091]},"n2138493855":{"id":"n2138493855","loc":[-85.6423847,41.9405111]},"n2138493856":{"id":"n2138493856","loc":[-85.6424081,41.9405265]},"n2140155811":{"id":"n2140155811","loc":[-85.6419547,41.9410956]},"n2140155814":{"id":"n2140155814","loc":[-85.6427577,41.9410884]},"n2140155816":{"id":"n2140155816","loc":[-85.6427545,41.9410052]},"n2140155818":{"id":"n2140155818","loc":[-85.6428057,41.9410028]},"n2140155821":{"id":"n2140155821","loc":[-85.6427993,41.9407339]},"n2140155823":{"id":"n2140155823","loc":[-85.6427385,41.9407339]},"n2140155825":{"id":"n2140155825","loc":[-85.6427417,41.9406435]},"n2140155827":{"id":"n2140155827","loc":[-85.6419515,41.9406482]},"n2140155828":{"id":"n2140155828","loc":[-85.6429368,41.9412407]},"n2140155829":{"id":"n2140155829","loc":[-85.6417756,41.9412526]},"n2140155830":{"id":"n2140155830","loc":[-85.641766,41.9405983]},"n2140155831":{"id":"n2140155831","loc":[-85.6419803,41.9405983]},"n2140155832":{"id":"n2140155832","loc":[-85.6419611,41.9401366]},"n2140155833":{"id":"n2140155833","loc":[-85.6429336,41.94012]},"n2140155834":{"id":"n2140155834","loc":[-85.6430697,41.9411732]},"n2140155835":{"id":"n2140155835","loc":[-85.6428411,41.9409974]},"n2140155837":{"id":"n2140155837","loc":[-85.6428388,41.9407211]},"n2140155839":{"id":"n2140155839","loc":[-85.6430624,41.9405521]},"n2140155840":{"id":"n2140155840","loc":[-85.6427323,41.9412396]},"n2140155842":{"id":"n2140155842","loc":[-85.6418147,41.9412457]},"n2140155844":{"id":"n2140155844","loc":[-85.641813,41.9411319]},"n2140155845":{"id":"n2140155845","loc":[-85.6418394,41.9411111]},"n2140155847":{"id":"n2140155847","loc":[-85.6418838,41.9410977]},"n2140155849":{"id":"n2140155849","loc":[-85.6427324,41.9410921]},"n2140155851":{"id":"n2140155851","loc":[-85.6427798,41.9412945]},"n2140155852":{"id":"n2140155852","loc":[-85.6427701,41.9411777]},"n2140155854":{"id":"n2140155854","loc":[-85.6427323,41.9411572]},"n2140155856":{"id":"n2140155856","loc":[-85.6418478,41.9411666]},"n2165942818":{"id":"n2165942818","loc":[-85.6437533,41.9415029]},"n2165942819":{"id":"n2165942819","loc":[-85.6437623,41.9421195]},"n2168510551":{"id":"n2168510551","loc":[-85.6423795,41.9422615]},"n2168510552":{"id":"n2168510552","loc":[-85.6423744,41.9419439]},"n2168510553":{"id":"n2168510553","loc":[-85.642518,41.9419427]},"n2168510554":{"id":"n2168510554","loc":[-85.6425186,41.9419801]},"n2168510555":{"id":"n2168510555","loc":[-85.6428314,41.9419773]},"n2168510556":{"id":"n2168510556","loc":[-85.6428368,41.9423116]},"n2168510557":{"id":"n2168510557","loc":[-85.6424947,41.9423146]},"n2168510558":{"id":"n2168510558","loc":[-85.6424938,41.9422605]},"n2189046007":{"id":"n2189046007","loc":[-85.6410866,41.9424327]},"n2189046009":{"id":"n2189046009","loc":[-85.6410805,41.9420061]},"n2189046011":{"id":"n2189046011","loc":[-85.6412443,41.9420048]},"n2189046012":{"id":"n2189046012","loc":[-85.6412505,41.9424314]},"n2189046014":{"id":"n2189046014","loc":[-85.6413311,41.942968]},"n2189046016":{"id":"n2189046016","loc":[-85.6413281,41.942713]},"n2189046018":{"id":"n2189046018","loc":[-85.641521,41.9427117]},"n2189046021":{"id":"n2189046021","loc":[-85.6415234,41.9429236]},"n2189046022":{"id":"n2189046022","loc":[-85.6415045,41.9429238]},"n2189046025":{"id":"n2189046025","loc":[-85.641505,41.9429668]},"n2189046053":{"id":"n2189046053","loc":[-85.6385988,41.942412]},"n2189046054":{"id":"n2189046054","loc":[-85.6385985,41.9423311]},"n2189046055":{"id":"n2189046055","loc":[-85.6387617,41.9423308]},"n2189046056":{"id":"n2189046056","loc":[-85.6387616,41.9423026]},"n2189046058":{"id":"n2189046058","loc":[-85.6388215,41.9423025]},"n2189046059":{"id":"n2189046059","loc":[-85.6388219,41.9424115]},"n2189046060":{"id":"n2189046060","loc":[-85.6391096,41.9424486]},"n2189046061":{"id":"n2189046061","loc":[-85.6391105,41.9423673]},"n2189046063":{"id":"n2189046063","loc":[-85.6392911,41.9423684]},"n2189046065":{"id":"n2189046065","loc":[-85.6392903,41.9424497]},"n2189046067":{"id":"n2189046067","loc":[-85.6397927,41.9423876]},"n2189046069":{"id":"n2189046069","loc":[-85.6397897,41.9422981]},"n2189046070":{"id":"n2189046070","loc":[-85.6399702,41.9422947]},"n2189046072":{"id":"n2189046072","loc":[-85.6399732,41.9423843]},"n2189046074":{"id":"n2189046074","loc":[-85.6396331,41.9430227]},"n2189046075":{"id":"n2189046075","loc":[-85.6398673,41.9430189]},"n2189046077":{"id":"n2189046077","loc":[-85.6398656,41.9429637]},"n2189046079":{"id":"n2189046079","loc":[-85.6398885,41.9429633]},"n2189046082":{"id":"n2189046082","loc":[-85.6398832,41.942779]},"n2189046083":{"id":"n2189046083","loc":[-85.6398513,41.9427796]},"n2189046085":{"id":"n2189046085","loc":[-85.6398502,41.9427401]},"n2189046087":{"id":"n2189046087","loc":[-85.6397889,41.9427411]},"n2189046089":{"id":"n2189046089","loc":[-85.6397892,41.942753]},"n2189046090":{"id":"n2189046090","loc":[-85.6396983,41.9427544]},"n2189046092":{"id":"n2189046092","loc":[-85.6396993,41.9427882]},"n2189046094":{"id":"n2189046094","loc":[-85.6396746,41.9427886]},"n2189046096":{"id":"n2189046096","loc":[-85.6396758,41.9428296]},"n2189046097":{"id":"n2189046097","loc":[-85.6397007,41.9428292]},"n2189046099":{"id":"n2189046099","loc":[-85.6397018,41.9428686]},"n2189046103":{"id":"n2189046103","loc":[-85.6396289,41.9428697]},"n2189046112":{"id":"n2189046112","loc":[-85.6435683,41.9429457]},"n2189046113":{"id":"n2189046113","loc":[-85.643568,41.9427766]},"n2189046115":{"id":"n2189046115","loc":[-85.6434011,41.9427767]},"n2189046116":{"id":"n2189046116","loc":[-85.6434012,41.9428631]},"n2189046117":{"id":"n2189046117","loc":[-85.643448,41.9428631]},"n2189046118":{"id":"n2189046118","loc":[-85.6434481,41.9429457]},"n2189046119":{"id":"n2189046119","loc":[-85.6428363,41.9429809]},"n2189046120":{"id":"n2189046120","loc":[-85.6429171,41.9429791]},"n2189046121":{"id":"n2189046121","loc":[-85.642914,41.9429041]},"n2189046122":{"id":"n2189046122","loc":[-85.6429385,41.9429035]},"n2189046123":{"id":"n2189046123","loc":[-85.6429348,41.9428126]},"n2189046124":{"id":"n2189046124","loc":[-85.6427746,41.9428163]},"n2189046125":{"id":"n2189046125","loc":[-85.6427783,41.942906]},"n2189046126":{"id":"n2189046126","loc":[-85.6428332,41.9429047]},"n2189046127":{"id":"n2189046127","loc":[-85.6423018,41.9428859]},"n2189046128":{"id":"n2189046128","loc":[-85.6422987,41.9427208]},"n2189046130":{"id":"n2189046130","loc":[-85.6424218,41.9427195]},"n2189046131":{"id":"n2189046131","loc":[-85.6424246,41.9428684]},"n2189046132":{"id":"n2189046132","loc":[-85.6423845,41.9428689]},"n2189046133":{"id":"n2189046133","loc":[-85.6423848,41.942885]},"n2189046134":{"id":"n2189046134","loc":[-85.641533,41.9429392]},"n2189046135":{"id":"n2189046135","loc":[-85.6416096,41.9428768]},"n2189046137":{"id":"n2189046137","loc":[-85.6416763,41.9429221]},"n2189046138":{"id":"n2189046138","loc":[-85.6415997,41.9429845]},"n2189046139":{"id":"n2189046139","loc":[-85.6420598,41.9428016]},"n2189046140":{"id":"n2189046140","loc":[-85.6420593,41.9427415]},"n2189046141":{"id":"n2189046141","loc":[-85.6421957,41.9427409]},"n2189046142":{"id":"n2189046142","loc":[-85.6421963,41.9428182]},"n2189046143":{"id":"n2189046143","loc":[-85.6421281,41.9428185]},"n2189046144":{"id":"n2189046144","loc":[-85.6421279,41.9428013]},"n2189046145":{"id":"n2189046145","loc":[-85.6409429,41.9429345]},"n2189046146":{"id":"n2189046146","loc":[-85.6410354,41.9429334]},"n2189046147":{"id":"n2189046147","loc":[-85.6410325,41.9427972]},"n2189046148":{"id":"n2189046148","loc":[-85.640997,41.9427976]},"n2189046149":{"id":"n2189046149","loc":[-85.6409963,41.9427643]},"n2189046150":{"id":"n2189046150","loc":[-85.6408605,41.9427659]},"n2189046152":{"id":"n2189046152","loc":[-85.6408623,41.9428482]},"n2189046153":{"id":"n2189046153","loc":[-85.640941,41.9428473]},"n2189152992":{"id":"n2189152992","loc":[-85.6437661,41.9422257]},"n2189152993":{"id":"n2189152993","loc":[-85.643768,41.9424067]},"n2189152994":{"id":"n2189152994","loc":[-85.6432176,41.9417705]},"n2189152995":{"id":"n2189152995","loc":[-85.6432097,41.941327]},"n2189152996":{"id":"n2189152996","loc":[-85.6436493,41.9413226]},"n2189152997":{"id":"n2189152997","loc":[-85.6436563,41.9417164]},"n2189152998":{"id":"n2189152998","loc":[-85.6435796,41.9417171]},"n2189152999":{"id":"n2189152999","loc":[-85.6435805,41.9417669]},"n2189153000":{"id":"n2189153000","loc":[-85.6438202,41.9414953]},"n2189153001":{"id":"n2189153001","loc":[-85.6438173,41.9413175]},"n2189153004":{"id":"n2189153004","loc":[-85.6432535,41.9418466]},"n2189153005":{"id":"n2189153005","loc":[-85.6433935,41.9418599]},"n2189153006":{"id":"n2189153006","loc":[-85.6434831,41.9418986]},"n2189153007":{"id":"n2189153007","loc":[-85.6435678,41.9419774]},"n2189153008":{"id":"n2189153008","loc":[-85.6435987,41.9420282]},"n2189153009":{"id":"n2189153009","loc":[-85.643438,41.9419573]},"n2189153010":{"id":"n2189153010","loc":[-85.6435284,41.9424676]},"n2189153011":{"id":"n2189153011","loc":[-85.6436207,41.9423631]},"n2189153012":{"id":"n2189153012","loc":[-85.6434957,41.9422973]},"n2189153013":{"id":"n2189153013","loc":[-85.6434457,41.9422458]},"n2189153014":{"id":"n2189153014","loc":[-85.6433976,41.9421772]},"n2189153015":{"id":"n2189153015","loc":[-85.6433861,41.9420785]},"n2189153016":{"id":"n2189153016","loc":[-85.6433765,41.9420313]},"n2189153017":{"id":"n2189153017","loc":[-85.6432207,41.9420284]},"n2189153018":{"id":"n2189153018","loc":[-85.6432245,41.9422759]},"n2189153019":{"id":"n2189153019","loc":[-85.6432649,41.9423474]},"n2189153020":{"id":"n2189153020","loc":[-85.6433226,41.9424132]},"n2189153021":{"id":"n2189153021","loc":[-85.6434111,41.9424704]},"n2189153022":{"id":"n2189153022","loc":[-85.6434591,41.9424347]},"n2189153025":{"id":"n2189153025","loc":[-85.6437669,41.9423073]},"n2189153026":{"id":"n2189153026","loc":[-85.6436611,41.942293]},"n2189153027":{"id":"n2189153027","loc":[-85.6435784,41.9422473]},"n2189153028":{"id":"n2189153028","loc":[-85.6435245,41.9421443]},"n2189153029":{"id":"n2189153029","loc":[-85.6435149,41.9420613]},"n2189153030":{"id":"n2189153030","loc":[-85.6433528,41.9419269]},"n2189153031":{"id":"n2189153031","loc":[-85.6432535,41.9419191]},"n2189153032":{"id":"n2189153032","loc":[-85.6430868,41.9419198]},"n2189153033":{"id":"n2189153033","loc":[-85.6434894,41.9420033]},"n2189153034":{"id":"n2189153034","loc":[-85.6432974,41.9419225]},"n2189153035":{"id":"n2189153035","loc":[-85.6433055,41.9421632]},"n2189153036":{"id":"n2189153036","loc":[-85.6433538,41.9422849]},"n2189153037":{"id":"n2189153037","loc":[-85.6434718,41.9423887]},"n2189153038":{"id":"n2189153038","loc":[-85.6436134,41.9422667]},"n2189153040":{"id":"n2189153040","loc":[-85.6438759,41.9414017]},"n2189153041":{"id":"n2189153041","loc":[-85.6438181,41.9413687]},"n2189153042":{"id":"n2189153042","loc":[-85.6436821,41.9413044]},"n2189153043":{"id":"n2189153043","loc":[-85.6435899,41.9412862]},"n2189153044":{"id":"n2189153044","loc":[-85.6433169,41.9417268]},"n2189153045":{"id":"n2189153045","loc":[-85.643301,41.9412859]},"n2189153046":{"id":"n2189153046","loc":[-85.6435531,41.9416981]},"n2189153047":{"id":"n2189153047","loc":[-85.6435427,41.9412863]},"n185948706":{"id":"n185948706","loc":[-85.6369439,41.940122]},"n185949348":{"id":"n185949348","loc":[-85.640039,41.931135]},"n185949870":{"id":"n185949870","loc":[-85.643195,41.949261]},"n185954680":{"id":"n185954680","loc":[-85.6337802,41.9401143]},"n185954784":{"id":"n185954784","loc":[-85.6487485,41.942527]},"n185958670":{"id":"n185958670","loc":[-85.637255,41.940104]},"n185958672":{"id":"n185958672","loc":[-85.636996,41.941355]},"n185960207":{"id":"n185960207","loc":[-85.634992,41.940118]},"n185963163":{"id":"n185963163","loc":[-85.638831,41.93398]},"n185963165":{"id":"n185963165","loc":[-85.640073,41.933968]},"n185963167":{"id":"n185963167","loc":[-85.641225,41.933972]},"n185963168":{"id":"n185963168","loc":[-85.642386,41.933952]},"n185964695":{"id":"n185964695","loc":[-85.6443608,41.9425645]},"n185964697":{"id":"n185964697","loc":[-85.644384,41.939941]},"n185964963":{"id":"n185964963","loc":[-85.6382347,41.9426146]},"n185964965":{"id":"n185964965","loc":[-85.637022,41.942622]},"n185964967":{"id":"n185964967","loc":[-85.6363706,41.9426606]},"n185964968":{"id":"n185964968","loc":[-85.6357988,41.9427748]},"n185964969":{"id":"n185964969","loc":[-85.6355409,41.9428465]},"n185964970":{"id":"n185964970","loc":[-85.6348729,41.9430443]},"n185966958":{"id":"n185966958","loc":[-85.641946,41.946413]},"n185966960":{"id":"n185966960","loc":[-85.643148,41.946389]},"n185967774":{"id":"n185967774","loc":[-85.641889,41.943852]},"n185967775":{"id":"n185967775","loc":[-85.641922,41.945121]},"n185967776":{"id":"n185967776","loc":[-85.641927,41.947544]},"n185967777":{"id":"n185967777","loc":[-85.641982,41.947622]},"n185969289":{"id":"n185969289","loc":[-85.63928,41.929221]},"n185969704":{"id":"n185969704","loc":[-85.6388186,41.9350099]},"n185969706":{"id":"n185969706","loc":[-85.6400709,41.9349957]},"n185969708":{"id":"n185969708","loc":[-85.6412214,41.9349827]},"n185969710":{"id":"n185969710","loc":[-85.6423509,41.934974]},"n185970602":{"id":"n185970602","loc":[-85.641293,41.931817]},"n185970604":{"id":"n185970604","loc":[-85.641258,41.932705]},"n185970605":{"id":"n185970605","loc":[-85.641148,41.936984]},"n185970606":{"id":"n185970606","loc":[-85.641112,41.938169]},"n185970906":{"id":"n185970906","loc":[-85.639454,41.943871]},"n185970908":{"id":"n185970908","loc":[-85.6394635,41.9450504]},"n185970909":{"id":"n185970909","loc":[-85.6394914,41.9451911]},"n185971368":{"id":"n185971368","loc":[-85.635769,41.940122]},"n185971978":{"id":"n185971978","loc":[-85.640003,41.936988]},"n185971980":{"id":"n185971980","loc":[-85.642299,41.936988]},"n185973633":{"id":"n185973633","loc":[-85.639023,41.92861]},"n185973635":{"id":"n185973635","loc":[-85.639153,41.928969]},"n185973637":{"id":"n185973637","loc":[-85.639213,41.929088]},"n185973639":{"id":"n185973639","loc":[-85.63935,41.929396]},"n185973641":{"id":"n185973641","loc":[-85.640143,41.931462]},"n185973644":{"id":"n185973644","loc":[-85.64019,41.931788]},"n185973646":{"id":"n185973646","loc":[-85.6401365,41.9327199]},"n185973648":{"id":"n185973648","loc":[-85.639983,41.938174]},"n185974477":{"id":"n185974477","loc":[-85.638206,41.941331]},"n185975928":{"id":"n185975928","loc":[-85.640683,41.94513]},"n185975930":{"id":"n185975930","loc":[-85.643102,41.945103]},"n185976255":{"id":"n185976255","loc":[-85.642424,41.931817]},"n185976257":{"id":"n185976257","loc":[-85.64242,41.932699]},"n185976258":{"id":"n185976258","loc":[-85.6422621,41.9381489]},"n185977452":{"id":"n185977452","loc":[-85.6457497,41.9398834]},"n185978772":{"id":"n185978772","loc":[-85.646656,41.939869]},"n185981472":{"id":"n185981472","loc":[-85.6388962,41.9321266]},"n185981474":{"id":"n185981474","loc":[-85.6388769,41.9327334]},"n185981476":{"id":"n185981476","loc":[-85.638829,41.934116]},"n185981478":{"id":"n185981478","loc":[-85.63876,41.937002]},"n185981480":{"id":"n185981480","loc":[-85.638682,41.93819]},"n185981999":{"id":"n185981999","loc":[-85.638194,41.9400866]},"n185982001":{"id":"n185982001","loc":[-85.646302,41.93988]},"n185982877":{"id":"n185982877","loc":[-85.640676,41.943867]},"n185982879":{"id":"n185982879","loc":[-85.640734,41.945887]},"n185985823":{"id":"n185985823","loc":[-85.643106,41.943841]},"n185985824":{"id":"n185985824","loc":[-85.643145,41.947641]},"n185985825":{"id":"n185985825","loc":[-85.643219,41.950829]},"n1475301385":{"id":"n1475301385","loc":[-85.6360612,41.9427042]},"n1475301397":{"id":"n1475301397","loc":[-85.6366651,41.9426328]},"n2139795811":{"id":"n2139795811","loc":[-85.6469154,41.9425427]},"n2139795830":{"id":"n2139795830","loc":[-85.6443194,41.9399444]},"n2139795834":{"id":"n2139795834","loc":[-85.6453506,41.9399002]},"n2139795837":{"id":"n2139795837","loc":[-85.645806,41.9398831]},"n2139858932":{"id":"n2139858932","loc":[-85.6351721,41.9429557]},"n2140019000":{"id":"n2140019000","loc":[-85.6359935,41.9427224]},"n2165942817":{"id":"n2165942817","loc":[-85.6442017,41.9414993]},"n2165942820":{"id":"n2165942820","loc":[-85.6442107,41.9421159]},"n2189152990":{"id":"n2189152990","loc":[-85.6442328,41.942404]},"n2189152991":{"id":"n2189152991","loc":[-85.6442309,41.9422229]},"n2189153002":{"id":"n2189153002","loc":[-85.6441329,41.9413147]},"n2189153003":{"id":"n2189153003","loc":[-85.6441357,41.9414925]},"n2189153023":{"id":"n2189153023","loc":[-85.6443453,41.9423074]},"n2189153024":{"id":"n2189153024","loc":[-85.6442318,41.9423045]},"n2189153039":{"id":"n2189153039","loc":[-85.6441343,41.9414025]},"w208643102":{"id":"w208643102","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153034","n2189153035","n2189153036","n2189153037","n2189153038"]},"w17966942":{"id":"w17966942","tags":{"highway":"residential","name":"Millard St"},"nodes":["n185954680","n185960207","n185971368","n185948706","n185958670","n185981999","n185981481","n185981482","n185973650","n185973659","n185970607","n185976259","n185976261","n2139795830","n185964697","n2139795834","n185977452","n2139795837","n185982001","n185978772"]},"w208643105":{"id":"w208643105","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153046","n2189153047"]},"w208631637":{"id":"w208631637","tags":{"area":"yes","building":"yes"},"nodes":["n2189046014","n2189046016","n2189046018","n2189046021","n2189046022","n2189046025","n2189046014"]},"w208643096":{"id":"w208643096","tags":{"amenity":"parking","area":"yes","fee":"no"},"nodes":["n2189152990","n2189153024","n2189152991","n2189152992","n2189153025","n2189152993","n2189152990"]},"w208631656":{"id":"w208631656","tags":{"area":"yes","building":"yes"},"nodes":["n2189046134","n2189046135","n2189046137","n2189046138","n2189046134"]},"w204003417":{"id":"w204003417","tags":{"area":"yes","building":"school"},"nodes":["n2140155811","n2140155814","n2140155816","n2140155818","n2140155821","n2140155823","n2140155825","n2140155827","n2140155811"]},"w208631654":{"id":"w208631654","tags":{"area":"yes","building":"yes"},"nodes":["n2189046127","n2189046128","n2189046130","n2189046131","n2189046132","n2189046133","n2189046127"]},"w17966327":{"id":"w17966327","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976261","n2140155839","n2140155834","n185974481","n2189153032","n185964959"]},"w41785752":{"id":"w41785752","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60","access":"yes"},"nodes":["n185954784","n2139795811","n185964695","n185964959","n185964960","n185964961","n185964962","n185964963","n185964965","n1475301397","n185964967","n1475301385","n2140019000","n185964968","n185964969","n2139858932","n185964970"]},"w203841842":{"id":"w203841842","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493848","n2138493849","n2138493850","n2138493851","n2138493852","n2138493853","n2138493854","n2138493855","n2138493856","n2138493848"]},"w208643103":{"id":"w208643103","tags":{"highway":"service"},"nodes":["n2189153039","n2189153040","n2189153041","n2189153042","n2189153043","n2189153047","n2189153045","n185974481"]},"w208643098":{"id":"w208643098","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153000","n2189153041","n2189153001","n2189153002","n2189153039","n2189153003","n2189153000"]},"w208631646":{"id":"w208631646","tags":{"area":"yes","building":"yes"},"nodes":["n2189046067","n2189046069","n2189046070","n2189046072","n2189046067"]},"w208631653":{"id":"w208631653","tags":{"area":"yes","building":"yes"},"nodes":["n2189046119","n2189046120","n2189046121","n2189046122","n2189046123","n2189046124","n2189046125","n2189046126","n2189046119"]},"w17966041":{"id":"w17966041","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973659","n185973660","n185964961"]},"w208631645":{"id":"w208631645","tags":{"area":"yes","building":"yes"},"nodes":["n2189046060","n2189046061","n2189046063","n2189046065","n2189046060"]},"w206803397":{"id":"w206803397","tags":{"area":"yes","building":"yes"},"nodes":["n2168510551","n2168510552","n2168510553","n2168510554","n2168510555","n2168510556","n2168510557","n2168510558","n2168510551"]},"w17965792":{"id":"w17965792","tags":{"highway":"residential","name":"N Hooker Ave"},"nodes":["n185964962","n185970906","n185970908","n185970909"]},"w208631651":{"id":"w208631651","tags":{"area":"yes","building":"yes"},"nodes":["n2189046112","n2189046113","n2189046115","n2189046116","n2189046117","n2189046118","n2189046112"]},"w208631643":{"id":"w208631643","tags":{"area":"yes","building":"yes"},"nodes":["n2189046053","n2189046054","n2189046055","n2189046056","n2189046058","n2189046059","n2189046053"]},"w17966878":{"id":"w17966878","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981472","n185981474","n185963163","n185981476","n185969704","n185981478","n185981480","n185981481"]},"w17966102":{"id":"w17966102","tags":{"highway":"residential","name":"South St"},"nodes":["n185958672","n185974477","n185974479","n185973660","n185970614"]},"w208631660":{"id":"w208631660","tags":{"area":"yes","building":"yes"},"nodes":["n2189046145","n2189046146","n2189046147","n2189046148","n2189046149","n2189046150","n2189046152","n2189046153","n2189046145"]},"w208643101":{"id":"w208643101","tags":{"highway":"service"},"nodes":["n2189153023","n2189153024","n2189153025","n2189153026","n2189153038","n2189153027","n2189153028","n2189153029","n2189153033","n2189153009","n2189153030","n2189153034","n2189153031","n2189153032"]},"w204000205":{"id":"w204000205","tags":{"highway":"residential","name":"South St","oneway":"yes"},"nodes":["n185974481","n2140155851","n185970614"]},"w203841841":{"id":"w203841841","tags":{"area":"yes","leisure":"pitch","pitch":"basketball"},"nodes":["n2138493844","n2138493845","n2138493846","n2138493847","n2138493844"]},"w17965444":{"id":"w17965444","tags":{"highway":"residential","name":"N Grant Ave"},"nodes":["n185964960","n185967774","n185967775","n185966958","n185967776","n185967777"]},"w208631648":{"id":"w208631648","tags":{"area":"yes","building":"yes"},"nodes":["n2189046074","n2189046075","n2189046077","n2189046079","n2189046082","n2189046083","n2189046085","n2189046087","n2189046089","n2189046090","n2189046092","n2189046094","n2189046096","n2189046097","n2189046099","n2189046103","n2189046074"]},"w208643100":{"id":"w208643100","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153010","n2189153011","n2189153012","n2189153013","n2189153014","n2189153015","n2189153016","n2189153017","n2189153018","n2189153019","n2189153020","n2189153021","n2189153022","n2189153010"]},"w17965749":{"id":"w17965749","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970614","n185970616","n185964960"]},"w206574482":{"id":"w206574482","tags":{"amenity":"library","area":"yes","building":"yes","name":"Three Rivers Public Library"},"nodes":["n2165942817","n2165942818","n2165942819","n2165942820","n2165942817"]},"w208643097":{"id":"w208643097","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189152994","n2189152995","n2189152996","n2189152997","n2189152998","n2189152999","n2189152994"]},"w17966879":{"id":"w17966879","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981482","n185974479","n185964962"]},"w17966325":{"id":"w17966325","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976255","n185976257","n185963168","n185969710","n185971980","n185976258","n185954700","n185976259"]},"w17967390":{"id":"w17967390","tags":{"highway":"residential","name":"N Douglas Ave"},"nodes":["n185964959","n185985823","n185975930","n185966960","n185985824","n185949870","n185985825"]},"w208631635":{"id":"w208631635","tags":{"area":"yes","building":"yes"},"nodes":["n2189046007","n2189046009","n2189046011","n2189046012","n2189046007"]},"w208643099":{"id":"w208643099","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153031","n2189153004","n2189153005","n2189153006","n2189153007","n2189153008","n2189153029","n2189153033","n2189153009","n2189153030","n2189153031"]},"w208631658":{"id":"w208631658","tags":{"area":"yes","building":"yes"},"nodes":["n2189046139","n2189046140","n2189046141","n2189046142","n2189046143","n2189046144","n2189046139"]},"w208643104":{"id":"w208643104","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153044","n2189153045"]},"w17966039":{"id":"w17966039","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973633","n185973635","n185973637","n185969289","n185973639","n185949348","n185973641","n185973644","n185973646","n185963165","n185969706","n185971978","n185973648","n185973650"]},"w204003420":{"id":"w204003420","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140155840","n2140155842","n2140155844","n2140155845","n2140155847","n2140155849","n2140155854","n2140155840"]},"w204003419":{"id":"w204003419","tags":{"highway":"service"},"nodes":["n2140155834","n2140155835","n2140155837","n2140155839"]},"w204003418":{"id":"w204003418","tags":{"amenity":"school","area":"yes","name":"Andrews Elementary School"},"nodes":["n2140155828","n2140155829","n2140155830","n2140155831","n2140155832","n2140155833","n2140155828"]},"w17965747":{"id":"w17965747","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970602","n185970604","n185963167","n185969708","n185970605","n185970606","n185970607"]},"w17967073":{"id":"w17967073","tags":{"highway":"residential","name":"N Lincoln Ave"},"nodes":["n185964961","n185982877","n185975928","n185982879"]},"w204003421":{"id":"w204003421","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2140155851","n2140155852","n2140155854","n2140155856"]},"r1943857":{"id":"r1943857","tags":{"modifier":"Business","name":"US 131 Business (Three Rivers, MI)","network":"US:US","ref":"131","route":"road","type":"route"},"members":[{"id":"w17966509","type":"way","role":"forward"},{"id":"w143497377","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w41785752","type":"way","role":""},{"id":"w17965146","type":"way","role":"forward"},{"id":"w17964031","type":"way","role":"forward"}]},"r270277":{"id":"r270277","tags":{"network":"US:MI","ref":"60","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-60_%28Michigan_highway%29"},"members":[{"id":"w17751087","type":"way","role":"east"},{"id":"w117148312","type":"way","role":"east"},{"id":"w40942155","type":"way","role":"west"},{"id":"w17751017","type":"way","role":""},{"id":"w17751083","type":"way","role":""},{"id":"w17747780","type":"way","role":""},{"id":"w41068082","type":"way","role":""},{"id":"w197025212","type":"way","role":""},{"id":"w17743874","type":"way","role":""},{"id":"w17751044","type":"way","role":""},{"id":"w17752167","type":"way","role":""},{"id":"w17751089","type":"way","role":""},{"id":"w17743879","type":"way","role":""},{"id":"w17751064","type":"way","role":""},{"id":"w197057073","type":"way","role":""},{"id":"w167699963","type":"way","role":""},{"id":"w167699972","type":"way","role":""},{"id":"w17967584","type":"way","role":""},{"id":"w167699964","type":"way","role":""},{"id":"w17967582","type":"way","role":"west"},{"id":"w41260270","type":"way","role":"west"},{"id":"w17965146","type":"way","role":"west"},{"id":"w41785752","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150836","type":"way","role":""},{"id":"w134150802","type":"way","role":""},{"id":"w41074896","type":"way","role":""},{"id":"w17966773","type":"way","role":""},{"id":"w17967415","type":"way","role":""},{"id":"w41074899","type":"way","role":""},{"id":"w17967581","type":"way","role":""},{"id":"w41074902","type":"way","role":""},{"id":"w41074906","type":"way","role":""},{"id":"w209707997","type":"way","role":""},{"id":"w209707998","type":"way","role":""},{"id":"w17964798","type":"way","role":""},{"id":"w17966034","type":"way","role":""},{"id":"w17967593","type":"way","role":""},{"id":"w41074888","type":"way","role":""},{"id":"w17733772","type":"way","role":""},{"id":"w41074813","type":"way","role":""},{"id":"w17742213","type":"way","role":""},{"id":"w17746863","type":"way","role":""},{"id":"w17745772","type":"way","role":""},{"id":"w17742222","type":"way","role":""},{"id":"w17745922","type":"way","role":""},{"id":"w17742198","type":"way","role":""},{"id":"w17747675","type":"way","role":""},{"id":"w17739927","type":"way","role":""},{"id":"w17745708","type":"way","role":""},{"id":"w41006323","type":"way","role":""},{"id":"w17744233","type":"way","role":""},{"id":"w17739436","type":"way","role":""},{"id":"w17742201","type":"way","role":""},{"id":"w151418616","type":"way","role":""},{"id":"w17750062","type":"way","role":""},{"id":"w17742227","type":"way","role":"east"},{"id":"w41006348","type":"way","role":"east"},{"id":"w41260984","type":"way","role":""},{"id":"w17832427","type":"way","role":""},{"id":"w17838408","type":"way","role":""},{"id":"w17835846","type":"way","role":""},{"id":"w17832923","type":"way","role":""},{"id":"w17839388","type":"way","role":""},{"id":"w17838390","type":"way","role":""},{"id":"w17831272","type":"way","role":""},{"id":"w17828581","type":"way","role":""},{"id":"w38240686","type":"way","role":""},{"id":"w17838405","type":"way","role":"east"},{"id":"w123323711","type":"way","role":"east"},{"id":"w17830167","type":"way","role":"east"},{"id":"w99011909","type":"way","role":"east"},{"id":"w41911361","type":"way","role":"east"},{"id":"w41911355","type":"way","role":"east"},{"id":"w41911356","type":"way","role":"east"},{"id":"w117148326","type":"way","role":"west"},{"id":"w41911352","type":"way","role":"west"},{"id":"w41911353","type":"way","role":"west"},{"id":"w41911354","type":"way","role":"west"},{"id":"w41911360","type":"way","role":"west"},{"id":"w38240676","type":"way","role":"west"},{"id":"w123323710","type":"way","role":"west"},{"id":"w41260271","type":"way","role":"east"},{"id":"w41260273","type":"way","role":"east"},{"id":"w17964031","type":"way","role":"east"},{"id":"w41006344","type":"way","role":"west"},{"id":"w41006351","type":"way","role":"west"}]},"n367813436":{"id":"n367813436","loc":[-85.63605205663384,41.94305506683346],"tags":{"amenity":"fire_station","name":"Three Rivers Fire Department"}},"n185948708":{"id":"n185948708","loc":[-85.6369828,41.9408789]},"n185948710":{"id":"n185948710","loc":[-85.6370184,41.9411346]},"n185954691":{"id":"n185954691","loc":[-85.634476,41.941475]},"n185954692":{"id":"n185954692","loc":[-85.635008,41.941846]},"n185954693":{"id":"n185954693","loc":[-85.635362,41.941962]},"n185954695":{"id":"n185954695","loc":[-85.63578,41.941978]},"n185972903":{"id":"n185972903","loc":[-85.63295,41.9430062]},"n185964971":{"id":"n185964971","loc":[-85.6346811,41.9431023]},"n1819805854":{"id":"n1819805854","loc":[-85.6331275,41.9404837]},"n1819805918":{"id":"n1819805918","loc":[-85.6331168,41.942798]},"n1819805762":{"id":"n1819805762","loc":[-85.6333034,41.9424123]},"n1819805907":{"id":"n1819805907","loc":[-85.6334819,41.9419121]},"n1819805915":{"id":"n1819805915","loc":[-85.6334554,41.9413588]},"n1819848888":{"id":"n1819848888","loc":[-85.6331625,41.942679]},"n1819848930":{"id":"n1819848930","loc":[-85.6338684,41.9431252]},"n1819858505":{"id":"n1819858505","loc":[-85.6346782,41.9429092]},"n1819858507":{"id":"n1819858507","loc":[-85.6339003,41.9414534]},"n1819858508":{"id":"n1819858508","loc":[-85.6345709,41.9427742]},"n1819858509":{"id":"n1819858509","loc":[-85.63419,41.9417322]},"n1819858511":{"id":"n1819858511","loc":[-85.6340666,41.9415652]},"n1819858512":{"id":"n1819858512","loc":[-85.6343295,41.9423027]},"n1819858514":{"id":"n1819858514","loc":[-85.6343241,41.942207]},"n1819858521":{"id":"n1819858521","loc":[-85.633391,41.941231]},"n1819858528":{"id":"n1819858528","loc":[-85.6343027,41.9419716]},"n185954683":{"id":"n185954683","loc":[-85.6335412,41.940147]},"n185954685":{"id":"n185954685","loc":[-85.6334296,41.9403023]},"n185954687":{"id":"n185954687","loc":[-85.6333988,41.9404704]},"n185954689":{"id":"n185954689","loc":[-85.6335511,41.9410225]},"n185954690":{"id":"n185954690","loc":[-85.6336721,41.9411669]},"n1820938802":{"id":"n1820938802","loc":[-85.6330671,41.941845]},"n1821006702":{"id":"n1821006702","loc":[-85.6344047,41.9395496]},"n2130304133":{"id":"n2130304133","loc":[-85.6349025,41.9427659]},"n2130304136":{"id":"n2130304136","loc":[-85.6346027,41.9422017]},"n2130304138":{"id":"n2130304138","loc":[-85.6348577,41.9421517]},"n2130304140":{"id":"n2130304140","loc":[-85.6348419,41.9422694]},"n2130304142":{"id":"n2130304142","loc":[-85.6349071,41.9423135]},"n2130304144":{"id":"n2130304144","loc":[-85.6350495,41.9423312]},"n2130304146":{"id":"n2130304146","loc":[-85.6351009,41.9422812]},"n2130304147":{"id":"n2130304147","loc":[-85.6351227,41.9421532]},"n2130304148":{"id":"n2130304148","loc":[-85.635526,41.9421547]},"n2130304149":{"id":"n2130304149","loc":[-85.6355339,41.9425768]},"n2130304150":{"id":"n2130304150","loc":[-85.6351582,41.9426562]},"n2130304151":{"id":"n2130304151","loc":[-85.6351207,41.9427032]},"n2138493807":{"id":"n2138493807","loc":[-85.6350923,41.9415216]},"n2138493808":{"id":"n2138493808","loc":[-85.6353603,41.9411061]},"n2138493809":{"id":"n2138493809","loc":[-85.6354421,41.9410942]},"n2138493810":{"id":"n2138493810","loc":[-85.6355079,41.9411044]},"n2138493811":{"id":"n2138493811","loc":[-85.6355693,41.9411246]},"n2138493812":{"id":"n2138493812","loc":[-85.6355829,41.9411061]},"n2138493813":{"id":"n2138493813","loc":[-85.6355624,41.9409777]},"n2138493814":{"id":"n2138493814","loc":[-85.6355011,41.9409152]},"n2138493815":{"id":"n2138493815","loc":[-85.635383,41.9409219]},"n2138493816":{"id":"n2138493816","loc":[-85.635299,41.9409658]},"n2138493817":{"id":"n2138493817","loc":[-85.6351695,41.941204]},"n2138493818":{"id":"n2138493818","loc":[-85.6348879,41.9415166]},"n2138493819":{"id":"n2138493819","loc":[-85.634897,41.9415757]},"n2138493820":{"id":"n2138493820","loc":[-85.6349606,41.9416399]},"n2138493821":{"id":"n2138493821","loc":[-85.6350219,41.9416669]},"n2138493822":{"id":"n2138493822","loc":[-85.6351241,41.9416314]},"n2138493823":{"id":"n2138493823","loc":[-85.6350855,41.9415622]},"n2138493824":{"id":"n2138493824","loc":[-85.6350401,41.9413603]},"n2138493825":{"id":"n2138493825","loc":[-85.6352206,41.9410765]},"n2138493826":{"id":"n2138493826","loc":[-85.6343865,41.9415594]},"n2138493827":{"id":"n2138493827","loc":[-85.6343506,41.9415873]},"n2138493828":{"id":"n2138493828","loc":[-85.6344158,41.9417557]},"n2138493829":{"id":"n2138493829","loc":[-85.6344614,41.9417968]},"n2138493830":{"id":"n2138493830","loc":[-85.6345005,41.9418186]},"n2138493831":{"id":"n2138493831","loc":[-85.6345965,41.9418162]},"n2138493832":{"id":"n2138493832","loc":[-85.6347317,41.9417242]},"n2138493833":{"id":"n2138493833","loc":[-85.6346722,41.941775]},"n2139858909":{"id":"n2139858909","loc":[-85.633403,41.9391006]},"n2139858910":{"id":"n2139858910","loc":[-85.6332973,41.9393967]},"n2139858911":{"id":"n2139858911","loc":[-85.633205,41.9396742]},"n2139858912":{"id":"n2139858912","loc":[-85.6332203,41.9397772]},"n2139858913":{"id":"n2139858913","loc":[-85.6333453,41.939936]},"n2139858914":{"id":"n2139858914","loc":[-85.6333761,41.9400018]},"n2139858915":{"id":"n2139858915","loc":[-85.63328,41.9402249]},"n2139858916":{"id":"n2139858916","loc":[-85.6332357,41.9403523]},"n2139858917":{"id":"n2139858917","loc":[-85.6332838,41.9405831]},"n2139858918":{"id":"n2139858918","loc":[-85.6333643,41.9408744]},"n2139858919":{"id":"n2139858919","loc":[-85.6334394,41.9410519]},"n2139858920":{"id":"n2139858920","loc":[-85.6335815,41.9411717]},"n2139858921":{"id":"n2139858921","loc":[-85.6337478,41.9412734]},"n2139858922":{"id":"n2139858922","loc":[-85.6343174,41.9415268]},"n2139858923":{"id":"n2139858923","loc":[-85.6343886,41.9417397]},"n2139858924":{"id":"n2139858924","loc":[-85.6344407,41.9418015]},"n2139858925":{"id":"n2139858925","loc":[-85.6345139,41.9418366]},"n2139858926":{"id":"n2139858926","loc":[-85.6344846,41.942005]},"n2139858927":{"id":"n2139858927","loc":[-85.6345775,41.9422218]},"n2139858928":{"id":"n2139858928","loc":[-85.6348771,41.9427814]},"n2139858929":{"id":"n2139858929","loc":[-85.6349487,41.9427995]},"n2139858930":{"id":"n2139858930","loc":[-85.6350415,41.9427874]},"n2139858931":{"id":"n2139858931","loc":[-85.6351246,41.9428589]},"n2139858978":{"id":"n2139858978","loc":[-85.6349658,41.9431481]},"n2139858979":{"id":"n2139858979","loc":[-85.6350081,41.9431287]},"n2139858980":{"id":"n2139858980","loc":[-85.6349967,41.9430997]},"n2139858981":{"id":"n2139858981","loc":[-85.6352158,41.9430352]},"n2139858982":{"id":"n2139858982","loc":[-85.6348174,41.94267]},"n2139858983":{"id":"n2139858983","loc":[-85.6346142,41.9425989]},"n2139858984":{"id":"n2139858984","loc":[-85.6344938,41.9423809]},"n2139858985":{"id":"n2139858985","loc":[-85.6344856,41.9422997]},"n2139870380":{"id":"n2139870380","loc":[-85.6346707,41.9417955]},"n2139870381":{"id":"n2139870381","loc":[-85.6345949,41.9418311]},"n2139870382":{"id":"n2139870382","loc":[-85.6343322,41.9418659]},"n2139870383":{"id":"n2139870383","loc":[-85.6342072,41.941885]},"n2139870384":{"id":"n2139870384","loc":[-85.6341325,41.9418919]},"n2139870385":{"id":"n2139870385","loc":[-85.6341314,41.9422028]},"n2139870386":{"id":"n2139870386","loc":[-85.6340472,41.9423271]},"n2139870387":{"id":"n2139870387","loc":[-85.6342185,41.9427933]},"n2139870388":{"id":"n2139870388","loc":[-85.6340605,41.9423924]},"n2139870389":{"id":"n2139870389","loc":[-85.6339889,41.9424069]},"n2139870390":{"id":"n2139870390","loc":[-85.633971,41.942356]},"n2139870391":{"id":"n2139870391","loc":[-85.63361,41.9424235]},"n2139870392":{"id":"n2139870392","loc":[-85.6337137,41.9426819]},"n2139870393":{"id":"n2139870393","loc":[-85.6336977,41.9428632]},"n2139870394":{"id":"n2139870394","loc":[-85.6338823,41.9428647]},"n2139870395":{"id":"n2139870395","loc":[-85.6339412,41.9430069]},"n2139870396":{"id":"n2139870396","loc":[-85.6338873,41.9430353]},"n2139870397":{"id":"n2139870397","loc":[-85.6337676,41.942815]},"n2139870398":{"id":"n2139870398","loc":[-85.6336822,41.9423505]},"n2139870399":{"id":"n2139870399","loc":[-85.634037,41.9422725]},"n2139870400":{"id":"n2139870400","loc":[-85.6340294,41.9422518]},"n2139870401":{"id":"n2139870401","loc":[-85.6336726,41.9423312]},"n2139870402":{"id":"n2139870402","loc":[-85.6342188,41.9425715]},"n2139870403":{"id":"n2139870403","loc":[-85.6342524,41.942565]},"n2139870404":{"id":"n2139870404","loc":[-85.6341438,41.942299]},"n2139870405":{"id":"n2139870405","loc":[-85.6341149,41.9423061]},"n2139870407":{"id":"n2139870407","loc":[-85.6340846,41.9431458]},"n2139870408":{"id":"n2139870408","loc":[-85.6339436,41.9429032]},"n2139870409":{"id":"n2139870409","loc":[-85.6343143,41.9428207]},"n2139870410":{"id":"n2139870410","loc":[-85.6343507,41.94277]},"n2139870411":{"id":"n2139870411","loc":[-85.6341527,41.942254]},"n2139870412":{"id":"n2139870412","loc":[-85.6340925,41.9422199]},"n2139870413":{"id":"n2139870413","loc":[-85.6335435,41.9423433]},"n2139870414":{"id":"n2139870414","loc":[-85.6335023,41.9423975]},"n2139870415":{"id":"n2139870415","loc":[-85.6335086,41.9424552]},"n2139870416":{"id":"n2139870416","loc":[-85.6336296,41.942665]},"n2139870417":{"id":"n2139870417","loc":[-85.6341396,41.9428596]},"n2139870418":{"id":"n2139870418","loc":[-85.6339701,41.9424487]},"n2139870419":{"id":"n2139870419","loc":[-85.6335514,41.9425294]},"n2139870420":{"id":"n2139870420","loc":[-85.6337406,41.9424929]},"n2139870421":{"id":"n2139870421","loc":[-85.6338939,41.9428687]},"n2139870422":{"id":"n2139870422","loc":[-85.6341323,41.9419538]},"n2139870423":{"id":"n2139870423","loc":[-85.6340321,41.9420376]},"n2139870424":{"id":"n2139870424","loc":[-85.6337648,41.942238]},"n2139870425":{"id":"n2139870425","loc":[-85.6337604,41.9422685]},"n2139870426":{"id":"n2139870426","loc":[-85.6337682,41.9422928]},"n2139870427":{"id":"n2139870427","loc":[-85.6338086,41.9423862]},"n2139870428":{"id":"n2139870428","loc":[-85.6349465,41.9416631]},"n2139870429":{"id":"n2139870429","loc":[-85.6351097,41.9416973]},"n2139870430":{"id":"n2139870430","loc":[-85.6353371,41.9416798]},"n2139870431":{"id":"n2139870431","loc":[-85.6349627,41.9422506]},"n2139870432":{"id":"n2139870432","loc":[-85.634979,41.9421815]},"n2139870433":{"id":"n2139870433","loc":[-85.634885,41.9421679]},"n2139870434":{"id":"n2139870434","loc":[-85.6348689,41.9422377]},"n2139870435":{"id":"n2139870435","loc":[-85.6349779,41.9419486]},"n2139870436":{"id":"n2139870436","loc":[-85.6349505,41.9418933]},"n2139870437":{"id":"n2139870437","loc":[-85.6347327,41.9419505]},"n2139870438":{"id":"n2139870438","loc":[-85.6347614,41.9420087]},"n2139870439":{"id":"n2139870439","loc":[-85.6351889,41.9416912]},"n2139870440":{"id":"n2139870440","loc":[-85.6351092,41.9418426]},"n2139870441":{"id":"n2139870441","loc":[-85.635086,41.9419659]},"n2139870442":{"id":"n2139870442","loc":[-85.6350584,41.9421466]},"n2139870443":{"id":"n2139870443","loc":[-85.6350993,41.9421606]},"n2139870444":{"id":"n2139870444","loc":[-85.6350993,41.9422132]},"n2139870445":{"id":"n2139870445","loc":[-85.6350794,41.9422855]},"n2139870446":{"id":"n2139870446","loc":[-85.6350474,41.9423159]},"n2139870447":{"id":"n2139870447","loc":[-85.6349251,41.9422998]},"n2139870448":{"id":"n2139870448","loc":[-85.634911,41.9422755]},"n2139870449":{"id":"n2139870449","loc":[-85.6349157,41.9422553]},"n2139870450":{"id":"n2139870450","loc":[-85.6347213,41.9419324]},"n2139870451":{"id":"n2139870451","loc":[-85.6349535,41.9418771]},"n2139870452":{"id":"n2139870452","loc":[-85.6350135,41.9419421]},"n2139870453":{"id":"n2139870453","loc":[-85.6348584,41.9418997]},"n2139870454":{"id":"n2139870454","loc":[-85.6348113,41.9418101]},"n2139870455":{"id":"n2139870455","loc":[-85.6347306,41.9417449]},"n2139870456":{"id":"n2139870456","loc":[-85.6349123,41.941776]},"n2139870457":{"id":"n2139870457","loc":[-85.6349423,41.9421448]},"n2139870458":{"id":"n2139870458","loc":[-85.6349436,41.9420652]},"n2139870459":{"id":"n2139870459","loc":[-85.6349136,41.9419963]},"n2139870460":{"id":"n2139870460","loc":[-85.6349814,41.9419789]},"n2139989328":{"id":"n2139989328","loc":[-85.6334188,41.9421725]},"n2139989330":{"id":"n2139989330","loc":[-85.6335087,41.9416308]},"n2139989335":{"id":"n2139989335","loc":[-85.6336856,41.9429371]},"n2139989337":{"id":"n2139989337","loc":[-85.6333713,41.9427217]},"n2139989339":{"id":"n2139989339","loc":[-85.6332912,41.9425383]},"n2139989341":{"id":"n2139989341","loc":[-85.6339369,41.9409198]},"n2139989344":{"id":"n2139989344","loc":[-85.634097,41.9409469]},"n2139989346":{"id":"n2139989346","loc":[-85.634137,41.9412852]},"n2139989348":{"id":"n2139989348","loc":[-85.6344536,41.9414151]},"n2139989350":{"id":"n2139989350","loc":[-85.6350794,41.9412392]},"n2139989351":{"id":"n2139989351","loc":[-85.6352541,41.9409387]},"n2139989353":{"id":"n2139989353","loc":[-85.6357198,41.9408007]},"n2139989355":{"id":"n2139989355","loc":[-85.6357235,41.9427088]},"n2139989357":{"id":"n2139989357","loc":[-85.6337119,41.9421256]},"n2139989359":{"id":"n2139989359","loc":[-85.6336913,41.9420655]},"n2139989360":{"id":"n2139989360","loc":[-85.633582,41.9420867]},"n2139989362":{"id":"n2139989362","loc":[-85.6336058,41.9421491]},"n2139989364":{"id":"n2139989364","loc":[-85.6339685,41.9410995]},"n2139989366":{"id":"n2139989366","loc":[-85.6339067,41.9411383]},"n2139989368":{"id":"n2139989368","loc":[-85.6339685,41.9411972]},"n2139989370":{"id":"n2139989370","loc":[-85.6340398,41.9411619]},"n2139870379":{"id":"n2139870379","loc":[-85.6348391,41.9416651]},"n2140006363":{"id":"n2140006363","loc":[-85.6353144,41.9430345]},"n2140006364":{"id":"n2140006364","loc":[-85.6349191,41.9431422]},"n2140018997":{"id":"n2140018997","loc":[-85.63645945147184,41.942986488012565],"tags":{"amenity":"townhall","name":"Three Rivers City Hall"}},"n2140018998":{"id":"n2140018998","loc":[-85.6370319,41.9427919]},"n2140018999":{"id":"n2140018999","loc":[-85.6360687,41.9427808]},"n2199856288":{"id":"n2199856288","loc":[-85.6344968,41.9407307]},"n2199856289":{"id":"n2199856289","loc":[-85.634492,41.9406036]},"n2199856290":{"id":"n2199856290","loc":[-85.634891,41.9406001]},"n2199856291":{"id":"n2199856291","loc":[-85.6348894,41.9405288]},"n2199856292":{"id":"n2199856292","loc":[-85.6349166,41.94053]},"n2199856293":{"id":"n2199856293","loc":[-85.6349166,41.9404956]},"n2199856294":{"id":"n2199856294","loc":[-85.6350219,41.9404956]},"n2199856295":{"id":"n2199856295","loc":[-85.6350251,41.94053]},"n2199856296":{"id":"n2199856296","loc":[-85.6350538,41.9405288]},"n2199856297":{"id":"n2199856297","loc":[-85.6350602,41.94079]},"n2199856298":{"id":"n2199856298","loc":[-85.6351703,41.9407912]},"n2199856299":{"id":"n2199856299","loc":[-85.6351688,41.9409171]},"n2199856300":{"id":"n2199856300","loc":[-85.6347889,41.9409135]},"n2199856301":{"id":"n2199856301","loc":[-85.6347921,41.94079]},"n2199856302":{"id":"n2199856302","loc":[-85.6348942,41.9407888]},"n2199856303":{"id":"n2199856303","loc":[-85.6348926,41.9407283]},"n185951869":{"id":"n185951869","loc":[-85.6387639,41.957288]},"n185958643":{"id":"n185958643","loc":[-85.636746,41.929221]},"n185958645":{"id":"n185958645","loc":[-85.636791,41.929363]},"n185958647":{"id":"n185958647","loc":[-85.6375975,41.9314987]},"n185958649":{"id":"n185958649","loc":[-85.637669,41.931667]},"n185958651":{"id":"n185958651","loc":[-85.637728,41.931901]},"n185958653":{"id":"n185958653","loc":[-85.637724,41.932187]},"n185958656":{"id":"n185958656","loc":[-85.637732,41.932761]},"n185958658":{"id":"n185958658","loc":[-85.637688,41.93398]},"n185958660":{"id":"n185958660","loc":[-85.637685,41.934223]},"n185958662":{"id":"n185958662","loc":[-85.6376468,41.9350232]},"n185958664":{"id":"n185958664","loc":[-85.637564,41.937028]},"n185958666":{"id":"n185958666","loc":[-85.637458,41.938197]},"n185958668":{"id":"n185958668","loc":[-85.637424,41.938692]},"n185964972":{"id":"n185964972","loc":[-85.6341901,41.9432732]},"n185971361":{"id":"n185971361","loc":[-85.635762,41.938208]},"n185971364":{"id":"n185971364","loc":[-85.635732,41.9384]},"n185971366":{"id":"n185971366","loc":[-85.635736,41.938697]},"n185972775":{"id":"n185972775","loc":[-85.635638,42.070357]},"n185972777":{"id":"n185972777","loc":[-85.635724,42.069929]},"n185972779":{"id":"n185972779","loc":[-85.635804,42.069248]},"n185972781":{"id":"n185972781","loc":[-85.635869,42.068361]},"n185972783":{"id":"n185972783","loc":[-85.635883,42.067582]},"n185972785":{"id":"n185972785","loc":[-85.635875,42.067114]},"n185972787":{"id":"n185972787","loc":[-85.635778,42.065359]},"n185972788":{"id":"n185972788","loc":[-85.635728,42.063416]},"n185972789":{"id":"n185972789","loc":[-85.635665,42.062491]},"n185972790":{"id":"n185972790","loc":[-85.635617,42.061928]},"n185972791":{"id":"n185972791","loc":[-85.635614,42.061898]},"n185972793":{"id":"n185972793","loc":[-85.635379,42.060288]},"n185972795":{"id":"n185972795","loc":[-85.635092,42.05799]},"n185972797":{"id":"n185972797","loc":[-85.634843,42.055781]},"n185972798":{"id":"n185972798","loc":[-85.634817,42.055549]},"n185972800":{"id":"n185972800","loc":[-85.634708,42.053942]},"n185972802":{"id":"n185972802","loc":[-85.634447,42.051809]},"n185972805":{"id":"n185972805","loc":[-85.634241,42.04946]},"n185972807":{"id":"n185972807","loc":[-85.633787,42.045926]},"n185972809":{"id":"n185972809","loc":[-85.633811,42.045645]},"n185972811":{"id":"n185972811","loc":[-85.63373,42.043626]},"n185972813":{"id":"n185972813","loc":[-85.633698,42.042184]},"n185972814":{"id":"n185972814","loc":[-85.63369,42.04181]},"n185972815":{"id":"n185972815","loc":[-85.633681,42.040714]},"n185972816":{"id":"n185972816","loc":[-85.633571,42.036322]},"n185972817":{"id":"n185972817","loc":[-85.633537,42.034044]},"n185972819":{"id":"n185972819","loc":[-85.633481,42.030785]},"n185972821":{"id":"n185972821","loc":[-85.633452,42.027538]},"n185972824":{"id":"n185972824","loc":[-85.633438,42.027427]},"n185972826":{"id":"n185972826","loc":[-85.633342,42.022656]},"n185972830":{"id":"n185972830","loc":[-85.63327,42.020724]},"n185972832":{"id":"n185972832","loc":[-85.633198,42.019106]},"n185972834":{"id":"n185972834","loc":[-85.633249,42.018363]},"n185972835":{"id":"n185972835","loc":[-85.633139,42.012944]},"n185972836":{"id":"n185972836","loc":[-85.63309,42.008284]},"n185972839":{"id":"n185972839","loc":[-85.63298,42.00005]},"n185972845":{"id":"n185972845","loc":[-85.6325369,41.9764959]},"n185972847":{"id":"n185972847","loc":[-85.6327549,41.9750005]},"n185972849":{"id":"n185972849","loc":[-85.6329374,41.9742527]},"n185972851":{"id":"n185972851","loc":[-85.6331387,41.9736039]},"n185972862":{"id":"n185972862","loc":[-85.6383589,41.9585023]},"n185972868":{"id":"n185972868","loc":[-85.6393633,41.9551716]},"n185972878":{"id":"n185972878","loc":[-85.639377,41.95335]},"n185972882":{"id":"n185972882","loc":[-85.6389179,41.9516944]},"n185972885":{"id":"n185972885","loc":[-85.6387444,41.9512105]},"n185972891":{"id":"n185972891","loc":[-85.636421,41.946392]},"n185972895":{"id":"n185972895","loc":[-85.635965,41.945809]},"n185972897":{"id":"n185972897","loc":[-85.635683,41.945449]},"n185972899":{"id":"n185972899","loc":[-85.635281,41.9450252]},"n185972905":{"id":"n185972905","loc":[-85.6324428,41.9425743]},"n185985217":{"id":"n185985217","loc":[-85.638243,41.943674]},"n185985219":{"id":"n185985219","loc":[-85.638228,41.943747]},"n185985221":{"id":"n185985221","loc":[-85.638163,41.943797]},"n185985222":{"id":"n185985222","loc":[-85.638089,41.943832]},"n185985223":{"id":"n185985223","loc":[-85.637969,41.943841]},"n185985225":{"id":"n185985225","loc":[-85.637841,41.943833]},"n185985227":{"id":"n185985227","loc":[-85.637601,41.943789]},"n185985229":{"id":"n185985229","loc":[-85.637449,41.943754]},"n185985231":{"id":"n185985231","loc":[-85.637342,41.943734]},"n185985233":{"id":"n185985233","loc":[-85.637218,41.943703]},"n185985235":{"id":"n185985235","loc":[-85.637151,41.943663]},"n185985238":{"id":"n185985238","loc":[-85.637118,41.943615]},"n185985240":{"id":"n185985240","loc":[-85.637073,41.943494]},"n185990434":{"id":"n185990434","loc":[-85.6329028,41.9984292],"tags":{"railway":"level_crossing"}},"n1475284023":{"id":"n1475284023","loc":[-85.6336163,41.9435806],"tags":{"railway":"level_crossing"}},"n1475293222":{"id":"n1475293222","loc":[-85.6394045,41.953658],"tags":{"railway":"level_crossing"}},"n1475293226":{"id":"n1475293226","loc":[-85.6364975,41.9638663],"tags":{"railway":"level_crossing"}},"n1475293234":{"id":"n1475293234","loc":[-85.6390449,41.9565145]},"n1475293240":{"id":"n1475293240","loc":[-85.636943,41.9473114]},"n1475293252":{"id":"n1475293252","loc":[-85.6392115,41.9559003]},"n1475293254":{"id":"n1475293254","loc":[-85.6348931,41.9685127],"tags":{"railway":"level_crossing"}},"n1475293260":{"id":"n1475293260","loc":[-85.6375999,41.9485401]},"n1475293261":{"id":"n1475293261","loc":[-85.6391256,41.9523817],"tags":{"railway":"level_crossing"}},"n1475293264":{"id":"n1475293264","loc":[-85.6394155,41.9546493],"tags":{"railway":"level_crossing"}},"n1819805614":{"id":"n1819805614","loc":[-85.6345652,41.9363097]},"n1819805618":{"id":"n1819805618","loc":[-85.6295334,41.9426862]},"n1819805622":{"id":"n1819805622","loc":[-85.6308208,41.9430773]},"n1819805626":{"id":"n1819805626","loc":[-85.6274734,41.9406592]},"n1819805629":{"id":"n1819805629","loc":[-85.6296943,41.9430533]},"n1819805632":{"id":"n1819805632","loc":[-85.6340931,41.9354477]},"n1819805636":{"id":"n1819805636","loc":[-85.6304131,41.9436598]},"n1819805639":{"id":"n1819805639","loc":[-85.6304882,41.9426623]},"n1819805641":{"id":"n1819805641","loc":[-85.6336103,41.9367487]},"n1819805643":{"id":"n1819805643","loc":[-85.6300376,41.9418084]},"n1819805645":{"id":"n1819805645","loc":[-85.6365286,41.9336679]},"n1819805647":{"id":"n1819805647","loc":[-85.632016,41.9429221]},"n1819805666":{"id":"n1819805666","loc":[-85.6314753,41.9442663]},"n1819805669":{"id":"n1819805669","loc":[-85.6268619,41.9402203]},"n1819805673":{"id":"n1819805673","loc":[-85.6296728,41.9412099]},"n1819805676":{"id":"n1819805676","loc":[-85.6354557,41.932766]},"n1819805680":{"id":"n1819805680","loc":[-85.632752,41.9431012]},"n1819805683":{"id":"n1819805683","loc":[-85.631147,41.9432014]},"n1819805687":{"id":"n1819805687","loc":[-85.635284,41.9343942]},"n1819805690":{"id":"n1819805690","loc":[-85.6249736,41.9405794]},"n1819805694":{"id":"n1819805694","loc":[-85.6294153,41.9417925]},"n1819805698":{"id":"n1819805698","loc":[-85.6323486,41.9426986]},"n1819805702":{"id":"n1819805702","loc":[-85.6340287,41.9373871]},"n1819805707":{"id":"n1819805707","loc":[-85.6353698,41.9316326]},"n1819805711":{"id":"n1819805711","loc":[-85.6284176,41.940356]},"n1819805715":{"id":"n1819805715","loc":[-85.6291471,41.9412897]},"n1819805718":{"id":"n1819805718","loc":[-85.6311105,41.943979]},"n1819805722":{"id":"n1819805722","loc":[-85.6320868,41.9400128]},"n1819805724":{"id":"n1819805724","loc":[-85.635166,41.9324627]},"n1819805727":{"id":"n1819805727","loc":[-85.6344686,41.9350567]},"n1819805728":{"id":"n1819805728","loc":[-85.6357132,41.9332369]},"n1819805731":{"id":"n1819805731","loc":[-85.629984,41.9434444]},"n1819805760":{"id":"n1819805760","loc":[-85.6330996,41.9378784]},"n1819805766":{"id":"n1819805766","loc":[-85.625274,41.9411141]},"n1819805770":{"id":"n1819805770","loc":[-85.6326321,41.9412173]},"n1819805774":{"id":"n1819805774","loc":[-85.6347047,41.9312096]},"n1819805777":{"id":"n1819805777","loc":[-85.6363569,41.9339552]},"n1819805780":{"id":"n1819805780","loc":[-85.6327392,41.941926]},"n1819805783":{"id":"n1819805783","loc":[-85.6357239,41.9338435]},"n1819805786":{"id":"n1819805786","loc":[-85.6356595,41.9346576]},"n1819805789":{"id":"n1819805789","loc":[-85.6316469,41.9436598]},"n1819805792":{"id":"n1819805792","loc":[-85.6350587,41.9354557]},"n1819805795":{"id":"n1819805795","loc":[-85.6360028,41.9322791]},"n1819805798":{"id":"n1819805798","loc":[-85.63125,41.9443062]},"n1819805802":{"id":"n1819805802","loc":[-85.6263362,41.9408109]},"n1819805805":{"id":"n1819805805","loc":[-85.6315075,41.9438753]},"n1819805808":{"id":"n1819805808","loc":[-85.6340008,41.9316051]},"n1819805810":{"id":"n1819805810","loc":[-85.6345545,41.9320557]},"n1819805812":{"id":"n1819805812","loc":[-85.6250809,41.9408587]},"n1819805814":{"id":"n1819805814","loc":[-85.6257783,41.9400926]},"n1819805834":{"id":"n1819805834","loc":[-85.6326408,41.9424363]},"n1819805838":{"id":"n1819805838","loc":[-85.6365607,41.9334365]},"n1819805842":{"id":"n1819805842","loc":[-85.6288253,41.9410343]},"n1819805846":{"id":"n1819805846","loc":[-85.6279133,41.9402921]},"n1819805849":{"id":"n1819805849","loc":[-85.6289433,41.9405156]},"n1819805852":{"id":"n1819805852","loc":[-85.6313787,41.9439152]},"n1819805858":{"id":"n1819805858","loc":[-85.6300805,41.9420398]},"n1819805861":{"id":"n1819805861","loc":[-85.6321941,41.9396297]},"n1819805864":{"id":"n1819805864","loc":[-85.6329129,41.9393903]},"n1819805868":{"id":"n1819805868","loc":[-85.632001,41.9434922]},"n1819805870":{"id":"n1819805870","loc":[-85.6314903,41.9431535]},"n1819805873":{"id":"n1819805873","loc":[-85.6251667,41.9401166]},"n1819805876":{"id":"n1819805876","loc":[-85.63287,41.939941]},"n1819805878":{"id":"n1819805878","loc":[-85.6307886,41.9437317]},"n1819805880":{"id":"n1819805880","loc":[-85.6321727,41.940348]},"n1819805883":{"id":"n1819805883","loc":[-85.6265872,41.940113]},"n1819805885":{"id":"n1819805885","loc":[-85.6268404,41.9406672]},"n1819805887":{"id":"n1819805887","loc":[-85.6325267,41.9389035]},"n1819805889":{"id":"n1819805889","loc":[-85.6364964,41.933189]},"n1819805911":{"id":"n1819805911","loc":[-85.6248663,41.9401804]},"n1819805922":{"id":"n1819805922","loc":[-85.633267,41.9387199]},"n1819805925":{"id":"n1819805925","loc":[-85.6293402,41.9408428]},"n1819848849":{"id":"n1819848849","loc":[-85.6464957,41.9695178]},"n1819848850":{"id":"n1819848850","loc":[-85.6497642,41.9611355]},"n1819848851":{"id":"n1819848851","loc":[-85.6480943,41.9624818]},"n1819848854":{"id":"n1819848854","loc":[-85.6500362,41.9657367]},"n1819848855":{"id":"n1819848855","loc":[-85.6493673,41.9783496]},"n1819848856":{"id":"n1819848856","loc":[-85.6457409,41.9548007]},"n1819848857":{"id":"n1819848857","loc":[-85.651313,41.9760426]},"n1819848858":{"id":"n1819848858","loc":[-85.6495819,41.9784772]},"n1819848859":{"id":"n1819848859","loc":[-85.6495105,41.9833722]},"n1819848860":{"id":"n1819848860","loc":[-85.6405053,41.9492792]},"n1819848863":{"id":"n1819848863","loc":[-85.6502293,41.9786826]},"n1819848865":{"id":"n1819848865","loc":[-85.6406877,41.9495106]},"n1819848870":{"id":"n1819848870","loc":[-85.6493136,41.9704611]},"n1819848871":{"id":"n1819848871","loc":[-85.6372249,41.9441284]},"n1819848873":{"id":"n1819848873","loc":[-85.6512379,41.9659441]},"n1819848875":{"id":"n1819848875","loc":[-85.6508087,41.9650187]},"n1819848877":{"id":"n1819848877","loc":[-85.6487166,41.9605352]},"n1819848878":{"id":"n1819848878","loc":[-85.6506478,41.9760665]},"n1819848879":{"id":"n1819848879","loc":[-85.651431,41.9758512]},"n1819848886":{"id":"n1819848886","loc":[-85.6477617,41.9563945]},"n1819848889":{"id":"n1819848889","loc":[-85.6497895,41.9832286]},"n1819848892":{"id":"n1819848892","loc":[-85.6504868,41.9791931]},"n1819848893":{"id":"n1819848893","loc":[-85.6498002,41.9615085]},"n1819848894":{"id":"n1819848894","loc":[-85.6404302,41.9502846]},"n1819848901":{"id":"n1819848901","loc":[-85.6354412,41.9439886]},"n1819848903":{"id":"n1819848903","loc":[-85.6472145,41.9698528]},"n1819848904":{"id":"n1819848904","loc":[-85.6401979,41.9486233]},"n1819848905":{"id":"n1819848905","loc":[-85.6475042,41.963503]},"n1819848909":{"id":"n1819848909","loc":[-85.6343405,41.94358]},"n1819848914":{"id":"n1819848914","loc":[-85.6503474,41.9737773]},"n1819848915":{"id":"n1819848915","loc":[-85.6389533,41.9470992]},"n1819848916":{"id":"n1819848916","loc":[-85.6483625,41.9577907]},"n1819848917":{"id":"n1819848917","loc":[-85.6484768,41.9617419]},"n1819848918":{"id":"n1819848918","loc":[-85.644078,41.9545693]},"n1819848919":{"id":"n1819848919","loc":[-85.6437169,41.9543041]},"n1819848920":{"id":"n1819848920","loc":[-85.6478331,41.9627949]},"n1819848922":{"id":"n1819848922","loc":[-85.6499144,41.9785889]},"n1819848924":{"id":"n1819848924","loc":[-85.647633,41.9720066]},"n1819848926":{"id":"n1819848926","loc":[-85.6487987,41.978868]},"n1819848927":{"id":"n1819848927","loc":[-85.6495105,41.9730355]},"n1819848928":{"id":"n1819848928","loc":[-85.648223,41.9829654]},"n1819848929":{"id":"n1819848929","loc":[-85.6514846,41.9659122]},"n1819848931":{"id":"n1819848931","loc":[-85.6498753,41.9731871]},"n1819848932":{"id":"n1819848932","loc":[-85.640906,41.9508575]},"n1819848933":{"id":"n1819848933","loc":[-85.649775,41.9799767]},"n1819848934":{"id":"n1819848934","loc":[-85.6507014,41.9739927]},"n1819848937":{"id":"n1819848937","loc":[-85.6479763,41.9840899]},"n1819848938":{"id":"n1819848938","loc":[-85.6501113,41.9600884]},"n1819848939":{"id":"n1819848939","loc":[-85.6389962,41.9478253]},"n1819848941":{"id":"n1819848941","loc":[-85.637469,41.9445791]},"n1819848942":{"id":"n1819848942","loc":[-85.6494569,41.9601682]},"n1819848943":{"id":"n1819848943","loc":[-85.6368803,41.9439351]},"n1819848945":{"id":"n1819848945","loc":[-85.6474398,41.9724213]},"n1819848946":{"id":"n1819848946","loc":[-85.6382629,41.9463666]},"n1819848948":{"id":"n1819848948","loc":[-85.6489633,41.9830771]},"n1819848952":{"id":"n1819848952","loc":[-85.6488882,41.9600326]},"n1819848953":{"id":"n1819848953","loc":[-85.6488094,41.9774324]},"n1819848954":{"id":"n1819848954","loc":[-85.6491135,41.9600485]},"n1819848955":{"id":"n1819848955","loc":[-85.6501435,41.9734583]},"n1819848956":{"id":"n1819848956","loc":[-85.6495534,41.960958]},"n1819848958":{"id":"n1819848958","loc":[-85.6474683,41.9561491]},"n1819848959":{"id":"n1819848959","loc":[-85.6401083,41.9485451]},"n1819848960":{"id":"n1819848960","loc":[-85.6481764,41.9678686]},"n1819848961":{"id":"n1819848961","loc":[-85.6484017,41.967382]},"n1819848962":{"id":"n1819848962","loc":[-85.6501328,41.959897]},"n1819848964":{"id":"n1819848964","loc":[-85.6403695,41.9504586]},"n1819848966":{"id":"n1819848966","loc":[-85.6398975,41.9491499]},"n1819848967":{"id":"n1819848967","loc":[-85.6412455,41.9510187]},"n1819848968":{"id":"n1819848968","loc":[-85.6482622,41.9619493]},"n1819848969":{"id":"n1819848969","loc":[-85.6405841,41.9501474]},"n1819848970":{"id":"n1819848970","loc":[-85.6478583,41.9703394]},"n1819848971":{"id":"n1819848971","loc":[-85.6493388,41.9832845]},"n1819848972":{"id":"n1819848972","loc":[-85.6485664,41.9829415]},"n1819848974":{"id":"n1819848974","loc":[-85.6491457,41.9779887]},"n1819848975":{"id":"n1819848975","loc":[-85.6468889,41.9697033]},"n1819848976":{"id":"n1819848976","loc":[-85.6452726,41.9546072]},"n1819848977":{"id":"n1819848977","loc":[-85.6448435,41.9546072]},"n1819848979":{"id":"n1819848979","loc":[-85.6485342,41.9763138]},"n1819848980":{"id":"n1819848980","loc":[-85.6495282,41.9664087]},"n1819848986":{"id":"n1819848986","loc":[-85.6486307,41.9603278]},"n1819848987":{"id":"n1819848987","loc":[-85.6492278,41.9791871]},"n1819848990":{"id":"n1819848990","loc":[-85.6501934,41.9800724]},"n1819848992":{"id":"n1819848992","loc":[-85.6482445,41.9819685]},"n1819848993":{"id":"n1819848993","loc":[-85.6481871,41.9704451]},"n1819848994":{"id":"n1819848994","loc":[-85.6371364,41.9457602]},"n1819848996":{"id":"n1819848996","loc":[-85.6500362,41.9801023]},"n1819849000":{"id":"n1819849000","loc":[-85.639007,41.9485914]},"n1819849001":{"id":"n1819849001","loc":[-85.6488882,41.9669253]},"n1819849002":{"id":"n1819849002","loc":[-85.6484698,41.9565062]},"n1819849004":{"id":"n1819849004","loc":[-85.6510769,41.9761064]},"n1819849005":{"id":"n1819849005","loc":[-85.6503581,41.9799029]},"n1819849006":{"id":"n1819849006","loc":[-85.6489381,41.9703893]},"n1819849008":{"id":"n1819849008","loc":[-85.6497457,41.9833588]},"n1819849011":{"id":"n1819849011","loc":[-85.6497358,41.9717593]},"n1819849012":{"id":"n1819849012","loc":[-85.6494676,41.9796796]},"n1819849019":{"id":"n1819849019","loc":[-85.6486093,41.9771034]},"n1819849021":{"id":"n1819849021","loc":[-85.6504546,41.9796556]},"n1819849022":{"id":"n1819849022","loc":[-85.6371294,41.9454154]},"n1819849023":{"id":"n1819849023","loc":[-85.6503436,41.9759249]},"n1819849025":{"id":"n1819849025","loc":[-85.6462382,41.9693822]},"n1819849026":{"id":"n1819849026","loc":[-85.6497573,41.983093]},"n1819849028":{"id":"n1819849028","loc":[-85.6497465,41.9602799]},"n1819849029":{"id":"n1819849029","loc":[-85.6374728,41.9460698]},"n1819849030":{"id":"n1819849030","loc":[-85.6486592,41.9566039]},"n1819849031":{"id":"n1819849031","loc":[-85.6515989,41.9654993]},"n1819849032":{"id":"n1819849032","loc":[-85.6387028,41.9482658]},"n1819849033":{"id":"n1819849033","loc":[-85.6464742,41.9688398]},"n1819849034":{"id":"n1819849034","loc":[-85.6495212,41.9589236]},"n1819849035":{"id":"n1819849035","loc":[-85.6490599,41.9790096]},"n1819849036":{"id":"n1819849036","loc":[-85.6489918,41.9800724]},"n1819849038":{"id":"n1819849038","loc":[-85.6499182,41.9659042]},"n1819849040":{"id":"n1819849040","loc":[-85.639758,41.9490143]},"n1819849041":{"id":"n1819849041","loc":[-85.6514846,41.9755241]},"n1819849042":{"id":"n1819849042","loc":[-85.6436633,41.9540647]},"n1819849045":{"id":"n1819849045","loc":[-85.6475541,41.9726387]},"n1819849046":{"id":"n1819849046","loc":[-85.6488308,41.9718331]},"n1819849047":{"id":"n1819849047","loc":[-85.6377694,41.9460953]},"n1819849048":{"id":"n1819849048","loc":[-85.6490706,41.9804452]},"n1819849049":{"id":"n1819849049","loc":[-85.6485449,41.9766248]},"n1819849051":{"id":"n1819849051","loc":[-85.6483625,41.9790256]},"n1819849052":{"id":"n1819849052","loc":[-85.6490706,41.9585167]},"n1819849053":{"id":"n1819849053","loc":[-85.6425008,41.9522874]},"n1819849054":{"id":"n1819849054","loc":[-85.6475793,41.9632158]},"n1819849055":{"id":"n1819849055","loc":[-85.6408631,41.9499399]},"n1819849056":{"id":"n1819849056","loc":[-85.6483373,41.9814681]},"n1819849057":{"id":"n1819849057","loc":[-85.6313548,41.9442876]},"n1819849058":{"id":"n1819849058","loc":[-85.6432663,41.9529796]},"n1819849059":{"id":"n1819849059","loc":[-85.6487128,41.9582873]},"n1819849060":{"id":"n1819849060","loc":[-85.6482338,41.9817612]},"n1819849062":{"id":"n1819849062","loc":[-85.6485664,41.9788661]},"n1819849063":{"id":"n1819849063","loc":[-85.6373081,41.9448824]},"n1819849064":{"id":"n1819849064","loc":[-85.6472215,41.9557582]},"n1819849065":{"id":"n1819849065","loc":[-85.6348984,41.9440414]},"n1819849066":{"id":"n1819849066","loc":[-85.6501972,41.9647315]},"n1819849067":{"id":"n1819849067","loc":[-85.6489741,41.9808281]},"n1819849068":{"id":"n1819849068","loc":[-85.6420111,41.9515034]},"n1819849069":{"id":"n1819849069","loc":[-85.6397972,41.9488882]},"n1819849070":{"id":"n1819849070","loc":[-85.6499718,41.9593465]},"n1819849071":{"id":"n1819849071","loc":[-85.6486844,41.9811311]},"n1819849072":{"id":"n1819849072","loc":[-85.6390392,41.9474663]},"n1819849074":{"id":"n1819849074","loc":[-85.6495642,41.9616362]},"n1819849075":{"id":"n1819849075","loc":[-85.6483518,41.9791931]},"n1819849076":{"id":"n1819849076","loc":[-85.6478974,41.9833104]},"n1819849077":{"id":"n1819849077","loc":[-85.640155,41.948719]},"n1819849078":{"id":"n1819849078","loc":[-85.6399366,41.9487845]},"n1819849079":{"id":"n1819849079","loc":[-85.6492959,41.9825348]},"n1819849080":{"id":"n1819849080","loc":[-85.6505083,41.9648352]},"n1819849081":{"id":"n1819849081","loc":[-85.6492959,41.9645241]},"n1819849082":{"id":"n1819849082","loc":[-85.6402049,41.9491835]},"n1819849083":{"id":"n1819849083","loc":[-85.6495175,41.9826963]},"n1819849084":{"id":"n1819849084","loc":[-85.6480836,41.9728361]},"n1819849085":{"id":"n1819849085","loc":[-85.6374349,41.9443425]},"n1819849086":{"id":"n1819849086","loc":[-85.6478331,41.9681238]},"n1819849089":{"id":"n1819849089","loc":[-85.639368,41.9486169]},"n1819849092":{"id":"n1819849092","loc":[-85.6503581,41.9788022]},"n1819849093":{"id":"n1819849093","loc":[-85.64862,41.9568014]},"n1819849094":{"id":"n1819849094","loc":[-85.6496999,41.9828877]},"n1819849095":{"id":"n1819849095","loc":[-85.647472,41.972198]},"n1819849096":{"id":"n1819849096","loc":[-85.6485771,41.9644523]},"n1819849097":{"id":"n1819849097","loc":[-85.6388353,41.9480488]},"n1819849099":{"id":"n1819849099","loc":[-85.6472752,41.9683312]},"n1819849104":{"id":"n1819849104","loc":[-85.6479548,41.9836035]},"n1819849105":{"id":"n1819849105","loc":[-85.6462489,41.9691668]},"n1819849107":{"id":"n1819849107","loc":[-85.6511912,41.9746328]},"n1819849108":{"id":"n1819849108","loc":[-85.6498646,41.9714881]},"n1819849111":{"id":"n1819849111","loc":[-85.6488239,41.961684]},"n1819849112":{"id":"n1819849112","loc":[-85.6469356,41.9553812]},"n1819849114":{"id":"n1819849114","loc":[-85.6479548,41.9640853]},"n1819849119":{"id":"n1819849119","loc":[-85.6491565,41.961692]},"n1819849121":{"id":"n1819849121","loc":[-85.651667,41.9656728]},"n1819849124":{"id":"n1819849124","loc":[-85.6388423,41.9484414]},"n1819849126":{"id":"n1819849126","loc":[-85.6371686,41.9450978]},"n1819849127":{"id":"n1819849127","loc":[-85.6502615,41.9656728]},"n1819849129":{"id":"n1819849129","loc":[-85.6498501,41.9613031]},"n1819849131":{"id":"n1819849131","loc":[-85.6513881,41.9653298]},"n1819849133":{"id":"n1819849133","loc":[-85.639883,41.9485291]},"n1819849139":{"id":"n1819849139","loc":[-85.6508693,41.9658264]},"n1819849140":{"id":"n1819849140","loc":[-85.6486806,41.9761642]},"n1819849141":{"id":"n1819849141","loc":[-85.6483159,41.9717613]},"n1819849144":{"id":"n1819849144","loc":[-85.6443714,41.9546232]},"n1819849146":{"id":"n1819849146","loc":[-85.641775,41.9513359]},"n1819849147":{"id":"n1819849147","loc":[-85.6495604,41.9757335]},"n1819849148":{"id":"n1819849148","loc":[-85.6465671,41.9551678]},"n1819849150":{"id":"n1819849150","loc":[-85.6485127,41.9794084]},"n1819849151":{"id":"n1819849151","loc":[-85.6499144,41.9757096]},"n1819849152":{"id":"n1819849152","loc":[-85.6433736,41.9531072]},"n1819849154":{"id":"n1819849154","loc":[-85.6489741,41.9607426]},"n1819849155":{"id":"n1819849155","loc":[-85.640627,41.9507697]},"n1819849156":{"id":"n1819849156","loc":[-85.6509659,41.9743058]},"n1819849157":{"id":"n1819849157","loc":[-85.6486844,41.9704431]},"n1819849158":{"id":"n1819849158","loc":[-85.6498538,41.9711132]},"n1819849159":{"id":"n1819849159","loc":[-85.6358937,41.943719]},"n1819849160":{"id":"n1819849160","loc":[-85.6497358,41.9707702]},"n1819849161":{"id":"n1819849161","loc":[-85.6480476,41.9564842]},"n1819849162":{"id":"n1819849162","loc":[-85.6482982,41.9574556]},"n1819849163":{"id":"n1819849163","loc":[-85.6501757,41.9757794]},"n1819849164":{"id":"n1819849164","loc":[-85.6372973,41.9459916]},"n1819849165":{"id":"n1819849165","loc":[-85.6513773,41.9750775]},"n1819849166":{"id":"n1819849166","loc":[-85.6436418,41.9537455]},"n1819849167":{"id":"n1819849167","loc":[-85.6483625,41.9571524]},"n1819849169":{"id":"n1819849169","loc":[-85.647751,41.9727962]},"n1819849170":{"id":"n1819849170","loc":[-85.6504546,41.9656808]},"n1819849171":{"id":"n1819849171","loc":[-85.6479977,41.971839]},"n1819849172":{"id":"n1819849172","loc":[-85.6482767,41.9642449]},"n1819849174":{"id":"n1819849174","loc":[-85.6414317,41.9512086]},"n1819849176":{"id":"n1819849176","loc":[-85.6469034,41.9685287]},"n1819849179":{"id":"n1819849179","loc":[-85.6408631,41.9497564]},"n1819849182":{"id":"n1819849182","loc":[-85.6476721,41.96384]},"n1819849183":{"id":"n1819849183","loc":[-85.6479725,41.983111]},"n1819849184":{"id":"n1819849184","loc":[-85.640788,41.9500516]},"n1819849185":{"id":"n1819849185","loc":[-85.6427798,41.9528778]},"n1819849186":{"id":"n1819849186","loc":[-85.6435308,41.9534124]},"n1819849187":{"id":"n1819849187","loc":[-85.6483733,41.9821998]},"n1819849189":{"id":"n1819849189","loc":[-85.6351752,41.9440796]},"n1819849191":{"id":"n1819849191","loc":[-85.6487021,41.9601463]},"n1819849192":{"id":"n1819849192","loc":[-85.6363811,41.9437605]},"n1819849193":{"id":"n1819849193","loc":[-85.6490883,41.9759728]},"n1819849194":{"id":"n1819849194","loc":[-85.6423292,41.9520081]},"n1819849195":{"id":"n1819849195","loc":[-85.6500003,41.960242]},"n1819849196":{"id":"n1819849196","loc":[-85.6385778,41.9466443]},"n1819849197":{"id":"n1819849197","loc":[-85.6494032,41.9718789]},"n1819849198":{"id":"n1819849198","loc":[-85.6404339,41.9506501]},"n1819849199":{"id":"n1819849199","loc":[-85.6426226,41.9527083]},"n1819849200":{"id":"n1819849200","loc":[-85.6439101,41.9545035]},"n1819849201":{"id":"n1819849201","loc":[-85.6516563,41.9657845]},"n1819849202":{"id":"n1819849202","loc":[-85.6473395,41.9699585]},"n1819858501":{"id":"n1819858501","loc":[-85.6361263,41.9437126]},"n1819858503":{"id":"n1819858503","loc":[-85.6350068,41.944034]},"n1819858513":{"id":"n1819858513","loc":[-85.6371402,41.9453282]},"n1819858518":{"id":"n1819858518","loc":[-85.6348713,41.9432923]},"n1819858523":{"id":"n1819858523","loc":[-85.6357047,41.943799]},"n1819858526":{"id":"n1819858526","loc":[-85.6349947,41.9435756]},"n1819858531":{"id":"n1819858531","loc":[-85.6350376,41.943827]},"n1820937508":{"id":"n1820937508","loc":[-85.1026013,42.0881722]},"n1820937509":{"id":"n1820937509","loc":[-85.0558088,42.102493]},"n1820937511":{"id":"n1820937511","loc":[-85.3030116,41.9724451]},"n1820937513":{"id":"n1820937513","loc":[-85.0353221,42.1027398]},"n1820937514":{"id":"n1820937514","loc":[-85.0835468,42.1015469]},"n1820937515":{"id":"n1820937515","loc":[-85.2421298,42.0106305]},"n1820937517":{"id":"n1820937517","loc":[-85.0090632,42.0910452]},"n1820937518":{"id":"n1820937518","loc":[-85.086626,42.0948838]},"n1820937520":{"id":"n1820937520","loc":[-85.2552039,42.0015448]},"n1820937521":{"id":"n1820937521","loc":[-85.3739614,41.9969917]},"n1820937522":{"id":"n1820937522","loc":[-85.4831166,41.993898]},"n1820937523":{"id":"n1820937523","loc":[-85.0341084,42.0977657]},"n1820937524":{"id":"n1820937524","loc":[-85.3272802,41.9710333]},"n1820937525":{"id":"n1820937525","loc":[-85.2125568,42.0414521]},"n1820937526":{"id":"n1820937526","loc":[-85.3798022,41.9992458]},"n1820937527":{"id":"n1820937527","loc":[-85.2652021,41.999768]},"n1820937528":{"id":"n1820937528","loc":[-85.3852739,42.0004896]},"n1820937529":{"id":"n1820937529","loc":[-85.3911919,42.0030513]},"n1820937530":{"id":"n1820937530","loc":[-85.5440349,41.9717109]},"n1820937531":{"id":"n1820937531","loc":[-85.2790155,41.9911764]},"n1820937532":{"id":"n1820937532","loc":[-85.4723277,41.9950518]},"n1820937533":{"id":"n1820937533","loc":[-85.5690546,41.9653931]},"n1820937535":{"id":"n1820937535","loc":[-85.5674882,41.9649623]},"n1820937536":{"id":"n1820937536","loc":[-85.6362815,41.9189165]},"n1820937537":{"id":"n1820937537","loc":[-85.5659003,41.963638]},"n1820937539":{"id":"n1820937539","loc":[-85.6391353,41.9122262]},"n1820937540":{"id":"n1820937540","loc":[-85.4834385,41.9894803]},"n1820937541":{"id":"n1820937541","loc":[-85.6399078,41.9160744]},"n1820937542":{"id":"n1820937542","loc":[-85.632874,41.941031]},"n1820937543":{"id":"n1820937543","loc":[-85.1307591,42.0726961]},"n1820937544":{"id":"n1820937544","loc":[-85.6444397,41.9128378]},"n1820937545":{"id":"n1820937545","loc":[-85.6197204,41.9420365]},"n1820937546":{"id":"n1820937546","loc":[-85.1164857,42.0864631]},"n1820937547":{"id":"n1820937547","loc":[-85.6476111,41.9142222]},"n1820937548":{"id":"n1820937548","loc":[-85.2915747,41.9774223]},"n1820937549":{"id":"n1820937549","loc":[-85.6430192,41.9102461]},"n1820937550":{"id":"n1820937550","loc":[-85.1597495,42.0639017]},"n1820937551":{"id":"n1820937551","loc":[-85.5504079,41.9701793]},"n1820937553":{"id":"n1820937553","loc":[-85.2781317,41.9948951]},"n1820937555":{"id":"n1820937555","loc":[-85.3724594,41.997518]},"n1820937556":{"id":"n1820937556","loc":[-85.5629434,41.9665155]},"n1820937557":{"id":"n1820937557","loc":[-85.3791971,41.9990808]},"n1820937558":{"id":"n1820937558","loc":[-85.001891,42.0878843]},"n1820937560":{"id":"n1820937560","loc":[-85.3140838,41.9709056]},"n1820937561":{"id":"n1820937561","loc":[-85.2468032,42.0146987]},"n1820937563":{"id":"n1820937563","loc":[-85.0877378,42.097255]},"n1820937564":{"id":"n1820937564","loc":[-85.2442498,42.0150654]},"n1820937566":{"id":"n1820937566","loc":[-85.3108973,41.9701478]},"n1820937568":{"id":"n1820937568","loc":[-85.0344584,42.1016572]},"n1820937569":{"id":"n1820937569","loc":[-85.2331025,42.0297387]},"n1820937570":{"id":"n1820937570","loc":[-85.5058446,41.9746996]},"n1820937571":{"id":"n1820937571","loc":[-85.5622739,41.9676427]},"n1820937572":{"id":"n1820937572","loc":[-85.2792687,41.9890337]},"n1820937574":{"id":"n1820937574","loc":[-84.9909302,42.08695]},"n1820937575":{"id":"n1820937575","loc":[-85.6218233,41.9418609]},"n1820937576":{"id":"n1820937576","loc":[-85.3577437,41.9931062]},"n1820937577":{"id":"n1820937577","loc":[-85.639028,41.9165853]},"n1820937578":{"id":"n1820937578","loc":[-84.9956576,42.0865348]},"n1820937579":{"id":"n1820937579","loc":[-85.4828376,41.990198]},"n1820937580":{"id":"n1820937580","loc":[-85.3244478,41.9720543]},"n1820937582":{"id":"n1820937582","loc":[-85.0517479,42.1035159]},"n1820937583":{"id":"n1820937583","loc":[-85.225646,42.0338025]},"n1820937584":{"id":"n1820937584","loc":[-84.9941019,42.0862163]},"n1820937586":{"id":"n1820937586","loc":[-85.1051762,42.0879452]},"n1820937587":{"id":"n1820937587","loc":[-85.1245203,42.0753162]},"n1820937588":{"id":"n1820937588","loc":[-85.3250808,41.9719506]},"n1820937589":{"id":"n1820937589","loc":[-85.2720109,41.997933]},"n1820937590":{"id":"n1820937590","loc":[-85.2556653,42.0027248]},"n1820937591":{"id":"n1820937591","loc":[-85.0872483,42.0943544]},"n1820937592":{"id":"n1820937592","loc":[-85.2778353,41.9955023]},"n1820937593":{"id":"n1820937593","loc":[-85.2984733,41.9735538]},"n1820937594":{"id":"n1820937594","loc":[-85.101578,42.0889552]},"n1820937595":{"id":"n1820937595","loc":[-85.3888745,42.0016959]},"n1820937596":{"id":"n1820937596","loc":[-84.9903508,42.0870654]},"n1820937597":{"id":"n1820937597","loc":[-85.6405558,41.9146261]},"n1820937598":{"id":"n1820937598","loc":[-85.6460704,41.9141311]},"n1820937599":{"id":"n1820937599","loc":[-85.0377468,42.1037428]},"n1820937600":{"id":"n1820937600","loc":[-85.2298345,42.0312899]},"n1820937601":{"id":"n1820937601","loc":[-85.1080958,42.0861964]},"n1820937602":{"id":"n1820937602","loc":[-85.6325307,41.9402329]},"n1820937603":{"id":"n1820937603","loc":[-85.1165984,42.0832184]},"n1820937604":{"id":"n1820937604","loc":[-85.6354446,41.9190602]},"n1820937605":{"id":"n1820937605","loc":[-85.1114592,42.0862959]},"n1820937606":{"id":"n1820937606","loc":[-85.0858763,42.1001646]},"n1820937607":{"id":"n1820937607","loc":[-85.0472083,42.1015151]},"n1820937608":{"id":"n1820937608","loc":[-85.0802477,42.1027609]},"n1820937610":{"id":"n1820937610","loc":[-85.0924585,42.0928564]},"n1820937611":{"id":"n1820937611","loc":[-85.0329617,42.09827]},"n1820937612":{"id":"n1820937612","loc":[-85.2814617,41.993465]},"n1820937613":{"id":"n1820937613","loc":[-85.3097708,41.9700282]},"n1820937614":{"id":"n1820937614","loc":[-85.2809427,41.993695]},"n1820937615":{"id":"n1820937615","loc":[-85.0583233,42.1026494]},"n1820937617":{"id":"n1820937617","loc":[-85.2801592,41.9840021]},"n1820937619":{"id":"n1820937619","loc":[-85.1064154,42.0863449]},"n1820937620":{"id":"n1820937620","loc":[-85.0423173,42.1014662]},"n1820937621":{"id":"n1820937621","loc":[-85.2168913,42.0398107]},"n1820937622":{"id":"n1820937622","loc":[-85.2798481,41.9833401]},"n1820937623":{"id":"n1820937623","loc":[-85.0575468,42.1028672]},"n1820937625":{"id":"n1820937625","loc":[-85.0130369,42.0893067]},"n1820937626":{"id":"n1820937626","loc":[-85.0346985,42.1018256]},"n1820937627":{"id":"n1820937627","loc":[-85.2231569,42.0372768]},"n1820937628":{"id":"n1820937628","loc":[-85.2956195,41.9732268]},"n1820937629":{"id":"n1820937629","loc":[-85.1052312,42.086893]},"n1820937630":{"id":"n1820937630","loc":[-85.4813356,41.9958436]},"n1820937631":{"id":"n1820937631","loc":[-85.0961599,42.0914672]},"n1820937632":{"id":"n1820937632","loc":[-85.308419,41.9704749]},"n1820937633":{"id":"n1820937633","loc":[-85.295952,41.9715119]},"n1820937634":{"id":"n1820937634","loc":[-85.3310933,41.9703923]},"n1820937635":{"id":"n1820937635","loc":[-85.2940745,41.9739686]},"n1820937636":{"id":"n1820937636","loc":[-85.3803343,42.000484]},"n1820937637":{"id":"n1820937637","loc":[-85.1174231,42.0845533]},"n1820937638":{"id":"n1820937638","loc":[-85.0095836,42.089839]},"n1820937639":{"id":"n1820937639","loc":[-85.3179354,41.9705866]},"n1820937640":{"id":"n1820937640","loc":[-85.257708,42.0001189]},"n1820937641":{"id":"n1820937641","loc":[-85.2563522,42.0002771]},"n1820937642":{"id":"n1820937642","loc":[-85.3181929,41.970419]},"n1820937643":{"id":"n1820937643","loc":[-85.2911884,41.9757154]},"n1820937644":{"id":"n1820937644","loc":[-85.2714423,41.9975862]},"n1820937645":{"id":"n1820937645","loc":[-85.0193669,42.089888]},"n1820937646":{"id":"n1820937646","loc":[-85.3889818,42.0039921]},"n1820937647":{"id":"n1820937647","loc":[-85.3408093,41.9853965]},"n1820937648":{"id":"n1820937648","loc":[-85.1258091,42.0748332]},"n1820937649":{"id":"n1820937649","loc":[-85.5722561,41.962782]},"n1820937650":{"id":"n1820937650","loc":[-85.3266902,41.9721819]},"n1820937651":{"id":"n1820937651","loc":[-85.1473255,42.065192]},"n1820937652":{"id":"n1820937652","loc":[-85.1462526,42.0655106]},"n1820937653":{"id":"n1820937653","loc":[-85.4641051,42.0013929]},"n1820937654":{"id":"n1820937654","loc":[-85.5620379,41.9700677]},"n1820937655":{"id":"n1820937655","loc":[-85.3226025,41.971121]},"n1820937656":{"id":"n1820937656","loc":[-85.0200965,42.0899516]},"n1820937657":{"id":"n1820937657","loc":[-85.0624714,42.1044711]},"n1820937658":{"id":"n1820937658","loc":[-85.5649562,41.9637178]},"n1820937659":{"id":"n1820937659","loc":[-85.2360315,42.0253315]},"n1820937660":{"id":"n1820937660","loc":[-85.3881449,41.9994475]},"n1820937661":{"id":"n1820937661","loc":[-85.5032911,41.976263]},"n1820937662":{"id":"n1820937662","loc":[-85.481297,41.9871414]},"n1820937663":{"id":"n1820937663","loc":[-85.1167056,42.0841898]},"n1820937664":{"id":"n1820937664","loc":[-85.2891714,41.9787223]},"n1820937665":{"id":"n1820937665","loc":[-85.4393429,42.0058736]},"n1820937666":{"id":"n1820937666","loc":[-85.0077007,42.0895762]},"n1820937667":{"id":"n1820937667","loc":[-85.2736202,41.9979171]},"n1820937668":{"id":"n1820937668","loc":[-84.9935332,42.0859296]},"n1820937669":{"id":"n1820937669","loc":[-85.0622769,42.1046713]},"n1820937670":{"id":"n1820937670","loc":[-85.2309031,42.0311249]},"n1820937671":{"id":"n1820937671","loc":[-85.0343726,42.10069]},"n1820937672":{"id":"n1820937672","loc":[-85.0596551,42.1048612]},"n1820937673":{"id":"n1820937673","loc":[-85.1338597,42.0707449]},"n1820937674":{"id":"n1820937674","loc":[-85.3117663,41.9689194]},"n1820937675":{"id":"n1820937675","loc":[-85.0705649,42.1057499]},"n1820937676":{"id":"n1820937676","loc":[-85.2441425,42.0180944]},"n1820937677":{"id":"n1820937677","loc":[-85.1171174,42.0862692]},"n1820937678":{"id":"n1820937678","loc":[-85.0346824,42.1005519]},"n1820937680":{"id":"n1820937680","loc":[-85.2389927,42.0229245]},"n1820937681":{"id":"n1820937681","loc":[-85.0834892,42.1018642]},"n1820937682":{"id":"n1820937682","loc":[-85.0619443,42.1049459]},"n1820937683":{"id":"n1820937683","loc":[-85.2845366,41.9811868]},"n1820937684":{"id":"n1820937684","loc":[-85.210411,42.0394123]},"n1820937685":{"id":"n1820937685","loc":[-85.4377383,42.0055942]},"n1820937686":{"id":"n1820937686","loc":[-85.2882058,41.9789138]},"n1820937687":{"id":"n1820937687","loc":[-85.2741191,41.9955808]},"n1820937688":{"id":"n1820937688","loc":[-85.3442211,41.9903575]},"n1820937689":{"id":"n1820937689","loc":[-85.2641413,41.9995237]},"n1820937690":{"id":"n1820937690","loc":[-85.2804489,41.9829174]},"n1820937691":{"id":"n1820937691","loc":[-85.5593342,41.9729074]},"n1820937692":{"id":"n1820937692","loc":[-85.3590912,41.9932601]},"n1820937694":{"id":"n1820937694","loc":[-85.4826445,41.9957479]},"n1820937695":{"id":"n1820937695","loc":[-85.4539127,42.0063041]},"n1820937696":{"id":"n1820937696","loc":[-85.2456767,42.0153683]},"n1820937697":{"id":"n1820937697","loc":[-85.5794015,41.9489631]},"n1820937698":{"id":"n1820937698","loc":[-85.4108686,42.0078507]},"n1820937699":{"id":"n1820937699","loc":[-85.0616386,42.1051529]},"n1820937700":{"id":"n1820937700","loc":[-85.4977979,41.978241]},"n1820937701":{"id":"n1820937701","loc":[-85.2488417,42.0086319]},"n1820937702":{"id":"n1820937702","loc":[-85.5588836,41.9728116]},"n1820937703":{"id":"n1820937703","loc":[-85.4557366,42.0051241]},"n1820937705":{"id":"n1820937705","loc":[-85.0723151,42.1056094]},"n1820937706":{"id":"n1820937706","loc":[-85.0057909,42.0887323]},"n1820937707":{"id":"n1820937707","loc":[-85.0756786,42.105677]},"n1820937708":{"id":"n1820937708","loc":[-85.0901504,42.0940001]},"n1820937709":{"id":"n1820937709","loc":[-85.0979999,42.0910213]},"n1820937710":{"id":"n1820937710","loc":[-85.2376301,42.0239686]},"n1820937711":{"id":"n1820937711","loc":[-85.2780671,41.9902299]},"n1820937712":{"id":"n1820937712","loc":[-85.251481,42.0113188]},"n1820937713":{"id":"n1820937713","loc":[-85.3114767,41.9690311]},"n1820937714":{"id":"n1820937714","loc":[-85.2649621,41.9975662]},"n1820937715":{"id":"n1820937715","loc":[-85.283807,41.9813383]},"n1820937716":{"id":"n1820937716","loc":[-85.5515451,41.9703867]},"n1820937717":{"id":"n1820937717","loc":[-85.1176605,42.0850896]},"n1820937718":{"id":"n1820937718","loc":[-85.1069317,42.0862441]},"n1820937719":{"id":"n1820937719","loc":[-85.2739314,41.9976938]},"n1820937720":{"id":"n1820937720","loc":[-85.5550212,41.9702112]},"n1820937721":{"id":"n1820937721","loc":[-85.3076679,41.9719904]},"n1820937722":{"id":"n1820937722","loc":[-85.592319,41.9440316]},"n1820937723":{"id":"n1820937723","loc":[-85.3139979,41.9704031]},"n1820937724":{"id":"n1820937724","loc":[-85.0421134,42.1013149]},"n1820937725":{"id":"n1820937725","loc":[-85.2508373,42.0102741]},"n1820937726":{"id":"n1820937726","loc":[-85.0830922,42.1038821]},"n1820937727":{"id":"n1820937727","loc":[-85.6342473,41.9360031]},"n1820937730":{"id":"n1820937730","loc":[-85.0500192,42.1024942]},"n1820937731":{"id":"n1820937731","loc":[-85.3498644,41.9926221]},"n1820937732":{"id":"n1820937732","loc":[-85.0234117,42.0918903]},"n1820937733":{"id":"n1820937733","loc":[-85.0464425,42.1009408]},"n1820937734":{"id":"n1820937734","loc":[-85.033938,42.099886]},"n1820937736":{"id":"n1820937736","loc":[-85.0152752,42.0886009]},"n1820937737":{"id":"n1820937737","loc":[-85.0441894,42.1012671]},"n1820937738":{"id":"n1820937738","loc":[-85.4668731,41.9979804]},"n1820937739":{"id":"n1820937739","loc":[-85.4407377,42.006033]},"n1820937740":{"id":"n1820937740","loc":[-85.2262253,42.0344878]},"n1820937741":{"id":"n1820937741","loc":[-85.2550001,42.0033706]},"n1820937742":{"id":"n1820937742","loc":[-85.3071422,41.9722617]},"n1820937743":{"id":"n1820937743","loc":[-85.6147852,41.942228]},"n1820937744":{"id":"n1820937744","loc":[-85.0183853,42.0901825]},"n1820937745":{"id":"n1820937745","loc":[-85.6323161,41.9228489]},"n1820937746":{"id":"n1820937746","loc":[-85.0095568,42.0901376]},"n1820937747":{"id":"n1820937747","loc":[-85.2524037,42.0113826]},"n1820937748":{"id":"n1820937748","loc":[-85.3186864,41.9708578]},"n1820937749":{"id":"n1820937749","loc":[-85.2805669,41.9870883]},"n1820937750":{"id":"n1820937750","loc":[-85.0585768,42.1038144]},"n1820937751":{"id":"n1820937751","loc":[-85.2970786,41.9715358]},"n1820937752":{"id":"n1820937752","loc":[-85.1315758,42.0723445]},"n1820937753":{"id":"n1820937753","loc":[-85.2448291,42.0175444]},"n1820937754":{"id":"n1820937754","loc":[-85.2446468,42.0174248]},"n1820937755":{"id":"n1820937755","loc":[-85.229165,42.032129]},"n1820937756":{"id":"n1820937756","loc":[-85.5612654,41.9724926]},"n1820937757":{"id":"n1820937757","loc":[-85.2331776,42.030854]},"n1820937758":{"id":"n1820937758","loc":[-85.2271909,42.0334519]},"n1820937759":{"id":"n1820937759","loc":[-85.1032396,42.0879214]},"n1820937760":{"id":"n1820937760","loc":[-85.0638447,42.1044154]},"n1820937761":{"id":"n1820937761","loc":[-85.1260706,42.0745556]},"n1820937762":{"id":"n1820937762","loc":[-85.3454485,41.99132]},"n1820937763":{"id":"n1820937763","loc":[-85.2639321,41.9980088]},"n1820937764":{"id":"n1820937764","loc":[-85.0837681,42.1013746]},"n1820937765":{"id":"n1820937765","loc":[-85.2808137,41.9869368]},"n1820937766":{"id":"n1820937766","loc":[-85.6338997,41.9309373]},"n1820937767":{"id":"n1820937767","loc":[-85.2267403,42.0332766]},"n1820937768":{"id":"n1820937768","loc":[-85.0605831,42.1052074]},"n1820937769":{"id":"n1820937769","loc":[-85.0259021,42.0930037]},"n1820937770":{"id":"n1820937770","loc":[-85.232963,42.0313162]},"n1820937771":{"id":"n1820937771","loc":[-85.2404947,42.0125381]},"n1820937772":{"id":"n1820937772","loc":[-85.0910892,42.0935742]},"n1820937773":{"id":"n1820937773","loc":[-85.2554829,42.0019435]},"n1820937774":{"id":"n1820937774","loc":[-85.2799339,41.9867773]},"n1820937775":{"id":"n1820937775","loc":[-85.1075432,42.0852767]},"n1820937776":{"id":"n1820937776","loc":[-85.1176927,42.0854001]},"n1820937777":{"id":"n1820937777","loc":[-85.1067064,42.0863357]},"n1820937778":{"id":"n1820937778","loc":[-85.2517492,42.0106333]},"n1820937779":{"id":"n1820937779","loc":[-85.0987174,42.0909031]},"n1820937780":{"id":"n1820937780","loc":[-85.1160083,42.0863994]},"n1820937781":{"id":"n1820937781","loc":[-85.1268645,42.0739703]},"n1820937782":{"id":"n1820937782","loc":[-85.0454702,42.1002852]},"n1820937783":{"id":"n1820937783","loc":[-85.1334145,42.0705418]},"n1820937784":{"id":"n1820937784","loc":[-85.5866542,41.947431]},"n1820937786":{"id":"n1820937786","loc":[-85.2359886,42.0250366]},"n1820937787":{"id":"n1820937787","loc":[-85.3138048,41.9698527]},"n1820937788":{"id":"n1820937788","loc":[-85.1274291,42.0733081]},"n1820937790":{"id":"n1820937790","loc":[-85.6292905,41.9411267]},"n1820937791":{"id":"n1820937791","loc":[-85.5958809,41.9417333]},"n1820937792":{"id":"n1820937792","loc":[-85.1271019,42.0737581]},"n1820937793":{"id":"n1820937793","loc":[-85.2312679,42.0314437]},"n1820937794":{"id":"n1820937794","loc":[-85.1081387,42.0863516]},"n1820937795":{"id":"n1820937795","loc":[-85.2424473,42.0212109]},"n1820937796":{"id":"n1820937796","loc":[-85.2710654,41.9975236]},"n1820937797":{"id":"n1820937797","loc":[-85.4798408,41.9863223]},"n1820937798":{"id":"n1820937798","loc":[-85.035939,42.104296]},"n1820937799":{"id":"n1820937799","loc":[-85.2178139,42.0395398]},"n1820937800":{"id":"n1820937800","loc":[-85.0630709,42.1042614]},"n1820937801":{"id":"n1820937801","loc":[-85.0440124,42.1014861]},"n1820937802":{"id":"n1820937802","loc":[-85.1321874,42.0720458]},"n1820937804":{"id":"n1820937804","loc":[-85.079427,42.1029121]},"n1820937805":{"id":"n1820937805","loc":[-85.2962632,41.9738968]},"n1820937806":{"id":"n1820937806","loc":[-85.6334748,41.9274627]},"n1820937807":{"id":"n1820937807","loc":[-85.1057341,42.0872804]},"n1820937808":{"id":"n1820937808","loc":[-85.4960169,41.9778263]},"n1820937809":{"id":"n1820937809","loc":[-85.2821226,41.9910273]},"n1820937810":{"id":"n1820937810","loc":[-85.0013868,42.0885054]},"n1820937811":{"id":"n1820937811","loc":[-85.2952547,41.9729795]},"n1820937812":{"id":"n1820937812","loc":[-85.1298375,42.0667842]},"n1820937813":{"id":"n1820937813","loc":[-85.1339201,42.0710025]},"n1820937814":{"id":"n1820937814","loc":[-85.0374356,42.103691]},"n1820937815":{"id":"n1820937815","loc":[-85.0061115,42.0880607]},"n1820937817":{"id":"n1820937817","loc":[-85.2398402,42.0226934]},"n1820937818":{"id":"n1820937818","loc":[-85.123501,42.076236]},"n1820937819":{"id":"n1820937819","loc":[-85.1209489,42.0791294]},"n1820937820":{"id":"n1820937820","loc":[-85.0818624,42.1025778]},"n1820937821":{"id":"n1820937821","loc":[-85.4428835,42.0054749]},"n1820937822":{"id":"n1820937822","loc":[-85.4710359,41.9961147]},"n1820937823":{"id":"n1820937823","loc":[-85.4253354,42.006198]},"n1820937824":{"id":"n1820937824","loc":[-85.5486483,41.9709451]},"n1820937825":{"id":"n1820937825","loc":[-85.2303238,42.0310452]},"n1820937826":{"id":"n1820937826","loc":[-85.6450405,41.9136361]},"n1820937828":{"id":"n1820937828","loc":[-85.2606853,41.9964073]},"n1820937830":{"id":"n1820937830","loc":[-85.097383,42.0911447]},"n1820937831":{"id":"n1820937831","loc":[-85.0498207,42.102136]},"n1820937832":{"id":"n1820937832","loc":[-85.1232435,42.0763793]},"n1820937833":{"id":"n1820937833","loc":[-85.394093,42.0055921]},"n1820937834":{"id":"n1820937834","loc":[-85.3566665,41.9928295]},"n1820937835":{"id":"n1820937835","loc":[-85.3543276,41.9920002]},"n1820937837":{"id":"n1820937837","loc":[-85.084668,42.1034932]},"n1820937838":{"id":"n1820937838","loc":[-85.4400296,42.0060649]},"n1820937839":{"id":"n1820937839","loc":[-85.2362246,42.025714]},"n1820937840":{"id":"n1820937840","loc":[-85.0409225,42.1012791]},"n1820937841":{"id":"n1820937841","loc":[-85.2442283,42.019832]},"n1820937842":{"id":"n1820937842","loc":[-85.1123001,42.084824]},"n1820937843":{"id":"n1820937843","loc":[-85.1603074,42.0638061]},"n1820937844":{"id":"n1820937844","loc":[-85.1359744,42.0650646]},"n1820937845":{"id":"n1820937845","loc":[-85.1757569,42.053849]},"n1820937846":{"id":"n1820937846","loc":[-85.5200925,41.9716686]},"n1820937848":{"id":"n1820937848","loc":[-85.5525322,41.9701315]},"n1820937849":{"id":"n1820937849","loc":[-85.0406489,42.10149]},"n1820937850":{"id":"n1820937850","loc":[-85.0142547,42.088825]},"n1820937851":{"id":"n1820937851","loc":[-85.343749,41.9881884]},"n1820937852":{"id":"n1820937852","loc":[-85.074996,42.1060205]},"n1820937853":{"id":"n1820937853","loc":[-85.2436275,42.0136864]},"n1820937854":{"id":"n1820937854","loc":[-85.2641453,41.9980897]},"n1820937856":{"id":"n1820937856","loc":[-85.2802343,41.9870086]},"n1820937857":{"id":"n1820937857","loc":[-85.0099256,42.0909946]},"n1820937858":{"id":"n1820937858","loc":[-85.493957,41.9786079]},"n1820937859":{"id":"n1820937859","loc":[-85.0739405,42.1059795]},"n1820937860":{"id":"n1820937860","loc":[-85.2331605,42.0301423]},"n1820937862":{"id":"n1820937862","loc":[-85.2035231,42.0438425]},"n1820937863":{"id":"n1820937863","loc":[-85.0884928,42.0986971]},"n1820937864":{"id":"n1820937864","loc":[-85.131597,42.0690142]},"n1820937865":{"id":"n1820937865","loc":[-85.3937454,42.0052677]},"n1820937866":{"id":"n1820937866","loc":[-85.2212729,42.0378561]},"n1820937867":{"id":"n1820937867","loc":[-85.0886068,42.0982421]},"n1820937868":{"id":"n1820937868","loc":[-85.0875004,42.0968064]},"n1820937869":{"id":"n1820937869","loc":[-85.0771323,42.1042642]},"n1820937870":{"id":"n1820937870","loc":[-85.0164554,42.0894887]},"n1820937871":{"id":"n1820937871","loc":[-85.6069102,41.9415577]},"n1820937872":{"id":"n1820937872","loc":[-85.3273875,41.9704908]},"n1820937873":{"id":"n1820937873","loc":[-85.3890891,41.9997983]},"n1820937875":{"id":"n1820937875","loc":[-85.5091276,41.9723705]},"n1820937876":{"id":"n1820937876","loc":[-85.0770626,42.1047696]},"n1820937877":{"id":"n1820937877","loc":[-85.612575,41.9419567]},"n1820937878":{"id":"n1820937878","loc":[-85.3868146,42.0036094]},"n1820937879":{"id":"n1820937879","loc":[-85.2722738,41.9981204]},"n1820937880":{"id":"n1820937880","loc":[-85.3064878,41.9723733]},"n1820937882":{"id":"n1820937882","loc":[-85.1270845,42.0727678]},"n1820937884":{"id":"n1820937884","loc":[-85.3316512,41.97923]},"n1820937885":{"id":"n1820937885","loc":[-85.3932519,42.0042472]},"n1820937886":{"id":"n1820937886","loc":[-85.2457411,42.0175444]},"n1820937887":{"id":"n1820937887","loc":[-85.1397509,42.0648415]},"n1820937891":{"id":"n1820937891","loc":[-85.3196735,41.9719665]},"n1820937892":{"id":"n1820937892","loc":[-85.3372473,41.9845033]},"n1820937894":{"id":"n1820937894","loc":[-85.3254778,41.9719745]},"n1820937897":{"id":"n1820937897","loc":[-85.3185148,41.9691268]},"n1820937899":{"id":"n1820937899","loc":[-85.5419106,41.9714556]},"n1820937901":{"id":"n1820937901","loc":[-85.3293509,41.9748368]},"n1820937903":{"id":"n1820937903","loc":[-85.0798078,42.1028365]},"n1820937905":{"id":"n1820937905","loc":[-85.3954191,42.0056025]},"n1820937909":{"id":"n1820937909","loc":[-85.3417534,41.9857155]},"n1820937913":{"id":"n1820937913","loc":[-84.9927822,42.0857107]},"n1820937915":{"id":"n1820937915","loc":[-85.5444212,41.9712801]},"n1820937917":{"id":"n1820937917","loc":[-85.259088,41.9981682]},"n1820937921":{"id":"n1820937921","loc":[-85.2784576,41.9876358]},"n1820937922":{"id":"n1820937922","loc":[-84.9971918,42.087753]},"n1820937924":{"id":"n1820937924","loc":[-85.5310688,41.966899]},"n1820937928":{"id":"n1820937928","loc":[-85.3766436,41.9979326]},"n1820937930":{"id":"n1820937930","loc":[-85.5494852,41.9704346]},"n1820937933":{"id":"n1820937933","loc":[-85.5548281,41.9695412]},"n1820937935":{"id":"n1820937935","loc":[-85.0768588,42.105088]},"n1820937937":{"id":"n1820937937","loc":[-85.2646885,41.9978054]},"n1820937939":{"id":"n1820937939","loc":[-85.2441532,42.0176082]},"n1820937941":{"id":"n1820937941","loc":[-85.105553,42.0877928]},"n1820937943":{"id":"n1820937943","loc":[-85.0879457,42.0958909]},"n1820937944":{"id":"n1820937944","loc":[-85.3187015,41.9704402]},"n1820937945":{"id":"n1820937945","loc":[-85.5624456,41.970626]},"n1820937946":{"id":"n1820937946","loc":[-85.0580176,42.1028644]},"n1820937948":{"id":"n1820937948","loc":[-85.3016061,41.9726286]},"n1820937949":{"id":"n1820937949","loc":[-85.4310388,42.0069418]},"n1820937950":{"id":"n1820937950","loc":[-85.2945144,41.9740723]},"n1820937951":{"id":"n1820937951","loc":[-85.1170222,42.082657]},"n1820937952":{"id":"n1820937952","loc":[-85.0864503,42.0947632]},"n1820937953":{"id":"n1820937953","loc":[-85.4285926,42.0059533]},"n1820937970":{"id":"n1820937970","loc":[-85.3629965,41.9938023]},"n1820937972":{"id":"n1820937972","loc":[-85.2438099,42.0199755]},"n1820937974":{"id":"n1820937974","loc":[-85.1327654,42.0699285]},"n1820937977":{"id":"n1820937977","loc":[-85.1515956,42.0611935]},"n1820937978":{"id":"n1820937978","loc":[-85.0107369,42.0896638]},"n1820937979":{"id":"n1820937979","loc":[-85.1152626,42.0862083]},"n1820937980":{"id":"n1820937980","loc":[-85.4531831,42.0062881]},"n1820937981":{"id":"n1820937981","loc":[-85.0341473,42.0985924]},"n1820937982":{"id":"n1820937982","loc":[-85.0877485,42.0960171]},"n1820937983":{"id":"n1820937983","loc":[-85.2756373,41.9951742]},"n1820937984":{"id":"n1820937984","loc":[-85.2965421,41.9714401]},"n1820937985":{"id":"n1820937985","loc":[-85.2409775,42.0226934]},"n1820937986":{"id":"n1820937986","loc":[-85.0170723,42.0900579]},"n1820937987":{"id":"n1820937987","loc":[-85.1034663,42.0880555]},"n1820937988":{"id":"n1820937988","loc":[-85.0585071,42.1031577]},"n1820937990":{"id":"n1820937990","loc":[-85.0819174,42.1032373]},"n1820937992":{"id":"n1820937992","loc":[-85.0546608,42.1030542]},"n1820937993":{"id":"n1820937993","loc":[-85.0100811,42.0906125]},"n1820937995":{"id":"n1820937995","loc":[-85.6304278,41.9432655]},"n1820937997":{"id":"n1820937997","loc":[-85.0255628,42.092778]},"n1820938011":{"id":"n1820938011","loc":[-85.2316756,42.0317146]},"n1820938012":{"id":"n1820938012","loc":[-85.4067917,42.008042]},"n1820938013":{"id":"n1820938013","loc":[-85.390398,42.0028759]},"n1820938014":{"id":"n1820938014","loc":[-85.0161604,42.0886527]},"n1820938015":{"id":"n1820938015","loc":[-85.125337,42.0744589]},"n1820938016":{"id":"n1820938016","loc":[-85.2151317,42.0404801]},"n1820938017":{"id":"n1820938017","loc":[-85.3165085,41.9706025]},"n1820938018":{"id":"n1820938018","loc":[-85.5641193,41.9640688]},"n1820938019":{"id":"n1820938019","loc":[-85.147583,42.0642203]},"n1820938022":{"id":"n1820938022","loc":[-85.2803781,41.9947886]},"n1820938024":{"id":"n1820938024","loc":[-85.2692469,41.9982053]},"n1820938026":{"id":"n1820938026","loc":[-85.4321975,42.0067505]},"n1820938028":{"id":"n1820938028","loc":[-85.572535,41.9633405]},"n1820938030":{"id":"n1820938030","loc":[-85.3237505,41.9716475]},"n1820938032":{"id":"n1820938032","loc":[-85.6487698,41.9141583]},"n1820938033":{"id":"n1820938033","loc":[-85.0526371,42.1038315]},"n1820938034":{"id":"n1820938034","loc":[-85.088069,42.0978731]},"n1820938035":{"id":"n1820938035","loc":[-85.2516312,42.0102267]},"n1820938039":{"id":"n1820938039","loc":[-85.2731374,41.9982958]},"n1820938040":{"id":"n1820938040","loc":[-85.5453224,41.9713439]},"n1820938041":{"id":"n1820938041","loc":[-85.4480548,42.0049647]},"n1820938043":{"id":"n1820938043","loc":[-85.2504081,42.010322]},"n1820938045":{"id":"n1820938045","loc":[-85.2663447,41.99919]},"n1820938046":{"id":"n1820938046","loc":[-85.0507287,42.102907]},"n1820938047":{"id":"n1820938047","loc":[-85.0408246,42.1024743]},"n1820938048":{"id":"n1820938048","loc":[-85.2796335,41.9866099]},"n1820938050":{"id":"n1820938050","loc":[-85.452475,42.0061127]},"n1820938051":{"id":"n1820938051","loc":[-85.2410569,42.0128147]},"n1820938052":{"id":"n1820938052","loc":[-85.0413302,42.1011477]},"n1820938053":{"id":"n1820938053","loc":[-85.6327409,41.9197627]},"n1820938056":{"id":"n1820938056","loc":[-85.1072039,42.0857994]},"n1820938057":{"id":"n1820938057","loc":[-85.2001114,42.0448145]},"n1820938058":{"id":"n1820938058","loc":[-85.2655347,41.9978186]},"n1820938059":{"id":"n1820938059","loc":[-85.2330918,42.0304874]},"n1820938060":{"id":"n1820938060","loc":[-85.2601113,41.9966545]},"n1820938061":{"id":"n1820938061","loc":[-85.5397863,41.9708494]},"n1820938062":{"id":"n1820938062","loc":[-85.2702085,41.9977217]},"n1820938063":{"id":"n1820938063","loc":[-85.2219982,42.03699]},"n1820938064":{"id":"n1820938064","loc":[-85.0668957,42.105121]},"n1820938065":{"id":"n1820938065","loc":[-85.2328665,42.0270769]},"n1820938066":{"id":"n1820938066","loc":[-85.3189654,41.9694778]},"n1820938067":{"id":"n1820938067","loc":[-85.3814115,42.0022915]},"n1820938068":{"id":"n1820938068","loc":[-85.2759108,41.9956008]},"n1820938069":{"id":"n1820938069","loc":[-85.0391938,42.1034853]},"n1820938070":{"id":"n1820938070","loc":[-85.2850623,41.9810353]},"n1820938071":{"id":"n1820938071","loc":[-85.538074,41.970855]},"n1820938073":{"id":"n1820938073","loc":[-85.1319661,42.0670932]},"n1820938074":{"id":"n1820938074","loc":[-85.2816763,41.9913678]},"n1820938075":{"id":"n1820938075","loc":[-85.3182144,41.9700282]},"n1820938076":{"id":"n1820938076","loc":[-85.5909028,41.9458989]},"n1820938077":{"id":"n1820938077","loc":[-85.4057617,42.0074361]},"n1820938078":{"id":"n1820938078","loc":[-85.2620438,41.9967729]},"n1820938079":{"id":"n1820938079","loc":[-85.1122143,42.0851107]},"n1820938080":{"id":"n1820938080","loc":[-85.2443785,42.0174567]},"n1820938081":{"id":"n1820938081","loc":[-85.0319733,42.0953853]},"n1820938082":{"id":"n1820938082","loc":[-85.0878276,42.09443]},"n1820938083":{"id":"n1820938083","loc":[-85.0271789,42.0935809]},"n1820938084":{"id":"n1820938084","loc":[-85.0326399,42.0974222]},"n1820938085":{"id":"n1820938085","loc":[-85.3989167,42.0065592]},"n1820938086":{"id":"n1820938086","loc":[-85.3263361,41.9721261]},"n1820938087":{"id":"n1820938087","loc":[-85.2547855,42.0037134]},"n1820938088":{"id":"n1820938088","loc":[-85.4373259,42.005746]},"n1820938089":{"id":"n1820938089","loc":[-85.3094275,41.9699245]},"n1820938090":{"id":"n1820938090","loc":[-85.2783246,41.9872793]},"n1820938092":{"id":"n1820938092","loc":[-85.0815633,42.1025169]},"n1820938093":{"id":"n1820938093","loc":[-85.1788511,42.0522134]},"n1820938095":{"id":"n1820938095","loc":[-85.2830345,41.9816733]},"n1820938096":{"id":"n1820938096","loc":[-85.0744984,42.1059835]},"n1820938097":{"id":"n1820938097","loc":[-85.2788396,41.9879333]},"n1820938098":{"id":"n1820938098","loc":[-85.3640093,41.9946531]},"n1820938099":{"id":"n1820938099","loc":[-85.291167,41.9787463]},"n1820938100":{"id":"n1820938100","loc":[-85.0772436,42.1038156]},"n1820938101":{"id":"n1820938101","loc":[-85.00563,42.0887482]},"n1820938102":{"id":"n1820938102","loc":[-85.0326881,42.0961245]},"n1820938104":{"id":"n1820938104","loc":[-85.0530448,42.1038634]},"n1820938105":{"id":"n1820938105","loc":[-85.2625266,41.9970639]},"n1820938106":{"id":"n1820938106","loc":[-85.2827556,41.9823512]},"n1820938107":{"id":"n1820938107","loc":[-85.2784319,41.9910752]},"n1820938108":{"id":"n1820938108","loc":[-85.0882099,42.094393]},"n1820938109":{"id":"n1820938109","loc":[-85.5718484,41.9645371]},"n1820938110":{"id":"n1820938110","loc":[-85.2559764,42.0099317]},"n1820938111":{"id":"n1820938111","loc":[-85.2969284,41.973179]},"n1820938113":{"id":"n1820938113","loc":[-85.3875055,42.0019726]},"n1820938114":{"id":"n1820938114","loc":[-85.4250779,42.0068199]},"n1820938115":{"id":"n1820938115","loc":[-85.0645367,42.104889]},"n1820938116":{"id":"n1820938116","loc":[-85.1636762,42.0623724]},"n1820938117":{"id":"n1820938117","loc":[-85.0757322,42.1055935]},"n1820938118":{"id":"n1820938118","loc":[-85.3695197,41.9981559]},"n1820938120":{"id":"n1820938120","loc":[-85.1297516,42.0671027]},"n1820938121":{"id":"n1820938121","loc":[-85.1057448,42.0875551]},"n1820938122":{"id":"n1820938122","loc":[-85.2805175,41.9943182]},"n1820938123":{"id":"n1820938123","loc":[-85.2545173,42.0040722]},"n1820938124":{"id":"n1820938124","loc":[-84.9966607,42.0871319]},"n1820938125":{"id":"n1820938125","loc":[-85.0099899,42.0904612]},"n1820938126":{"id":"n1820938126","loc":[-85.2489919,42.0091102]},"n1820938127":{"id":"n1820938127","loc":[-85.0342706,42.0979476]},"n1820938128":{"id":"n1820938128","loc":[-85.1080891,42.0855884]},"n1820938129":{"id":"n1820938129","loc":[-85.0128183,42.0905356]},"n1820938130":{"id":"n1820938130","loc":[-85.631608,41.9434251]},"n1820938131":{"id":"n1820938131","loc":[-85.2551975,42.0008524]},"n1820938132":{"id":"n1820938132","loc":[-85.6421823,41.9096233]},"n1820938133":{"id":"n1820938133","loc":[-85.0125059,42.0906284]},"n1820938134":{"id":"n1820938134","loc":[-85.5499358,41.9701793]},"n1820938135":{"id":"n1820938135","loc":[-85.5472107,41.9712323]},"n1820938136":{"id":"n1820938136","loc":[-85.2760758,41.9958691]},"n1820938137":{"id":"n1820938137","loc":[-85.276678,41.9960433]},"n1820938138":{"id":"n1820938138","loc":[-85.0570319,42.1024731]},"n1820938140":{"id":"n1820938140","loc":[-85.2394325,42.0227492]},"n1820938142":{"id":"n1820938142","loc":[-85.5666341,41.9638829]},"n1820938144":{"id":"n1820938144","loc":[-85.258101,41.9996353]},"n1820938147":{"id":"n1820938147","loc":[-85.2129645,42.0413565]},"n1820938149":{"id":"n1820938149","loc":[-84.9962369,42.0868373]},"n1820938151":{"id":"n1820938151","loc":[-85.2570386,42.0084968]},"n1820938153":{"id":"n1820938153","loc":[-85.3971142,42.0050285]},"n1820938155":{"id":"n1820938155","loc":[-85.1072093,42.0855566]},"n1820938157":{"id":"n1820938157","loc":[-85.2840323,41.9920959]},"n1820938159":{"id":"n1820938159","loc":[-85.1187924,42.0816458]},"n1820938161":{"id":"n1820938161","loc":[-85.2681324,41.9985788]},"n1820938163":{"id":"n1820938163","loc":[-85.0887034,42.0984969]},"n1820938165":{"id":"n1820938165","loc":[-85.4133405,42.0073141]},"n1820938166":{"id":"n1820938166","loc":[-85.0097445,42.0902888]},"n1820938167":{"id":"n1820938167","loc":[-85.0828133,42.1037388]},"n1820938168":{"id":"n1820938168","loc":[-85.0549599,42.1030833]},"n1820938169":{"id":"n1820938169","loc":[-85.4571528,42.0010421]},"n1820938178":{"id":"n1820938178","loc":[-85.2706644,41.9975941]},"n1820938180":{"id":"n1820938180","loc":[-85.2258606,42.0335794]},"n1820938182":{"id":"n1820938182","loc":[-85.2832276,41.9814659]},"n1820938184":{"id":"n1820938184","loc":[-85.1082299,42.0860928]},"n1820938185":{"id":"n1820938185","loc":[-85.3839392,42.0022381]},"n1820938186":{"id":"n1820938186","loc":[-85.2772131,41.995905]},"n1820938187":{"id":"n1820938187","loc":[-85.1044895,42.0879214]},"n1820938188":{"id":"n1820938188","loc":[-85.2135267,42.0407087]},"n1820938189":{"id":"n1820938189","loc":[-85.2543993,42.0044628]},"n1820938190":{"id":"n1820938190","loc":[-85.1501793,42.0617351]},"n1820938191":{"id":"n1820938191","loc":[-85.3350587,41.9820469]},"n1820938192":{"id":"n1820938192","loc":[-85.1350731,42.0655735]},"n1820938193":{"id":"n1820938193","loc":[-85.0404008,42.1028843]},"n1820938194":{"id":"n1820938194","loc":[-85.6323161,41.943042]},"n1820938195":{"id":"n1820938195","loc":[-85.1259593,42.0742837]},"n1820938196":{"id":"n1820938196","loc":[-85.4562988,42.0033758]},"n1820938197":{"id":"n1820938197","loc":[-85.256824,42.0056826]},"n1820938198":{"id":"n1820938198","loc":[-85.2742103,41.9963862]},"n1820938199":{"id":"n1820938199","loc":[-85.0380888,42.1037877]},"n1820938200":{"id":"n1820938200","loc":[-85.47404,41.9944721]},"n1820938201":{"id":"n1820938201","loc":[-85.103021,42.087948]},"n1820938202":{"id":"n1820938202","loc":[-85.4030151,42.0065113]},"n1820938203":{"id":"n1820938203","loc":[-85.2113981,42.040735]},"n1820938204":{"id":"n1820938204","loc":[-85.2603433,41.9965137]},"n1820938206":{"id":"n1820938206","loc":[-85.1669378,42.0607634]},"n1820938207":{"id":"n1820938207","loc":[-85.0642027,42.1046076]},"n1820938208":{"id":"n1820938208","loc":[-85.2812428,41.9915696]},"n1820938209":{"id":"n1820938209","loc":[-85.0839559,42.1038343]},"n1820938210":{"id":"n1820938210","loc":[-85.1239946,42.0769368]},"n1820938211":{"id":"n1820938211","loc":[-85.2311177,42.0283042]},"n1820938212":{"id":"n1820938212","loc":[-85.2791614,41.9882682]},"n1820938213":{"id":"n1820938213","loc":[-85.2674941,41.9987582]},"n1820938214":{"id":"n1820938214","loc":[-85.352787,41.9919579]},"n1820938215":{"id":"n1820938215","loc":[-85.0874146,42.0952182]},"n1820938216":{"id":"n1820938216","loc":[-85.0069711,42.0877092]},"n1820938217":{"id":"n1820938217","loc":[-85.2059049,42.0404004]},"n1820938218":{"id":"n1820938218","loc":[-85.2403552,42.0227332]},"n1820938219":{"id":"n1820938219","loc":[-85.2492923,42.0098915]},"n1820938220":{"id":"n1820938220","loc":[-85.269778,41.9979541]},"n1820938221":{"id":"n1820938221","loc":[-85.2097673,42.0389024]},"n1820938222":{"id":"n1820938222","loc":[-85.0845942,42.1032015]},"n1820938223":{"id":"n1820938223","loc":[-84.993206,42.0858142]},"n1820938224":{"id":"n1820938224","loc":[-85.2108187,42.0402729]},"n1820938225":{"id":"n1820938225","loc":[-84.9893959,42.0873043]},"n1820938226":{"id":"n1820938226","loc":[-85.2952332,41.9719984]},"n1820938227":{"id":"n1820938227","loc":[-85.4100961,42.0081536]},"n1820938228":{"id":"n1820938228","loc":[-85.3299088,41.9785696]},"n1820938229":{"id":"n1820938229","loc":[-85.2258176,42.0340097]},"n1820938230":{"id":"n1820938230","loc":[-85.3146739,41.9711449]},"n1820938231":{"id":"n1820938231","loc":[-85.5447645,41.9712801]},"n1820938232":{"id":"n1820938232","loc":[-85.5510087,41.9705941]},"n1820938233":{"id":"n1820938233","loc":[-85.5122389,41.9703445]},"n1820938234":{"id":"n1820938234","loc":[-85.2792687,41.9865381]},"n1820938235":{"id":"n1820938235","loc":[-85.1475229,42.0630151]},"n1820938237":{"id":"n1820938237","loc":[-85.0332889,42.0996034]},"n1820938238":{"id":"n1820938238","loc":[-85.2588882,41.9986877]},"n1820938239":{"id":"n1820938239","loc":[-85.0656458,42.1050892]},"n1820938240":{"id":"n1820938240","loc":[-84.9913915,42.086098]},"n1820938241":{"id":"n1820938241","loc":[-85.4752416,41.9944402]},"n1820938242":{"id":"n1820938242","loc":[-85.1214304,42.0791147]},"n1820938243":{"id":"n1820938243","loc":[-85.0075183,42.0886925]},"n1820938244":{"id":"n1820938244","loc":[-85.1052888,42.0872087]},"n1820938245":{"id":"n1820938245","loc":[-85.3104252,41.9703393]},"n1820938246":{"id":"n1820938246","loc":[-85.232109,42.0318158]},"n1820938247":{"id":"n1820938247","loc":[-85.0756075,42.1059528]},"n1820938248":{"id":"n1820938248","loc":[-85.0075612,42.0890866]},"n1820938249":{"id":"n1820938249","loc":[-85.1013312,42.0897474]},"n1820938250":{"id":"n1820938250","loc":[-85.1168076,42.0828919]},"n1820938251":{"id":"n1820938251","loc":[-85.2951367,41.9723334]},"n1820938252":{"id":"n1820938252","loc":[-85.0879363,42.0976053]},"n1820938253":{"id":"n1820938253","loc":[-85.0354763,42.1021838]},"n1820938254":{"id":"n1820938254","loc":[-85.2379627,42.0236339]},"n1820938255":{"id":"n1820938255","loc":[-85.1308245,42.0685364]},"n1820938256":{"id":"n1820938256","loc":[-85.0914446,42.0934774]},"n1820938257":{"id":"n1820938257","loc":[-85.2436812,42.014069]},"n1820938258":{"id":"n1820938258","loc":[-85.0682529,42.1056106]},"n1820938259":{"id":"n1820938259","loc":[-85.290652,41.9766805]},"n1820938260":{"id":"n1820938260","loc":[-85.0133494,42.0897434]},"n1820938261":{"id":"n1820938261","loc":[-85.2753047,41.9949429]},"n1820938262":{"id":"n1820938262","loc":[-85.0314691,42.0950788]},"n1820938263":{"id":"n1820938263","loc":[-85.3444786,41.9908359]},"n1820938264":{"id":"n1820938264","loc":[-85.0443115,42.1009061]},"n1820938265":{"id":"n1820938265","loc":[-85.0634853,42.1043159]},"n1820938267":{"id":"n1820938267","loc":[-85.3978223,42.0053952]},"n1820938268":{"id":"n1820938268","loc":[-85.0228659,42.0911885]},"n1820938269":{"id":"n1820938269","loc":[-85.0220237,42.0906272]},"n1820938270":{"id":"n1820938270","loc":[-85.1061525,42.0863369]},"n1820938271":{"id":"n1820938271","loc":[-85.2382309,42.0233708]},"n1820938272":{"id":"n1820938272","loc":[-85.310672,41.9702755]},"n1820938273":{"id":"n1820938273","loc":[-85.1448192,42.0652613]},"n1820938274":{"id":"n1820938274","loc":[-85.6036057,41.9403766]},"n1820938275":{"id":"n1820938275","loc":[-85.0778941,42.1032413]},"n1820938276":{"id":"n1820938276","loc":[-85.1279374,42.0723974]},"n1820938277":{"id":"n1820938277","loc":[-85.2806635,41.9847836]},"n1820938278":{"id":"n1820938278","loc":[-85.2653201,41.9976352]},"n1820938279":{"id":"n1820938279","loc":[-85.0351665,42.1001805]},"n1820938280":{"id":"n1820938280","loc":[-85.0718269,42.1056253]},"n1820938281":{"id":"n1820938281","loc":[-85.2574248,42.0075322]},"n1820938282":{"id":"n1820938282","loc":[-85.126666,42.0740778]},"n1820938283":{"id":"n1820938283","loc":[-85.077705,42.1034733]},"n1820938284":{"id":"n1820938284","loc":[-85.3535552,41.9919045]},"n1820938286":{"id":"n1820938286","loc":[-85.2810711,41.9866657]},"n1820938287":{"id":"n1820938287","loc":[-85.4567494,42.0019885]},"n1820938288":{"id":"n1820938288","loc":[-85.2642419,41.9992936]},"n1820938289":{"id":"n1820938289","loc":[-85.2643344,41.9980925]},"n1820938290":{"id":"n1820938290","loc":[-85.3270335,41.9776125]},"n1820938291":{"id":"n1820938291","loc":[-85.1200584,42.0795077]},"n1820938292":{"id":"n1820938292","loc":[-85.2290792,42.0340256]},"n1820938293":{"id":"n1820938293","loc":[-85.6015887,41.9401372]},"n1820938294":{"id":"n1820938294","loc":[-85.5370869,41.970488]},"n1820938295":{"id":"n1820938295","loc":[-85.3108866,41.9698048]},"n1820938297":{"id":"n1820938297","loc":[-85.1556511,42.0628184]},"n1820938298":{"id":"n1820938298","loc":[-85.0027922,42.0875221]},"n1820938300":{"id":"n1820938300","loc":[-85.3873338,42.0040614]},"n1820938301":{"id":"n1820938301","loc":[-85.0350753,42.1004034]},"n1820938302":{"id":"n1820938302","loc":[-85.6239476,41.9411906]},"n1820938304":{"id":"n1820938304","loc":[-85.0118246,42.0897964]},"n1820938306":{"id":"n1820938306","loc":[-85.4796877,41.995275]},"n1820938307":{"id":"n1820938307","loc":[-85.5388636,41.9707856]},"n1820938309":{"id":"n1820938309","loc":[-85.2971902,41.9727773]},"n1820938310":{"id":"n1820938310","loc":[-85.5426831,41.9715513]},"n1820938311":{"id":"n1820938311","loc":[-85.2798373,41.9836671]},"n1820938312":{"id":"n1820938312","loc":[-85.2432198,42.0104017]},"n1820938313":{"id":"n1820938313","loc":[-85.2650412,41.9987554]},"n1820938317":{"id":"n1820938317","loc":[-85.0015423,42.0882386]},"n1820938318":{"id":"n1820938318","loc":[-85.1409783,42.064879]},"n1820938319":{"id":"n1820938319","loc":[-85.1691908,42.058995]},"n1820938320":{"id":"n1820938320","loc":[-85.1059165,42.0864882]},"n1820938321":{"id":"n1820938321","loc":[-85.3664941,41.9965771]},"n1820938323":{"id":"n1820938323","loc":[-85.3143198,41.9710971]},"n1820938324":{"id":"n1820938324","loc":[-85.0016067,42.0880675]},"n1820938325":{"id":"n1820938325","loc":[-85.0148139,42.0887164]},"n1820938326":{"id":"n1820938326","loc":[-85.0324682,42.0959056]},"n1820938327":{"id":"n1820938327","loc":[-85.0898661,42.0939921]},"n1820938328":{"id":"n1820938328","loc":[-85.2556427,42.0004936]},"n1820938329":{"id":"n1820938329","loc":[-85.6287112,41.9407437]},"n1820938330":{"id":"n1820938330","loc":[-84.9913392,42.0866701]},"n1820938331":{"id":"n1820938331","loc":[-85.2685777,41.9984632]},"n1820938332":{"id":"n1820938332","loc":[-85.0078884,42.0901614]},"n1820938333":{"id":"n1820938333","loc":[-84.999642,42.0878616]},"n1820938334":{"id":"n1820938334","loc":[-85.0188909,42.0899186]},"n1820938335":{"id":"n1820938335","loc":[-85.2830238,41.9819843]},"n1820938336":{"id":"n1820938336","loc":[-85.2491421,42.0096204]},"n1820938337":{"id":"n1820938337","loc":[-85.0585701,42.1034295]},"n1820938338":{"id":"n1820938338","loc":[-85.0651965,42.1051636]},"n1820938339":{"id":"n1820938339","loc":[-85.0583944,42.104292]},"n1820938340":{"id":"n1820938340","loc":[-85.119876,42.0801567]},"n1820938341":{"id":"n1820938341","loc":[-85.0943937,42.0931323]},"n1820938342":{"id":"n1820938342","loc":[-85.1504583,42.0613209]},"n1820938343":{"id":"n1820938343","loc":[-85.0425426,42.1019836]},"n1820938345":{"id":"n1820938345","loc":[-84.9991391,42.0878206]},"n1820938346":{"id":"n1820938346","loc":[-85.2563841,42.0094614]},"n1820938347":{"id":"n1820938347","loc":[-85.0515387,42.103297]},"n1820938348":{"id":"n1820938348","loc":[-85.0857261,42.1003636]},"n1820938349":{"id":"n1820938349","loc":[-85.078971,42.1029241]},"n1820938350":{"id":"n1820938350","loc":[-85.5699558,41.958931]},"n1820938351":{"id":"n1820938351","loc":[-85.3181285,41.9696533]},"n1820938352":{"id":"n1820938352","loc":[-85.5998506,41.9402329]},"n1820938353":{"id":"n1820938353","loc":[-85.2567277,42.000317]},"n1820938354":{"id":"n1820938354","loc":[-85.3082795,41.9708338]},"n1820938355":{"id":"n1820938355","loc":[-85.3127856,41.9692784]},"n1820938356":{"id":"n1820938356","loc":[-85.0340775,42.1010721]},"n1820938357":{"id":"n1820938357","loc":[-85.3158111,41.9706583]},"n1820938359":{"id":"n1820938359","loc":[-85.2312035,42.0280412]},"n1820938360":{"id":"n1820938360","loc":[-85.2448613,42.018477]},"n1820938361":{"id":"n1820938361","loc":[-85.29077,41.9759068]},"n1820938364":{"id":"n1820938364","loc":[-85.3677387,41.9976615]},"n1820938365":{"id":"n1820938365","loc":[-85.0785204,42.1030355]},"n1820938366":{"id":"n1820938366","loc":[-85.2262039,42.0333722]},"n1820938367":{"id":"n1820938367","loc":[-85.1226011,42.0780902]},"n1820938368":{"id":"n1820938368","loc":[-85.3229673,41.971129]},"n1820938369":{"id":"n1820938369","loc":[-85.385334,42.0000056]},"n1820938370":{"id":"n1820938370","loc":[-85.000098,42.0879094]},"n1820938372":{"id":"n1820938372","loc":[-85.3852481,42.0025091]},"n1820938373":{"id":"n1820938373","loc":[-85.3770513,41.9982515]},"n1820938374":{"id":"n1820938374","loc":[-85.6278314,41.9405362]},"n1820938375":{"id":"n1820938375","loc":[-85.6355133,41.9344068]},"n1820938376":{"id":"n1820938376","loc":[-85.635642,41.9324753]},"n1820938377":{"id":"n1820938377","loc":[-85.3154463,41.970778]},"n1820938378":{"id":"n1820938378","loc":[-85.0920334,42.093411]},"n1820938379":{"id":"n1820938379","loc":[-85.3269155,41.9722297]},"n1820938381":{"id":"n1820938381","loc":[-85.1134334,42.0849184]},"n1820938382":{"id":"n1820938382","loc":[-85.005968,42.088585]},"n1820938384":{"id":"n1820938384","loc":[-85.1245203,42.0757183]},"n1820938385":{"id":"n1820938385","loc":[-85.020704,42.0905396]},"n1820938386":{"id":"n1820938386","loc":[-85.119585,42.0808984]},"n1820938387":{"id":"n1820938387","loc":[-85.0072447,42.0880117]},"n1820938388":{"id":"n1820938388","loc":[-85.2742908,41.9960273]},"n1820938389":{"id":"n1820938389","loc":[-85.3275807,41.9696852]},"n1820938390":{"id":"n1820938390","loc":[-85.2385635,42.0231556]},"n1820938392":{"id":"n1820938392","loc":[-85.0202856,42.0900778]},"n1820938393":{"id":"n1820938393","loc":[-85.2067847,42.0395398]},"n1820938394":{"id":"n1820938394","loc":[-85.5183544,41.9713495]},"n1820938396":{"id":"n1820938396","loc":[-85.5073037,41.9736787]},"n1820938397":{"id":"n1820938397","loc":[-85.2519638,42.0114225]},"n1820938398":{"id":"n1820938398","loc":[-85.287487,41.9793285]},"n1820938399":{"id":"n1820938399","loc":[-85.2298088,42.0336431]},"n1820938400":{"id":"n1820938400","loc":[-85.229444,42.0339141]},"n1820938401":{"id":"n1820938401","loc":[-85.2421791,42.0220239]},"n1820938402":{"id":"n1820938402","loc":[-85.2976687,41.9737612]},"n1820938403":{"id":"n1820938403","loc":[-85.3622069,41.993473]},"n1820938404":{"id":"n1820938404","loc":[-85.2465458,42.014906]},"n1820938405":{"id":"n1820938405","loc":[-85.5724663,41.9639412]},"n1820938406":{"id":"n1820938406","loc":[-85.3708501,41.9982037]},"n1820938408":{"id":"n1820938408","loc":[-85.2564592,42.0055311]},"n1820938409":{"id":"n1820938409","loc":[-85.1192846,42.0810856]},"n1820938410":{"id":"n1820938410","loc":[-85.5623812,41.971663]},"n1820938411":{"id":"n1820938411","loc":[-85.3221948,41.9719665]},"n1820938412":{"id":"n1820938412","loc":[-85.5168738,41.9710305]},"n1820938413":{"id":"n1820938413","loc":[-85.4546852,42.0061127]},"n1820938414":{"id":"n1820938414","loc":[-85.5896153,41.9463617]},"n1820938415":{"id":"n1820938415","loc":[-85.2978189,41.9722138]},"n1820938416":{"id":"n1820938416","loc":[-85.1021681,42.0883581]},"n1820938417":{"id":"n1820938417","loc":[-85.2797193,41.9912984]},"n1820938419":{"id":"n1820938419","loc":[-85.2362461,42.0248533]},"n1820938420":{"id":"n1820938420","loc":[-85.4833639,41.9846252]},"n1820938422":{"id":"n1820938422","loc":[-85.3281064,41.9689433]},"n1820938424":{"id":"n1820938424","loc":[-85.2416963,42.0130088]},"n1820938425":{"id":"n1820938425","loc":[-85.5718655,41.9564577]},"n1820938426":{"id":"n1820938426","loc":[-85.0512812,42.1030701]},"n1820938427":{"id":"n1820938427","loc":[-85.1273527,42.0723616]},"n1820938428":{"id":"n1820938428","loc":[-85.0215033,42.0904083]},"n1820938429":{"id":"n1820938429","loc":[-85.6169953,41.942228]},"n1820938430":{"id":"n1820938430","loc":[-85.2829165,41.9907243]},"n1820938431":{"id":"n1820938431","loc":[-85.2240796,42.0374203]},"n1820938432":{"id":"n1820938432","loc":[-85.0167598,42.0898442]},"n1820938433":{"id":"n1820938433","loc":[-85.2132649,42.0411334]},"n1820938434":{"id":"n1820938434","loc":[-85.2293839,42.031513]},"n1820938435":{"id":"n1820938435","loc":[-85.1203374,42.0792608]},"n1820938436":{"id":"n1820938436","loc":[-85.109571,42.086268]},"n1820938437":{"id":"n1820938437","loc":[-85.1079026,42.0853842]},"n1820938438":{"id":"n1820938438","loc":[-85.109237,42.0862413]},"n1820938439":{"id":"n1820938439","loc":[-85.2259936,42.0350831]},"n1820938440":{"id":"n1820938440","loc":[-85.3669705,41.99679]},"n1820938441":{"id":"n1820938441","loc":[-85.2418143,42.0223507]},"n1820938442":{"id":"n1820938442","loc":[-85.3101248,41.9702515]},"n1820938443":{"id":"n1820938443","loc":[-85.069315,42.1059688]},"n1820938444":{"id":"n1820938444","loc":[-85.205862,42.0410378]},"n1820938445":{"id":"n1820938445","loc":[-85.0388076,42.1036604]},"n1820938446":{"id":"n1820938446","loc":[-85.2225389,42.0370115]},"n1820938447":{"id":"n1820938447","loc":[-85.3241474,41.9719346]},"n1820938448":{"id":"n1820938448","loc":[-85.3125496,41.9690789]},"n1820938449":{"id":"n1820938449","loc":[-85.1146497,42.0857039]},"n1820938450":{"id":"n1820938450","loc":[-85.1333944,42.0714963]},"n1820938451":{"id":"n1820938451","loc":[-85.5619306,41.9720937]},"n1820938452":{"id":"n1820938452","loc":[-85.2553651,42.0006479]},"n1820938453":{"id":"n1820938453","loc":[-85.3151137,41.9710093]},"n1820938454":{"id":"n1820938454","loc":[-85.2592315,41.9977947]},"n1820938455":{"id":"n1820938455","loc":[-85.2655723,41.9995966]},"n1820938456":{"id":"n1820938456","loc":[-85.4820652,41.9959233]},"n1820938459":{"id":"n1820938459","loc":[-85.450737,42.0055068]},"n1820938460":{"id":"n1820938460","loc":[-85.2428658,42.0205573]},"n1820938461":{"id":"n1820938461","loc":[-85.0835576,42.1021559]},"n1820938462":{"id":"n1820938462","loc":[-85.244636,42.0194733]},"n1820938463":{"id":"n1820938463","loc":[-85.5702562,41.9581332]},"n1820938465":{"id":"n1820938465","loc":[-85.5680031,41.9659515]},"n1820938467":{"id":"n1820938467","loc":[-85.2798752,41.9948353]},"n1820938468":{"id":"n1820938468","loc":[-85.0477407,42.1015537]},"n1820938469":{"id":"n1820938469","loc":[-85.6403842,41.913732]},"n1820938470":{"id":"n1820938470","loc":[-85.0396029,42.103289]},"n1820938471":{"id":"n1820938471","loc":[-85.2824702,41.9907777]},"n1820938472":{"id":"n1820938472","loc":[-85.2969284,41.9735538]},"n1820938474":{"id":"n1820938474","loc":[-85.401041,42.0070853]},"n1820938475":{"id":"n1820938475","loc":[-85.4116625,42.0073883]},"n1820938476":{"id":"n1820938476","loc":[-85.0437764,42.1016214]},"n1820938477":{"id":"n1820938477","loc":[-85.3643269,41.9958436]},"n1820938478":{"id":"n1820938478","loc":[-85.3895182,42.0009465]},"n1820938479":{"id":"n1820938479","loc":[-85.636157,41.9333373]},"n1820938480":{"id":"n1820938480","loc":[-85.2811355,41.9858044]},"n1820938481":{"id":"n1820938481","loc":[-85.0239052,42.092153]},"n1820938482":{"id":"n1820938482","loc":[-85.2558798,42.0053557]},"n1820938483":{"id":"n1820938483","loc":[-85.2544422,42.0047339]},"n1820938484":{"id":"n1820938484","loc":[-85.4864683,41.9843183]},"n1820938485":{"id":"n1820938485","loc":[-85.2554185,42.0031075]},"n1820938486":{"id":"n1820938486","loc":[-85.3082795,41.9712486]},"n1820938487":{"id":"n1820938487","loc":[-85.2433378,42.0133436]},"n1820938488":{"id":"n1820938488","loc":[-85.0216696,42.0904162]},"n1820938489":{"id":"n1820938489","loc":[-85.2546138,42.0050289]},"n1820938490":{"id":"n1820938490","loc":[-85.2717521,41.9977349]},"n1820938491":{"id":"n1820938491","loc":[-85.0100489,42.0908195]},"n1820938492":{"id":"n1820938492","loc":[-85.207879,42.0392211]},"n1820938493":{"id":"n1820938493","loc":[-85.0007363,42.0882836]},"n1820938494":{"id":"n1820938494","loc":[-85.5775303,41.9504097]},"n1820938495":{"id":"n1820938495","loc":[-85.1131584,42.0847683]},"n1820938496":{"id":"n1820938496","loc":[-85.0887825,42.0941633]},"n1820938497":{"id":"n1820938497","loc":[-85.1185926,42.0818938]},"n1820938498":{"id":"n1820938498","loc":[-85.2748487,41.9948712]},"n1820938499":{"id":"n1820938499","loc":[-85.2566952,42.0090788]},"n1820938500":{"id":"n1820938500","loc":[-85.0774757,42.1036234]},"n1820938501":{"id":"n1820938501","loc":[-85.4190869,42.008903]},"n1820938502":{"id":"n1820938502","loc":[-85.1140395,42.0850577]},"n1820938503":{"id":"n1820938503","loc":[-85.1136104,42.0848627]},"n1820938504":{"id":"n1820938504","loc":[-85.5828089,41.9480638]},"n1820938505":{"id":"n1820938505","loc":[-85.625514,41.9405202]},"n1820938506":{"id":"n1820938506","loc":[-85.2063384,42.0398322]},"n1820938507":{"id":"n1820938507","loc":[-85.3395476,41.9851636]},"n1820938508":{"id":"n1820938508","loc":[-85.0328853,42.0963606]},"n1820938510":{"id":"n1820938510","loc":[-85.1170369,42.0843702]},"n1820938511":{"id":"n1820938511","loc":[-85.2784748,41.9868487]},"n1820938512":{"id":"n1820938512","loc":[-85.6310501,41.9435528]},"n1820938514":{"id":"n1820938514","loc":[-85.0334284,42.0981028]},"n1820938515":{"id":"n1820938515","loc":[-84.9912091,42.0868226]},"n1820938516":{"id":"n1820938516","loc":[-85.2806141,41.9940351]},"n1820938517":{"id":"n1820938517","loc":[-85.1233025,42.0776734]},"n1820938518":{"id":"n1820938518","loc":[-85.2047891,42.0429023]},"n1820938519":{"id":"n1820938519","loc":[-85.1475443,42.0648312]},"n1820938520":{"id":"n1820938520","loc":[-85.2644685,41.9990891]},"n1820938521":{"id":"n1820938521","loc":[-85.1056281,42.0872553]},"n1820938522":{"id":"n1820938522","loc":[-85.4813184,41.9930105]},"n1820938523":{"id":"n1820938523","loc":[-85.321551,41.9722936]},"n1820938524":{"id":"n1820938524","loc":[-85.1564664,42.0631211]},"n1820938525":{"id":"n1820938525","loc":[-85.4149885,42.0079144]},"n1820938527":{"id":"n1820938527","loc":[-85.2861888,41.9803653]},"n1820938528":{"id":"n1820938528","loc":[-85.1301379,42.0682178]},"n1820938529":{"id":"n1820938529","loc":[-85.4156537,42.0084247]},"n1820938530":{"id":"n1820938530","loc":[-85.245151,42.0176082]},"n1820938531":{"id":"n1820938531","loc":[-85.457818,42.0001651]},"n1820938532":{"id":"n1820938532","loc":[-85.310951,41.9694538]},"n1820938533":{"id":"n1820938533","loc":[-85.1509089,42.0611298]},"n1820938534":{"id":"n1820938534","loc":[-85.1108249,42.086321]},"n1820938535":{"id":"n1820938535","loc":[-85.1260344,42.0740687]},"n1820938536":{"id":"n1820938536","loc":[-85.4561228,42.0042791]},"n1820938537":{"id":"n1820938537","loc":[-85.2805082,41.9945761]},"n1820938538":{"id":"n1820938538","loc":[-85.273352,41.9981921]},"n1820938539":{"id":"n1820938539","loc":[-85.1084216,42.0864364]},"n1820938540":{"id":"n1820938540","loc":[-85.5009737,41.9773637]},"n1820938541":{"id":"n1820938541","loc":[-85.3960843,42.0051879]},"n1820938542":{"id":"n1820938542","loc":[-85.3425088,41.9865034]},"n1820938545":{"id":"n1820938545","loc":[-84.9937907,42.0860849]},"n1820938546":{"id":"n1820938546","loc":[-85.1084176,42.086065]},"n1820938547":{"id":"n1820938547","loc":[-85.3492851,41.9924786]},"n1820938548":{"id":"n1820938548","loc":[-85.2512235,42.0101147]},"n1820938549":{"id":"n1820938549","loc":[-85.3717298,41.9979326]},"n1820938551":{"id":"n1820938551","loc":[-85.2573712,42.0064081]},"n1820938552":{"id":"n1820938552","loc":[-85.2514596,42.010139]},"n1820938553":{"id":"n1820938553","loc":[-85.416512,42.0088073]},"n1820938554":{"id":"n1820938554","loc":[-85.4365964,42.0061606]},"n1820938555":{"id":"n1820938555","loc":[-85.4552431,42.0057301]},"n1820938556":{"id":"n1820938556","loc":[-85.2916283,41.9778769]},"n1820938557":{"id":"n1820938557","loc":[-85.100709,42.0902968]},"n1820938558":{"id":"n1820938558","loc":[-85.4703064,41.9965771]},"n1820938559":{"id":"n1820938559","loc":[-85.3134722,41.9696134]},"n1820938560":{"id":"n1820938560","loc":[-85.4834213,41.9885768]},"n1820938561":{"id":"n1820938561","loc":[-85.2740641,41.9975236]},"n1820938562":{"id":"n1820938562","loc":[-85.148334,42.0623405]},"n1820938563":{"id":"n1820938563","loc":[-85.2358598,42.0263675]},"n1820938565":{"id":"n1820938565","loc":[-85.2902979,41.9790892]},"n1820938566":{"id":"n1820938566","loc":[-85.2528865,42.0112869]},"n1820938567":{"id":"n1820938567","loc":[-85.2595319,41.9973003]},"n1820938568":{"id":"n1820938568","loc":[-85.071151,42.105689]},"n1820938570":{"id":"n1820938570","loc":[-85.299278,41.9732188]},"n1820938571":{"id":"n1820938571","loc":[-85.0354669,42.1024771]},"n1820938583":{"id":"n1820938583","loc":[-85.3313937,41.972562]},"n1820938585":{"id":"n1820938585","loc":[-85.0756933,42.1058334]},"n1820938587":{"id":"n1820938587","loc":[-85.3130324,41.9694219]},"n1820938590":{"id":"n1820938590","loc":[-85.0934227,42.0931681]},"n1820938592":{"id":"n1820938592","loc":[-85.3517956,41.9922553]},"n1820938593":{"id":"n1820938593","loc":[-85.4023971,42.0065169]},"n1820938594":{"id":"n1820938594","loc":[-85.3506798,41.9925583]},"n1820938595":{"id":"n1820938595","loc":[-85.3673524,41.9971193]},"n1820938596":{"id":"n1820938596","loc":[-85.1073608,42.0853523]},"n1820938597":{"id":"n1820938597","loc":[-85.2976579,41.972477]},"n1820938598":{"id":"n1820938598","loc":[-85.5616517,41.9694295]},"n1820938599":{"id":"n1820938599","loc":[-85.3552074,41.9921915]},"n1820938600":{"id":"n1820938600","loc":[-85.4665126,41.9999953]},"n1820938601":{"id":"n1820938601","loc":[-85.2740695,41.9966226]},"n1820938602":{"id":"n1820938602","loc":[-85.279376,41.9886669]},"n1820938603":{"id":"n1820938603","loc":[-85.0771109,42.1040413]},"n1820938604":{"id":"n1820938604","loc":[-85.2636049,41.9977895]},"n1820938605":{"id":"n1820938605","loc":[-85.3762145,41.9976456]},"n1820938606":{"id":"n1820938606","loc":[-85.2321369,42.0289577]},"n1820938620":{"id":"n1820938620","loc":[-85.4947724,41.9776189]},"n1820938622":{"id":"n1820938622","loc":[-85.1547069,42.0622768]},"n1820938624":{"id":"n1820938624","loc":[-85.0005056,42.0880249]},"n1820938626":{"id":"n1820938626","loc":[-85.0735596,42.1059357]},"n1820938628":{"id":"n1820938628","loc":[-85.4665298,41.99932]},"n1820938629":{"id":"n1820938629","loc":[-85.434515,42.0065273]},"n1820938630":{"id":"n1820938630","loc":[-85.117462,42.0823823]},"n1820938631":{"id":"n1820938631","loc":[-85.0131777,42.0890707]},"n1820938632":{"id":"n1820938632","loc":[-85.0875326,42.0961934]},"n1820938634":{"id":"n1820938634","loc":[-85.6433839,41.9112042]},"n1820938635":{"id":"n1820938635","loc":[-85.1366181,42.064969]},"n1820938636":{"id":"n1820938636","loc":[-85.073109,42.1057925]},"n1820938638":{"id":"n1820938638","loc":[-85.161406,42.0632541]},"n1820938640":{"id":"n1820938640","loc":[-85.6343932,41.9188845]},"n1820938642":{"id":"n1820938642","loc":[-85.2500004,42.010306]},"n1820938644":{"id":"n1820938644","loc":[-85.291918,41.9753166]},"n1820938663":{"id":"n1820938663","loc":[-85.2841611,41.9916812]},"n1820938664":{"id":"n1820938664","loc":[-85.1052955,42.0868134]},"n1820938665":{"id":"n1820938665","loc":[-85.4606118,42.0005534]},"n1820938666":{"id":"n1820938666","loc":[-85.5672736,41.9642922]},"n1820938667":{"id":"n1820938667","loc":[-85.6348481,41.9316932]},"n1820938668":{"id":"n1820938668","loc":[-85.0224904,42.0909576]},"n1820938669":{"id":"n1820938669","loc":[-85.0133856,42.0899755]},"n1820938670":{"id":"n1820938670","loc":[-85.344779,41.991139]},"n1820938671":{"id":"n1820938671","loc":[-85.632874,41.9425313]},"n1820938673":{"id":"n1820938673","loc":[-85.4941501,41.9779698]},"n1820938675":{"id":"n1820938675","loc":[-85.0862559,42.0997519]},"n1820938676":{"id":"n1820938676","loc":[-85.0097874,42.0898032]},"n1820938678":{"id":"n1820938678","loc":[-84.9913553,42.0863675]},"n1820938680":{"id":"n1820938680","loc":[-85.0533666,42.1038315]},"n1820938682":{"id":"n1820938682","loc":[-85.2950294,41.9743914]},"n1820938684":{"id":"n1820938684","loc":[-85.2517385,42.0104499]},"n1820938686":{"id":"n1820938686","loc":[-85.0247971,42.0922514]},"n1820938688":{"id":"n1820938688","loc":[-85.0807037,42.1026017]},"n1820938690":{"id":"n1820938690","loc":[-85.52462,41.9722748]},"n1820938694":{"id":"n1820938694","loc":[-85.2586535,41.9988818]},"n1820938695":{"id":"n1820938695","loc":[-85.0931612,42.092948]},"n1820938697":{"id":"n1820938697","loc":[-85.2470822,42.016564]},"n1820938698":{"id":"n1820938698","loc":[-85.4143018,42.0075158]},"n1820938699":{"id":"n1820938699","loc":[-85.0771484,42.104487]},"n1820938700":{"id":"n1820938700","loc":[-85.0291208,42.0942775]},"n1820938701":{"id":"n1820938701","loc":[-85.6367964,41.9185971]},"n1820938702":{"id":"n1820938702","loc":[-85.085419,42.1010693]},"n1820938703":{"id":"n1820938703","loc":[-85.0583877,42.1040584]},"n1820938705":{"id":"n1820938705","loc":[-85.2573379,42.0003182]},"n1820938706":{"id":"n1820938706","loc":[-85.2655937,41.9981575]},"n1820938707":{"id":"n1820938707","loc":[-85.023181,42.0915758]},"n1820938708":{"id":"n1820938708","loc":[-85.2318687,42.0274674]},"n1820938709":{"id":"n1820938709","loc":[-85.1056389,42.0866184]},"n1820938710":{"id":"n1820938710","loc":[-85.5276265,41.9700978]},"n1820938711":{"id":"n1820938711","loc":[-85.0864128,42.0945761]},"n1820938712":{"id":"n1820938712","loc":[-84.9897071,42.0871888]},"n1820938714":{"id":"n1820938714","loc":[-85.1328845,42.0665611]},"n1820938715":{"id":"n1820938715","loc":[-85.0336537,42.0991377]},"n1820938716":{"id":"n1820938716","loc":[-85.087597,42.0986692]},"n1820938717":{"id":"n1820938717","loc":[-85.1241394,42.0761882]},"n1820938718":{"id":"n1820938718","loc":[-85.1176002,42.0847723]},"n1820938719":{"id":"n1820938719","loc":[-85.2423615,42.0216572]},"n1820938721":{"id":"n1820938721","loc":[-85.2196378,42.0387908]},"n1820938722":{"id":"n1820938722","loc":[-85.0164272,42.0890082]},"n1820938723":{"id":"n1820938723","loc":[-85.5917182,41.9451807]},"n1820938724":{"id":"n1820938724","loc":[-85.2458806,42.0086638]},"n1820938725":{"id":"n1820938725","loc":[-85.1264474,42.0740527]},"n1820938726":{"id":"n1820938726","loc":[-85.1961631,42.04738]},"n1820938727":{"id":"n1820938727","loc":[-85.2784643,41.9943648]},"n1820938728":{"id":"n1820938728","loc":[-85.2905554,41.9763216]},"n1820938729":{"id":"n1820938729","loc":[-85.2913386,41.9771511]},"n1820938730":{"id":"n1820938730","loc":[-85.0112519,42.0895683]},"n1820938732":{"id":"n1820938732","loc":[-85.4290261,42.0064531]},"n1820938733":{"id":"n1820938733","loc":[-85.3867073,42.0031629]},"n1820938734":{"id":"n1820938734","loc":[-85.4943647,41.9836005]},"n1820938735":{"id":"n1820938735","loc":[-85.4900303,41.9860728]},"n1820938736":{"id":"n1820938736","loc":[-85.0866153,42.0944539]},"n1820938737":{"id":"n1820938737","loc":[-85.0869532,42.0990911]},"n1820938738":{"id":"n1820938738","loc":[-85.6321659,41.9208851]},"n1820938739":{"id":"n1820938739","loc":[-85.5930485,41.9433453]},"n1820938740":{"id":"n1820938740","loc":[-85.0406851,42.102733]},"n1820938741":{"id":"n1820938741","loc":[-85.1051131,42.0869846]},"n1820938742":{"id":"n1820938742","loc":[-85.1377554,42.0648893]},"n1820938743":{"id":"n1820938743","loc":[-85.2795694,41.994604]},"n1820938745":{"id":"n1820938745","loc":[-85.4948153,41.9826594]},"n1820938746":{"id":"n1820938746","loc":[-85.4488916,42.0050923]},"n1820938747":{"id":"n1820938747","loc":[-85.1052526,42.0866144]},"n1820938748":{"id":"n1820938748","loc":[-85.1468749,42.0653991]},"n1820938749":{"id":"n1820938749","loc":[-85.0856886,42.1006104]},"n1820938750":{"id":"n1820938750","loc":[-85.2144022,42.0404004]},"n1820938751":{"id":"n1820938751","loc":[-85.277771,41.9907458]},"n1820938752":{"id":"n1820938752","loc":[-85.1474542,42.0636149]},"n1820938753":{"id":"n1820938753","loc":[-85.0820515,42.1028075]},"n1820938754":{"id":"n1820938754","loc":[-85.1122948,42.08525]},"n1820938756":{"id":"n1820938756","loc":[-85.0173352,42.0901933]},"n1820938757":{"id":"n1820938757","loc":[-85.2259721,42.0354018]},"n1820938758":{"id":"n1820938758","loc":[-85.0872389,42.0987795]},"n1820938759":{"id":"n1820938759","loc":[-85.2291436,42.031874]},"n1820938760":{"id":"n1820938760","loc":[-85.3802485,42.0016002]},"n1820938761":{"id":"n1820938761","loc":[-85.3945822,42.0057938]},"n1820938762":{"id":"n1820938762","loc":[-85.5273237,41.9713017]},"n1820938763":{"id":"n1820938763","loc":[-85.2868862,41.9798629]},"n1820938764":{"id":"n1820938764","loc":[-85.2516677,42.0107899]},"n1820938766":{"id":"n1820938766","loc":[-85.3183002,41.9693103]},"n1820938768":{"id":"n1820938768","loc":[-85.2159042,42.0401932]},"n1820938770":{"id":"n1820938770","loc":[-85.0094481,42.0911141]},"n1820938771":{"id":"n1820938771","loc":[-85.0244538,42.0922155]},"n1820938772":{"id":"n1820938772","loc":[-85.231697,42.028862]},"n1820938773":{"id":"n1820938773","loc":[-85.2102394,42.0390617]},"n1820938774":{"id":"n1820938774","loc":[-85.2463419,42.0151212]},"n1820938775":{"id":"n1820938775","loc":[-85.0726195,42.1056424]},"n1820938776":{"id":"n1820938776","loc":[-85.0060431,42.0883262]},"n1820938778":{"id":"n1820938778","loc":[-85.425889,42.0056982]},"n1820938779":{"id":"n1820938779","loc":[-85.1183042,42.0820638]},"n1820938780":{"id":"n1820938780","loc":[-85.441596,42.0058257]},"n1820938781":{"id":"n1820938781","loc":[-85.1124879,42.0847086]},"n1820938782":{"id":"n1820938782","loc":[-85.2452733,42.0153894]},"n1820938783":{"id":"n1820938783","loc":[-85.2741191,41.9969244]},"n1820938784":{"id":"n1820938784","loc":[-85.2829487,41.9822236]},"n1820938785":{"id":"n1820938785","loc":[-85.3202743,41.972142]},"n1820938786":{"id":"n1820938786","loc":[-85.2345402,42.0266465]},"n1820938787":{"id":"n1820938787","loc":[-85.3037626,41.9724611]},"n1820938789":{"id":"n1820938789","loc":[-85.2474792,42.0161973]},"n1820938790":{"id":"n1820938790","loc":[-85.2951045,41.9727323]},"n1820938791":{"id":"n1820938791","loc":[-85.322345,41.9712726]},"n1820938792":{"id":"n1820938792","loc":[-85.2402372,42.0110394]},"n1820938793":{"id":"n1820938793","loc":[-85.5135693,41.9698659]},"n1820938794":{"id":"n1820938794","loc":[-85.4695339,41.9967366]},"n1820938796":{"id":"n1820938796","loc":[-85.0418492,42.1011131]},"n1820938797":{"id":"n1820938797","loc":[-85.3334107,41.9806337]},"n1820938798":{"id":"n1820938798","loc":[-85.5625314,41.9711685]},"n1820938799":{"id":"n1820938799","loc":[-85.3755707,41.9973585]},"n1820938800":{"id":"n1820938800","loc":[-85.5227532,41.9722429]},"n1820938801":{"id":"n1820938801","loc":[-85.4267687,42.0052836]},"n1820938803":{"id":"n1820938803","loc":[-85.0284704,42.0940837]},"n1820938804":{"id":"n1820938804","loc":[-85.015585,42.0885305]},"n1820938805":{"id":"n1820938805","loc":[-85.0765905,42.1053865]},"n1820938806":{"id":"n1820938806","loc":[-85.2614953,41.9964551]},"n1820938808":{"id":"n1820938808","loc":[-85.0307355,42.0947313]},"n1820938810":{"id":"n1820938810","loc":[-85.3894753,42.0003565]},"n1820938812":{"id":"n1820938812","loc":[-85.0868848,42.095006]},"n1820938813":{"id":"n1820938813","loc":[-85.3854198,42.0009465]},"n1820938814":{"id":"n1820938814","loc":[-85.2659692,41.9993534]},"n1820938815":{"id":"n1820938815","loc":[-85.1234259,42.0765266]},"n1820938816":{"id":"n1820938816","loc":[-85.1426906,42.0648893]},"n1820938818":{"id":"n1820938818","loc":[-85.1014533,42.0893067]},"n1820938819":{"id":"n1820938819","loc":[-85.0883064,42.098067]},"n1820938820":{"id":"n1820938820","loc":[-85.0503156,42.102704]},"n1820938821":{"id":"n1820938821","loc":[-85.1179649,42.0821884]},"n1820938822":{"id":"n1820938822","loc":[-85.3484697,41.9921596]},"n1820938823":{"id":"n1820938823","loc":[-85.3732962,41.9970874]},"n1820938824":{"id":"n1820938824","loc":[-85.2784104,41.9898312]},"n1820938825":{"id":"n1820938825","loc":[-85.4441709,42.0052198]},"n1820938826":{"id":"n1820938826","loc":[-85.3925438,42.0038326]},"n1820938829":{"id":"n1820938829","loc":[-85.5717582,41.9621861]},"n1820938830":{"id":"n1820938830","loc":[-85.0866314,42.0995051]},"n1820938831":{"id":"n1820938831","loc":[-85.576672,41.9522769]},"n1820938832":{"id":"n1820938832","loc":[-85.1587238,42.0636205]},"n1820938833":{"id":"n1820938833","loc":[-85.3804245,41.9999155]},"n1820938834":{"id":"n1820938834","loc":[-85.280083,41.9948843]},"n1820938836":{"id":"n1820938836","loc":[-85.561892,41.9686693]},"n1820938837":{"id":"n1820938837","loc":[-85.0158975,42.0885253]},"n1820938838":{"id":"n1820938838","loc":[-85.4248204,42.007633]},"n1820938839":{"id":"n1820938839","loc":[-85.0352738,42.1039657]},"n1820938840":{"id":"n1820938840","loc":[-85.211956,42.0411812]},"n1820938841":{"id":"n1820938841","loc":[-85.4816575,41.9908997]},"n1820938842":{"id":"n1820938842","loc":[-85.3807635,42.0020308]},"n1820938843":{"id":"n1820938843","loc":[-85.0100865,42.0898521]},"n1820938844":{"id":"n1820938844","loc":[-85.0103936,42.0897434]},"n1820938848":{"id":"n1820938848","loc":[-85.2430052,42.0131363]},"n1820938849":{"id":"n1820938849","loc":[-85.112559,42.0853723]},"n1820938851":{"id":"n1820938851","loc":[-85.3641553,41.9952535]},"n1820938852":{"id":"n1820938852","loc":[-85.2087373,42.0390777]},"n1820938853":{"id":"n1820938853","loc":[-85.2473933,42.0148263]},"n1820938854":{"id":"n1820938854","loc":[-85.0213464,42.090509]},"n1820938855":{"id":"n1820938855","loc":[-85.0673208,42.1052353]},"n1820938856":{"id":"n1820938856","loc":[-85.1003053,42.0905528]},"n1820938857":{"id":"n1820938857","loc":[-85.2617367,41.9965389]},"n1820938858":{"id":"n1820938858","loc":[-85.280363,41.9916015]},"n1820938859":{"id":"n1820938859","loc":[-85.0038866,42.0873469]},"n1820938860":{"id":"n1820938860","loc":[-85.2476401,42.0151451]},"n1820938861":{"id":"n1820938861","loc":[-85.193717,42.0499294]},"n1820938862":{"id":"n1820938862","loc":[-85.3478689,41.9917609]},"n1820938863":{"id":"n1820938863","loc":[-85.5638017,41.9648881]},"n1820938864":{"id":"n1820938864","loc":[-85.4356308,42.0064476]},"n1820938865":{"id":"n1820938865","loc":[-85.0561722,42.1023509]},"n1820938867":{"id":"n1820938867","loc":[-85.2256031,42.0356034]},"n1820938868":{"id":"n1820938868","loc":[-85.6102576,41.9420844]},"n1820938869":{"id":"n1820938869","loc":[-85.2285213,42.0339938]},"n1820938870":{"id":"n1820938870","loc":[-85.0326238,42.0978003]},"n1820938871":{"id":"n1820938871","loc":[-85.0131389,42.0903736]},"n1820938872":{"id":"n1820938872","loc":[-85.2550859,42.0012259]},"n1820938873":{"id":"n1820938873","loc":[-85.1130029,42.0846966]},"n1820938874":{"id":"n1820938874","loc":[-85.1579041,42.06336]},"n1820938875":{"id":"n1820938875","loc":[-85.0430522,42.1020234]},"n1820938876":{"id":"n1820938876","loc":[-85.2786679,41.9865935]},"n1820938877":{"id":"n1820938877","loc":[-85.1221666,42.0788706]},"n1820938878":{"id":"n1820938878","loc":[-85.2554614,42.0103303]},"n1820938879":{"id":"n1820938879","loc":[-85.2349801,42.0265748]},"n1820938880":{"id":"n1820938880","loc":[-85.0997434,42.0907864]},"n1820938881":{"id":"n1820938881","loc":[-85.0045464,42.0878167]},"n1820938882":{"id":"n1820938882","loc":[-85.2728048,41.9982519]},"n1820938883":{"id":"n1820938883","loc":[-85.3111333,41.9691587]},"n1820938884":{"id":"n1820938884","loc":[-85.3219802,41.9721899]},"n1820938885":{"id":"n1820938885","loc":[-85.3091378,41.9699325]},"n1820938887":{"id":"n1820938887","loc":[-85.4242367,42.0085203]},"n1820938888":{"id":"n1820938888","loc":[-84.9968377,42.0874504]},"n1820938890":{"id":"n1820938890","loc":[-85.5443139,41.9714078]},"n1820938891":{"id":"n1820938891","loc":[-85.6404013,41.9154676]},"n1820938892":{"id":"n1820938892","loc":[-85.3644986,41.9962582]},"n1820938893":{"id":"n1820938893","loc":[-85.0496772,42.1018323]},"n1820938894":{"id":"n1820938894","loc":[-85.297261,41.9737373]},"n1820938895":{"id":"n1820938895","loc":[-85.0327096,42.098071]},"n1820938896":{"id":"n1820938896","loc":[-85.3856773,41.9996867]},"n1820938897":{"id":"n1820938897","loc":[-85.0493862,42.1015509]},"n1820938898":{"id":"n1820938898","loc":[-84.9969879,42.0876614]},"n1820938899":{"id":"n1820938899","loc":[-85.0848625,42.1013587]},"n1820938900":{"id":"n1820938900","loc":[-85.5853195,41.9479201]},"n1820938901":{"id":"n1820938901","loc":[-85.6329169,41.9387964]},"n1820938902":{"id":"n1820938902","loc":[-85.0843046,42.1029468]},"n1820938903":{"id":"n1820938903","loc":[-85.1228747,42.0778474]},"n1820938904":{"id":"n1820938904","loc":[-85.4855456,41.984095]},"n1820938905":{"id":"n1820938905","loc":[-85.0573269,42.1026801]},"n1820938906":{"id":"n1820938906","loc":[-85.2425868,42.0131523]},"n1820938907":{"id":"n1820938907","loc":[-85.1149622,42.0860053]},"n1820938908":{"id":"n1820938908","loc":[-85.4833097,41.9951578]},"n1820938909":{"id":"n1820938909","loc":[-85.075979,42.1056372]},"n1820938910":{"id":"n1820938910","loc":[-85.0338509,42.0977139]},"n1820938911":{"id":"n1820938911","loc":[-85.6384272,41.9115715]},"n1820938912":{"id":"n1820938912","loc":[-85.0458363,42.1004074]},"n1820938913":{"id":"n1820938913","loc":[-85.0592138,42.1048305]},"n1820938914":{"id":"n1820938914","loc":[-85.2807493,41.9916653]},"n1820938915":{"id":"n1820938915","loc":[-85.1103274,42.0864193]},"n1820938916":{"id":"n1820938916","loc":[-85.6267156,41.9404404]},"n1820938918":{"id":"n1820938918","loc":[-85.0331374,42.0982911]},"n1820938919":{"id":"n1820938919","loc":[-85.5637331,41.965409]},"n1820938920":{"id":"n1820938920","loc":[-85.5457515,41.9714237]},"n1820938922":{"id":"n1820938922","loc":[-85.082073,42.1030104]},"n1820938923":{"id":"n1820938923","loc":[-85.0780765,42.103102]},"n1820938924":{"id":"n1820938924","loc":[-85.4208035,42.0089508]},"n1820938925":{"id":"n1820938925","loc":[-85.3469934,41.9914795]},"n1820938926":{"id":"n1820938926","loc":[-85.0322,42.095619]},"n1820938927":{"id":"n1820938927","loc":[-85.4784431,41.9949401]},"n1820938928":{"id":"n1820938928","loc":[-85.1303095,42.0667523]},"n1820938929":{"id":"n1820938929","loc":[-85.2463784,42.0084781]},"n1820938930":{"id":"n1820938930","loc":[-85.6299986,41.9427707]},"n1820938931":{"id":"n1820938931","loc":[-85.6325907,41.9238499]},"n1820938932":{"id":"n1820938932","loc":[-85.4808464,41.9914476]},"n1820938934":{"id":"n1820938934","loc":[-85.2411599,42.0105292]},"n1820938935":{"id":"n1820938935","loc":[-85.0163213,42.0892379]},"n1820938936":{"id":"n1820938936","loc":[-85.3290934,41.9682322]},"n1820938937":{"id":"n1820938937","loc":[-85.4925623,41.9853231]},"n1820938938":{"id":"n1820938938","loc":[-85.0338294,42.09892]},"n1820938940":{"id":"n1820938940","loc":[-85.4174561,42.008903]},"n1820938941":{"id":"n1820938941","loc":[-85.1165595,42.0838845]},"n1820938942":{"id":"n1820938942","loc":[-85.2954585,41.9717192]},"n1820938943":{"id":"n1820938943","loc":[-85.6330199,41.9257338]},"n1820938944":{"id":"n1820938944","loc":[-85.2294654,42.0324478]},"n1820938945":{"id":"n1820938945","loc":[-85.5601282,41.9728914]},"n1820938946":{"id":"n1820938946","loc":[-85.1176324,42.08568]},"n1820938947":{"id":"n1820938947","loc":[-85.0210245,42.0906005]},"n1820938948":{"id":"n1820938948","loc":[-85.0251887,42.09253]},"n1820938949":{"id":"n1820938949","loc":[-85.0895832,42.0939551]},"n1820938950":{"id":"n1820938950","loc":[-84.9915109,42.085842]},"n1820938951":{"id":"n1820938951","loc":[-85.2187366,42.0393486]},"n1820938952":{"id":"n1820938952","loc":[-85.006605,42.087579]},"n1820938953":{"id":"n1820938953","loc":[-85.046641,42.1012393]},"n1820938954":{"id":"n1820938954","loc":[-85.052102,42.103695]},"n1820938955":{"id":"n1820938955","loc":[-85.283925,41.9912825]},"n1820938956":{"id":"n1820938956","loc":[-85.2326626,42.0316349]},"n1820938957":{"id":"n1820938957","loc":[-85.1174298,42.0859694]},"n1820938958":{"id":"n1820938958","loc":[-85.3802056,41.9994794]},"n1820938959":{"id":"n1820938959","loc":[-85.4586334,41.9999737]},"n1820938960":{"id":"n1820938960","loc":[-85.4302234,42.0069418]},"n1820938961":{"id":"n1820938961","loc":[-85.092201,42.0930674]},"n1820938962":{"id":"n1820938962","loc":[-85.3684511,41.9979382]},"n1820938963":{"id":"n1820938963","loc":[-85.4618735,42.0011856]},"n1820938964":{"id":"n1820938964","loc":[-85.4828205,41.9877793]},"n1820938965":{"id":"n1820938965","loc":[-85.0837789,42.1025726]},"n1820938966":{"id":"n1820938966","loc":[-85.0176195,42.090253]},"n1820938967":{"id":"n1820938967","loc":[-85.3801627,42.001074]},"n1820938968":{"id":"n1820938968","loc":[-85.4767007,41.994488]},"n1820938969":{"id":"n1820938969","loc":[-85.274268,41.9957495]},"n1820938970":{"id":"n1820938970","loc":[-85.2977438,41.9719506]},"n1820938971":{"id":"n1820938971","loc":[-85.2425546,42.0208682]},"n1820938972":{"id":"n1820938972","loc":[-85.2557082,42.002382]},"n1820938973":{"id":"n1820938973","loc":[-85.3187937,41.9691986]},"n1820938975":{"id":"n1820938975","loc":[-85.2448077,42.0153045]},"n1820938977":{"id":"n1820938977","loc":[-85.0343015,42.0997718]},"n1820938978":{"id":"n1820938978","loc":[-85.2449364,42.01874]},"n1820938979":{"id":"n1820938979","loc":[-85.2598391,41.9969602]},"n1820938980":{"id":"n1820938980","loc":[-85.4294724,42.0067665]},"n1820938981":{"id":"n1820938981","loc":[-85.428082,42.0055124]},"n1820938983":{"id":"n1820938983","loc":[-85.5436315,41.9717484]},"n1820938985":{"id":"n1820938985","loc":[-85.5978336,41.9407437]},"n1820938986":{"id":"n1820938986","loc":[-85.491661,41.9860249]},"n1820938987":{"id":"n1820938987","loc":[-85.4942789,41.9801392]},"n1820938988":{"id":"n1820938988","loc":[-85.0416306,42.1010841]},"n1820938989":{"id":"n1820938989","loc":[-85.2653644,41.9984433]},"n1820938990":{"id":"n1820938990","loc":[-85.1028266,42.0881124]},"n1820938991":{"id":"n1820938991","loc":[-85.0163146,42.0887932]},"n1820938992":{"id":"n1820938992","loc":[-85.5282209,41.9678112]},"n1820938993":{"id":"n1820938993","loc":[-85.5442752,41.9715888]},"n1820938994":{"id":"n1820938994","loc":[-85.5634327,41.9658558]},"n1820938995":{"id":"n1820938995","loc":[-85.0384227,42.1037627]},"n1820938996":{"id":"n1820938996","loc":[-85.1144258,42.0854439]},"n1820938997":{"id":"n1820938997","loc":[-85.1870651,42.0506305]},"n1820938998":{"id":"n1820938998","loc":[-85.1256159,42.0747376]},"n1820938999":{"id":"n1820938999","loc":[-85.3272695,41.9715836]},"n1820939000":{"id":"n1820939000","loc":[-85.0543067,42.103098]},"n1820939001":{"id":"n1820939001","loc":[-85.4678173,41.9973585]},"n1820939003":{"id":"n1820939003","loc":[-85.0266626,42.0933154]},"n1820939004":{"id":"n1820939004","loc":[-85.0353046,42.1019728]},"n1820939005":{"id":"n1820939005","loc":[-85.1237961,42.0762798]},"n1820939006":{"id":"n1820939006","loc":[-85.2812214,41.9826702]},"n1820939007":{"id":"n1820939007","loc":[-85.2927763,41.9747343]},"n1820939008":{"id":"n1820939008","loc":[-85.3270979,41.9720862]},"n1820939009":{"id":"n1820939009","loc":[-85.488657,41.9856581]},"n1820939010":{"id":"n1820939010","loc":[-85.3087301,41.9701399]},"n1820939011":{"id":"n1820939011","loc":[-85.0276939,42.093768]},"n1820939012":{"id":"n1820939012","loc":[-85.2956516,41.9748779]},"n1820939013":{"id":"n1820939013","loc":[-85.1298579,42.0726443]},"n1820939014":{"id":"n1820939014","loc":[-85.105144,42.0870893]},"n1820939015":{"id":"n1820939015","loc":[-85.0677486,42.1053917]},"n1820939016":{"id":"n1820939016","loc":[-85.0333681,42.0993459]},"n1820939017":{"id":"n1820939017","loc":[-85.6384272,41.910805]},"n1820939018":{"id":"n1820939018","loc":[-85.399496,42.006894]},"n1820939019":{"id":"n1820939019","loc":[-85.2648427,41.9998318]},"n1820939020":{"id":"n1820939020","loc":[-85.1237424,42.0766779]},"n1820939021":{"id":"n1820939021","loc":[-85.2515025,42.0109442]},"n1820939022":{"id":"n1820939022","loc":[-85.5566306,41.9718385]},"n1820939023":{"id":"n1820939023","loc":[-85.090644,42.0938369]},"n1820939024":{"id":"n1820939024","loc":[-85.1245525,42.074914]},"n1820939025":{"id":"n1820939025","loc":[-85.1099934,42.0863926]},"n1820939026":{"id":"n1820939026","loc":[-85.1251653,42.0744589]},"n1820939027":{"id":"n1820939027","loc":[-85.401792,42.0068143]},"n1820939028":{"id":"n1820939028","loc":[-85.0094763,42.0899584]},"n1820939029":{"id":"n1820939029","loc":[-85.1330779,42.0705605]},"n1820939030":{"id":"n1820939030","loc":[-85.4935064,41.984398]},"n1820939031":{"id":"n1820939031","loc":[-85.5713334,41.9613939]},"n1820939032":{"id":"n1820939032","loc":[-85.0873945,42.0964669]},"n1820939033":{"id":"n1820939033","loc":[-85.0886497,42.0986481]},"n1820939034":{"id":"n1820939034","loc":[-85.3276343,41.9758897]},"n1820939035":{"id":"n1820939035","loc":[-85.1304386,42.0727387]},"n1820939036":{"id":"n1820939036","loc":[-85.2551932,42.0052999]},"n1820939037":{"id":"n1820939037","loc":[-85.2206936,42.0384458]},"n1820939038":{"id":"n1820939038","loc":[-85.2313645,42.0286389]},"n1820939039":{"id":"n1820939039","loc":[-85.0754586,42.1059835]},"n1820939040":{"id":"n1820939040","loc":[-85.0663324,42.1050812]},"n1820939041":{"id":"n1820939041","loc":[-85.2406234,42.0106887]},"n1820939042":{"id":"n1820939042","loc":[-85.0685962,42.1058175]},"n1820939043":{"id":"n1820939043","loc":[-85.0689462,42.1059437]},"n1820939044":{"id":"n1820939044","loc":[-85.0586144,42.1046144]},"n1820939045":{"id":"n1820939045","loc":[-85.3650565,41.9965452]},"n1820939047":{"id":"n1820939047","loc":[-85.5752558,41.9536014]},"n1820939048":{"id":"n1820939048","loc":[-85.5110159,41.9710624]},"n1820939050":{"id":"n1820939050","loc":[-85.2832641,41.9926477]},"n1820939051":{"id":"n1820939051","loc":[-85.0078402,42.0898947]},"n1820939052":{"id":"n1820939052","loc":[-85.3882737,42.0017916]},"n1820939053":{"id":"n1820939053","loc":[-85.1718945,42.0564937]},"n1820939054":{"id":"n1820939054","loc":[-85.0947048,42.0929293]},"n1820939055":{"id":"n1820939055","loc":[-85.4456944,42.0051082]},"n1820939056":{"id":"n1820939056","loc":[-85.3139872,41.9706903]},"n1820939057":{"id":"n1820939057","loc":[-85.3893895,42.0034021]},"n1820939058":{"id":"n1820939058","loc":[-85.2425332,42.0106089]},"n1820939059":{"id":"n1820939059","loc":[-85.6085624,41.9420844]},"n1820939060":{"id":"n1820939060","loc":[-85.210411,42.0397789]},"n1820939061":{"id":"n1820939061","loc":[-85.2762542,41.9960473]},"n1820939062":{"id":"n1820939062","loc":[-85.4686584,41.9969973]},"n1820939063":{"id":"n1820939063","loc":[-85.3860421,42.0018394]},"n1820939064":{"id":"n1820939064","loc":[-85.5636944,41.9644414]},"n1820939065":{"id":"n1820939065","loc":[-85.3267331,41.9766554]},"n1820939066":{"id":"n1820939066","loc":[-85.0868996,42.0943822]},"n1820939067":{"id":"n1820939067","loc":[-85.104861,42.0880038]},"n1820939068":{"id":"n1820939068","loc":[-85.5537123,41.9695093]},"n1820939069":{"id":"n1820939069","loc":[-85.6325092,41.9396743]},"n1820939070":{"id":"n1820939070","loc":[-85.3869648,42.0024454]},"n1820939071":{"id":"n1820939071","loc":[-85.2775349,41.9957335]},"n1820939072":{"id":"n1820939072","loc":[-85.2055616,42.0421533]},"n1820939073":{"id":"n1820939073","loc":[-85.4731431,41.9946531]},"n1820939074":{"id":"n1820939074","loc":[-85.0399609,42.1030833]},"n1820939075":{"id":"n1820939075","loc":[-85.3055758,41.9725169]},"n1820939076":{"id":"n1820939076","loc":[-85.4834599,41.994488]},"n1820939077":{"id":"n1820939077","loc":[-85.3819866,42.0023018]},"n1820939078":{"id":"n1820939078","loc":[-85.1218756,42.0789992]},"n1820939079":{"id":"n1820939079","loc":[-85.2793159,41.9944458]},"n1820939080":{"id":"n1820939080","loc":[-85.2495498,42.0101466]},"n1820939081":{"id":"n1820939081","loc":[-85.0035969,42.0872434]},"n1820939082":{"id":"n1820939082","loc":[-85.1054243,42.0865626]},"n1820939083":{"id":"n1820939083","loc":[-85.0917665,42.0934774]},"n1820939084":{"id":"n1820939084","loc":[-85.3442211,41.988938]},"n1820939086":{"id":"n1820939086","loc":[-85.273989,41.9953588]},"n1820939087":{"id":"n1820939087","loc":[-85.1142541,42.0852488]},"n1820939089":{"id":"n1820939089","loc":[-85.1526684,42.0615758]},"n1820939090":{"id":"n1820939090","loc":[-85.2538843,42.0110159]},"n1820939091":{"id":"n1820939091","loc":[-85.28341,41.9909635]},"n1820939092":{"id":"n1820939092","loc":[-85.3963178,42.0050217]},"n1820939093":{"id":"n1820939093","loc":[-85.0851682,42.1012472]},"n1820939095":{"id":"n1820939095","loc":[-85.2811784,41.986243]},"n1820939096":{"id":"n1820939096","loc":[-85.4274125,42.0052995]},"n1820939097":{"id":"n1820939097","loc":[-85.0871262,42.0951652]},"n1820939099":{"id":"n1820939099","loc":[-85.1314253,42.0671665]},"n1820939100":{"id":"n1820939100","loc":[-85.2778997,41.991001]},"n1820939101":{"id":"n1820939101","loc":[-85.112107,42.0862812]},"n1820939102":{"id":"n1820939102","loc":[-85.299911,41.9729955]},"n1820939103":{"id":"n1820939103","loc":[-85.639822,41.9094796]},"n1820939104":{"id":"n1820939104","loc":[-85.122294,42.0785334]},"n1820939105":{"id":"n1820939105","loc":[-85.2476294,42.015719]},"n1820939106":{"id":"n1820939106","loc":[-85.4946007,41.9814631]},"n1820939107":{"id":"n1820939107","loc":[-85.0879524,42.0986919]},"n1820939108":{"id":"n1820939108","loc":[-85.0342814,42.098274]},"n1820939109":{"id":"n1820939109","loc":[-85.2450695,42.0095463]},"n1820939110":{"id":"n1820939110","loc":[-85.3847546,42.0024135]},"n1820939111":{"id":"n1820939111","loc":[-85.2961344,41.9742558]},"n1820939112":{"id":"n1820939112","loc":[-85.27899,41.994317]},"n1820939114":{"id":"n1820939114","loc":[-85.1017644,42.0886618]},"n1820939115":{"id":"n1820939115","loc":[-85.076215,42.1056333]},"n1820939116":{"id":"n1820939116","loc":[-85.1198009,42.0805349]},"n1820939117":{"id":"n1820939117","loc":[-85.11988,42.0798513]},"n1820939118":{"id":"n1820939118","loc":[-85.147819,42.0625476]},"n1820939119":{"id":"n1820939119","loc":[-85.0585969,42.1029042]},"n1820939120":{"id":"n1820939120","loc":[-85.1248596,42.0745744]},"n1820939121":{"id":"n1820939121","loc":[-85.3023786,41.9725249]},"n1820939123":{"id":"n1820939123","loc":[-85.0119332,42.0900699]},"n1820939124":{"id":"n1820939124","loc":[-85.2466852,42.0170343]},"n1820939125":{"id":"n1820939125","loc":[-85.0033019,42.0872792]},"n1820939126":{"id":"n1820939126","loc":[-85.0042084,42.0875778]},"n1820939128":{"id":"n1820939128","loc":[-85.0052961,42.0885424]},"n1820939130":{"id":"n1820939130","loc":[-85.0647942,42.10508]},"n1820939131":{"id":"n1820939131","loc":[-85.2824123,41.9825107]},"n1820939132":{"id":"n1820939132","loc":[-85.3210039,41.9723255]},"n1820939133":{"id":"n1820939133","loc":[-85.0491033,42.1014184]},"n1820939134":{"id":"n1820939134","loc":[-85.1127776,42.0855168]},"n1820939135":{"id":"n1820939135","loc":[-85.1243768,42.0759322]},"n1820939137":{"id":"n1820939137","loc":[-85.125974,42.0747547]},"n1820939138":{"id":"n1820939138","loc":[-85.1071248,42.0859973]},"n1820939139":{"id":"n1820939139","loc":[-85.5326175,41.9674833]},"n1820939140":{"id":"n1820939140","loc":[-85.1338715,42.0660833]},"n1820939142":{"id":"n1820939142","loc":[-85.649671,41.9135675]},"n1820939144":{"id":"n1820939144","loc":[-85.0236545,42.0920444]},"n1820939145":{"id":"n1820939145","loc":[-85.1084391,42.0859376]},"n1820939146":{"id":"n1820939146","loc":[-85.1539988,42.0618626]},"n1820939147":{"id":"n1820939147","loc":[-85.2354521,42.026511]},"n1820939148":{"id":"n1820939148","loc":[-85.2362246,42.0260408]},"n1820939149":{"id":"n1820939149","loc":[-85.2401342,42.0115233]},"n1820939150":{"id":"n1820939150","loc":[-85.295319,41.9747423]},"n1820939151":{"id":"n1820939151","loc":[-85.1164696,42.0835409]},"n1820939152":{"id":"n1820939152","loc":[-85.3232891,41.9712885]},"n1820939153":{"id":"n1820939153","loc":[-85.2574463,42.0068944]},"n1820939155":{"id":"n1820939155","loc":[-85.5704064,41.9598246]},"n1820939156":{"id":"n1820939156","loc":[-85.0349077,42.0998116]},"n1820939157":{"id":"n1820939157","loc":[-85.0949529,42.0925619]},"n1820939159":{"id":"n1820939159","loc":[-85.0179829,42.0902343]},"n1820939160":{"id":"n1820939160","loc":[-85.0405832,42.1016942]},"n1820939161":{"id":"n1820939161","loc":[-85.2534015,42.0111833]},"n1820939162":{"id":"n1820939162","loc":[-85.0839881,42.102708]},"n1820939163":{"id":"n1820939163","loc":[-85.0341996,42.1008385]},"n1820939164":{"id":"n1820939164","loc":[-85.1037761,42.0879731]},"n1820939173":{"id":"n1820939173","loc":[-85.0460616,42.1005786]},"n1820939177":{"id":"n1820939177","loc":[-85.0061651,42.0878059]},"n1820939181":{"id":"n1820939181","loc":[-85.1456775,42.0654684]},"n1820939183":{"id":"n1820939183","loc":[-85.1325508,42.0718439]},"n1820939185":{"id":"n1820939185","loc":[-85.2485842,42.008329]},"n1820939187":{"id":"n1820939187","loc":[-85.2744128,41.9949322]},"n1820939189":{"id":"n1820939189","loc":[-85.2579025,41.9999542]},"n1820939191":{"id":"n1820939191","loc":[-85.3358998,41.9828987]},"n1820939193":{"id":"n1820939193","loc":[-85.3192658,41.9716714]},"n1820939194":{"id":"n1820939194","loc":[-85.6400795,41.9130725]},"n1820939195":{"id":"n1820939195","loc":[-85.3278489,41.9780591]},"n1820939196":{"id":"n1820939196","loc":[-85.2800197,41.983061]},"n1820939197":{"id":"n1820939197","loc":[-85.3278167,41.9692943]},"n1820939198":{"id":"n1820939198","loc":[-85.3366894,41.9838653]},"n1820939199":{"id":"n1820939199","loc":[-85.0328383,42.0969923]},"n1820939201":{"id":"n1820939201","loc":[-85.3259284,41.9720383]},"n1820939217":{"id":"n1820939217","loc":[-85.1840181,42.0503277]},"n1820939220":{"id":"n1820939220","loc":[-85.422563,42.0089986]},"n1820939222":{"id":"n1820939222","loc":[-85.555386,41.9707856]},"n1820939224":{"id":"n1820939224","loc":[-85.3830809,42.002254]},"n1820939226":{"id":"n1820939226","loc":[-84.9917938,42.0857517]},"n1820939227":{"id":"n1820939227","loc":[-85.2936775,41.9740484]},"n1820939228":{"id":"n1820939228","loc":[-85.2632133,41.9975024]},"n1820939229":{"id":"n1820939229","loc":[-85.2809424,41.9853259]},"n1820939230":{"id":"n1820939230","loc":[-85.242104,42.0131204]},"n1820939232":{"id":"n1820939232","loc":[-85.2610246,41.9963901]},"n1820939233":{"id":"n1820939233","loc":[-85.2335531,42.0268378]},"n1820939234":{"id":"n1820939234","loc":[-85.3188839,41.9713575]},"n1820939235":{"id":"n1820939235","loc":[-85.2413637,42.0225658]},"n1820939237":{"id":"n1820939237","loc":[-85.0010796,42.0887215]},"n1820939239":{"id":"n1820939239","loc":[-85.2241697,42.0362624]},"n1820939243":{"id":"n1820939243","loc":[-85.0368456,42.1040134]},"n1820939244":{"id":"n1820939244","loc":[-85.1327986,42.069524]},"n1820939260":{"id":"n1820939260","loc":[-85.5408163,41.9711206]},"n1820939261":{"id":"n1820939261","loc":[-85.2959199,41.9746546]},"n1820939262":{"id":"n1820939262","loc":[-85.3298659,41.9683598]},"n1820939263":{"id":"n1820939263","loc":[-85.2240581,42.0358425]},"n1820939264":{"id":"n1820939264","loc":[-85.2438206,42.0101944]},"n1820939265":{"id":"n1820939265","loc":[-85.3984489,42.0059589]},"n1820939266":{"id":"n1820939266","loc":[-85.2330811,42.0294279]},"n1820939268":{"id":"n1820939268","loc":[-85.1126877,42.0857704]},"n1820939271":{"id":"n1820939271","loc":[-85.254925,42.0106253]},"n1820939273":{"id":"n1820939273","loc":[-85.4328046,42.0064662]},"n1820939277":{"id":"n1820939277","loc":[-85.289622,41.9789616]},"n1820939279":{"id":"n1820939279","loc":[-85.4574532,42.0004043]},"n1820939281":{"id":"n1820939281","loc":[-85.4803486,41.9867211]},"n1820939283":{"id":"n1820939283","loc":[-85.157475,42.0631848]},"n1820939285":{"id":"n1820939285","loc":[-85.2571458,42.0059935]},"n1820939287":{"id":"n1820939287","loc":[-85.2818544,41.9825984]},"n1820939289":{"id":"n1820939289","loc":[-85.2298302,42.0328781]},"n1820939291":{"id":"n1820939291","loc":[-85.4819523,41.984821]},"n1820939301":{"id":"n1820939301","loc":[-85.3139765,41.9701159]},"n1820939304":{"id":"n1820939304","loc":[-85.0424447,42.101742]},"n1820939306":{"id":"n1820939306","loc":[-85.6360283,41.9338482]},"n1820939310":{"id":"n1820939310","loc":[-85.3463025,41.9913622]},"n1820939312":{"id":"n1820939312","loc":[-85.4664869,41.9988097]},"n1820939314":{"id":"n1820939314","loc":[-85.149364,42.0622449]},"n1820939316":{"id":"n1820939316","loc":[-85.2460415,42.0153125]},"n1820939318":{"id":"n1820939318","loc":[-85.4806103,41.9924523]},"n1820939320":{"id":"n1820939320","loc":[-85.2449042,42.0190987]},"n1820939322":{"id":"n1820939322","loc":[-85.5280165,41.9689263]},"n1820939324":{"id":"n1820939324","loc":[-85.0051204,42.0882625]},"n1820939326":{"id":"n1820939326","loc":[-85.1240925,42.0771546]},"n1820939329":{"id":"n1820939329","loc":[-85.2261653,42.0342225]},"n1820939331":{"id":"n1820939331","loc":[-85.5259933,41.972211]},"n1820939333":{"id":"n1820939333","loc":[-85.0074754,42.0883183]},"n1820939335":{"id":"n1820939335","loc":[-85.0764014,42.1055549]},"n1820939336":{"id":"n1820939336","loc":[-85.2908773,41.9769597]},"n1820939337":{"id":"n1820939337","loc":[-85.4095382,42.0083449]},"n1820939346":{"id":"n1820939346","loc":[-85.2514166,42.0111753]},"n1820939348":{"id":"n1820939348","loc":[-85.0030377,42.0873799]},"n1820939350":{"id":"n1820939350","loc":[-85.3659362,41.9964974]},"n1820939352":{"id":"n1820939352","loc":[-85.226058,42.0348281]},"n1820939355":{"id":"n1820939355","loc":[-85.1902408,42.0507101]},"n1820939357":{"id":"n1820939357","loc":[-85.2781854,41.9946001]},"n1820939359":{"id":"n1820939359","loc":[-85.2139988,42.0405175]},"n1820939361":{"id":"n1820939361","loc":[-85.0086609,42.0908262]},"n1820939363":{"id":"n1820939363","loc":[-85.0627128,42.1043398]},"n1820939365":{"id":"n1820939365","loc":[-85.1311346,42.072501]},"n1820939369":{"id":"n1820939369","loc":[-85.248198,42.0082652]},"n1820939370":{"id":"n1820939370","loc":[-84.99792,42.087794]},"n1820939371":{"id":"n1820939371","loc":[-85.2786775,41.9942783]},"n1820939372":{"id":"n1820939372","loc":[-85.0342103,42.1013957]},"n1820939373":{"id":"n1820939373","loc":[-85.2022357,42.0444799]},"n1820939374":{"id":"n1820939374","loc":[-85.2279205,42.0337388]},"n1820939375":{"id":"n1820939375","loc":[-85.1337699,42.0712614]},"n1820939376":{"id":"n1820939376","loc":[-85.317517,41.9707062]},"n1820939377":{"id":"n1820939377","loc":[-85.1326326,42.070218]},"n1820939394":{"id":"n1820939394","loc":[-85.0197746,42.0899118]},"n1820939397":{"id":"n1820939397","loc":[-85.2590076,41.9984632]},"n1820939399":{"id":"n1820939399","loc":[-85.2469964,42.0083449]},"n1820939400":{"id":"n1820939400","loc":[-85.2470929,42.0146668]},"n1820939401":{"id":"n1820939401","loc":[-84.9984095,42.0878087]},"n1820939402":{"id":"n1820939402","loc":[-85.2372653,42.0243273]},"n1820939403":{"id":"n1820939403","loc":[-85.2454986,42.0091955]},"n1820939404":{"id":"n1820939404","loc":[-85.0539205,42.1035995]},"n1820939405":{"id":"n1820939405","loc":[-85.550601,41.9706101]},"n1820939406":{"id":"n1820939406","loc":[-85.0351343,42.0999656]},"n1820939407":{"id":"n1820939407","loc":[-85.0082908,42.0905755]},"n1820939408":{"id":"n1820939408","loc":[-85.0132904,42.0902251]},"n1820939410":{"id":"n1820939410","loc":[-85.0892546,42.094012]},"n1820939412":{"id":"n1820939412","loc":[-85.0350793,42.1030315]},"n1820939416":{"id":"n1820939416","loc":[-85.0012406,42.0886777]},"n1820939418":{"id":"n1820939418","loc":[-85.0577453,42.1029229]},"n1820939420":{"id":"n1820939420","loc":[-85.1230786,42.0776722]},"n1820939422":{"id":"n1820939422","loc":[-85.571136,41.9649304]},"n1820939436":{"id":"n1820939436","loc":[-85.1137968,42.0848997]},"n1820939437":{"id":"n1820939437","loc":[-85.3559584,41.9925105]},"n1820939438":{"id":"n1820939438","loc":[-85.0080172,42.0903565]},"n1820939439":{"id":"n1820939439","loc":[-85.0048897,42.0880913]},"n1820939441":{"id":"n1820939441","loc":[-85.0406959,42.1018574]},"n1820939443":{"id":"n1820939443","loc":[-85.3897328,42.0029078]},"n1820939445":{"id":"n1820939445","loc":[-85.122349,42.0782814]},"n1820939448":{"id":"n1820939448","loc":[-85.4872193,41.985036]},"n1820939450":{"id":"n1820939450","loc":[-85.0120459,42.0904919]},"n1820939452":{"id":"n1820939452","loc":[-85.6320543,41.921982]},"n1820939456":{"id":"n1820939456","loc":[-85.0844749,42.1036843]},"n1820939458":{"id":"n1820939458","loc":[-85.0968037,42.091296]},"n1820939463":{"id":"n1820939463","loc":[-85.5339747,41.9681841]},"n1820939465":{"id":"n1820939465","loc":[-85.4125423,42.0072129]},"n1820939467":{"id":"n1820939467","loc":[-85.6335563,41.9303626]},"n1820939469":{"id":"n1820939469","loc":[-85.2821014,41.9932126]},"n1820939471":{"id":"n1820939471","loc":[-85.374691,41.9969917]},"n1820939485":{"id":"n1820939485","loc":[-85.4471321,42.0049806]},"n1820939487":{"id":"n1820939487","loc":[-85.3752532,41.9972206]},"n1820939489":{"id":"n1820939489","loc":[-85.4517283,42.005927]},"n1820939492":{"id":"n1820939492","loc":[-85.4662552,42.0005693]},"n1820939494":{"id":"n1820939494","loc":[-85.0120083,42.0902928]},"n1820939496":{"id":"n1820939496","loc":[-85.044463,42.1004631]},"n1820939498":{"id":"n1820939498","loc":[-85.418293,42.0089667]},"n1820939500":{"id":"n1820939500","loc":[-85.0554762,42.1027358]},"n1820939504":{"id":"n1820939504","loc":[-85.1246289,42.0746858]},"n1820939507":{"id":"n1820939507","loc":[-85.0408139,42.1021838]},"n1820939508":{"id":"n1820939508","loc":[-85.1236204,42.0775169]},"n1820939509":{"id":"n1820939509","loc":[-85.0350109,42.1037428]},"n1820939510":{"id":"n1820939510","loc":[-85.0551583,42.1029878]},"n1820939511":{"id":"n1820939511","loc":[-85.0956771,42.0916662]},"n1820939512":{"id":"n1820939512","loc":[-85.2323408,42.0273638]},"n1820939513":{"id":"n1820939513","loc":[-85.1232771,42.0762388]},"n1820939531":{"id":"n1820939531","loc":[-85.264608,41.9997828]},"n1820939533":{"id":"n1820939533","loc":[-85.4198808,42.0087914]},"n1820939535":{"id":"n1820939535","loc":[-85.3080864,41.9715677]},"n1820939536":{"id":"n1820939536","loc":[-85.1189426,42.0812596]},"n1820939537":{"id":"n1820939537","loc":[-85.2642741,41.9996764]},"n1820939538":{"id":"n1820939538","loc":[-85.2572531,42.0079627]},"n1820939539":{"id":"n1820939539","loc":[-85.2907807,41.9790174]},"n1820939540":{"id":"n1820939540","loc":[-85.3171415,41.9707301]},"n1820939541":{"id":"n1820939541","loc":[-85.08777,42.0953841]},"n1820939542":{"id":"n1820939542","loc":[-85.1239262,42.0773218]},"n1820939543":{"id":"n1820939543","loc":[-84.9973956,42.0877968]},"n1820939544":{"id":"n1820939544","loc":[-85.011606,42.0896161]},"n1820939545":{"id":"n1820939545","loc":[-85.4077358,42.0082971]},"n1820939546":{"id":"n1820939546","loc":[-85.3614945,41.9933717]},"n1820939547":{"id":"n1820939547","loc":[-85.3189118,41.9697649]},"n1820939550":{"id":"n1820939550","loc":[-85.1262691,42.0740221]},"n1820939551":{"id":"n1820939551","loc":[-85.3863639,41.9994635]},"n1820939552":{"id":"n1820939552","loc":[-85.2836034,41.9923953]},"n1820939554":{"id":"n1820939554","loc":[-85.3222377,41.9715916]},"n1820939555":{"id":"n1820939555","loc":[-85.0122658,42.0906312]},"n1820939556":{"id":"n1820939556","loc":[-85.0022652,42.0877581]},"n1820939557":{"id":"n1820939557","loc":[-85.1011314,42.0899954]},"n1820939559":{"id":"n1820939559","loc":[-85.0008181,42.0885293]},"n1820939561":{"id":"n1820939561","loc":[-85.3637046,41.9942488]},"n1820939562":{"id":"n1820939562","loc":[-85.4500117,42.0052892]},"n1820939563":{"id":"n1820939563","loc":[-85.0537636,42.1036365]},"n1820939565":{"id":"n1820939565","loc":[-85.2367503,42.0246939]},"n1820939566":{"id":"n1820939566","loc":[-85.0448479,42.1002653]},"n1820939567":{"id":"n1820939567","loc":[-85.6337065,41.9295006]},"n1820939568":{"id":"n1820939568","loc":[-85.0879792,42.095623]},"n1820939569":{"id":"n1820939569","loc":[-85.6347623,41.9352369]},"n1820939570":{"id":"n1820939570","loc":[-85.1497931,42.0620378]},"n1820939571":{"id":"n1820939571","loc":[-85.5676169,41.9656324]},"n1820939572":{"id":"n1820939572","loc":[-85.638041,41.9166971]},"n1820939573":{"id":"n1820939573","loc":[-85.4993429,41.9781293]},"n1820939574":{"id":"n1820939574","loc":[-85.5352831,41.9692127]},"n1820939575":{"id":"n1820939575","loc":[-84.9924429,42.0857118]},"n1820939577":{"id":"n1820939577","loc":[-85.0581101,42.1026721]},"n1820939578":{"id":"n1820939578","loc":[-85.641088,41.9094477]},"n1820939579":{"id":"n1820939579","loc":[-85.2548821,42.0052282]},"n1820939580":{"id":"n1820939580","loc":[-85.1124463,42.0859734]},"n1820939581":{"id":"n1820939581","loc":[-85.1083479,42.0857624]},"n1820939583":{"id":"n1820939583","loc":[-85.1387424,42.0648893]},"n1820939584":{"id":"n1820939584","loc":[-85.5152645,41.9700892]},"n1820939585":{"id":"n1820939585","loc":[-85.5463738,41.9713439]},"n1820939586":{"id":"n1820939586","loc":[-85.360207,41.9933717]},"n1820939587":{"id":"n1820939587","loc":[-85.2402372,42.0120917]},"n1820939588":{"id":"n1820939588","loc":[-85.3936381,42.0047255]},"n1820939589":{"id":"n1820939589","loc":[-85.3310246,41.973784]},"n1820939590":{"id":"n1820939590","loc":[-85.0329403,42.096642]},"n1820939591":{"id":"n1820939591","loc":[-85.0097271,42.0910981]},"n1820939593":{"id":"n1820939593","loc":[-85.0446562,42.1003437]},"n1820939595":{"id":"n1820939595","loc":[-85.0856671,42.1008452]},"n1820939596":{"id":"n1820939596","loc":[-85.4087228,42.0083449]},"n1820939597":{"id":"n1820939597","loc":[-85.0609519,42.1052564]},"n1820939598":{"id":"n1820939598","loc":[-85.3432126,41.9874548]},"n1820939599":{"id":"n1820939599","loc":[-85.4041738,42.0067027]},"n1820939600":{"id":"n1820939600","loc":[-85.0825437,42.1035768]},"n1820939601":{"id":"n1820939601","loc":[-85.048422,42.101498]},"n1820939602":{"id":"n1820939602","loc":[-85.0336256,42.0999031]},"n1820939603":{"id":"n1820939603","loc":[-85.046818,42.1014104]},"n1820939605":{"id":"n1820939605","loc":[-85.2856524,41.98078]},"n1820939607":{"id":"n1820939607","loc":[-85.1118173,42.0864245]},"n1820939609":{"id":"n1820939609","loc":[-85.0443397,42.1006263]},"n1820939610":{"id":"n1820939610","loc":[-85.0336698,42.0978361]},"n1820939611":{"id":"n1820939611","loc":[-85.4630322,42.0014248]},"n1820939612":{"id":"n1820939612","loc":[-85.0613127,42.1052353]},"n1820939613":{"id":"n1820939613","loc":[-85.0137571,42.0887801]},"n1820939614":{"id":"n1820939614","loc":[-85.272487,41.9982013]},"n1820939616":{"id":"n1820939616","loc":[-85.4665727,41.9983791]},"n1820939617":{"id":"n1820939617","loc":[-85.1288078,42.0725476]},"n1820939618":{"id":"n1820939618","loc":[-85.4653282,42.00109]},"n1820939619":{"id":"n1820939619","loc":[-85.2314717,42.0276746]},"n1820939620":{"id":"n1820939620","loc":[-85.255982,42.0003569]},"n1820939621":{"id":"n1820939621","loc":[-85.2886779,41.9787223]},"n1820939622":{"id":"n1820939622","loc":[-85.22438,42.0367509]},"n1820939623":{"id":"n1820939623","loc":[-85.0334713,42.0998382]},"n1820939624":{"id":"n1820939624","loc":[-85.2236504,42.037484]},"n1820939625":{"id":"n1820939625","loc":[-85.636908,41.9175162]},"n1820939627":{"id":"n1820939627","loc":[-85.2669187,41.9989707]},"n1820939628":{"id":"n1820939628","loc":[-85.3247268,41.9720702]},"n1820939629":{"id":"n1820939629","loc":[-85.3785104,41.9987299]},"n1820939630":{"id":"n1820939630","loc":[-85.5267658,41.9720515]},"n1820939631":{"id":"n1820939631","loc":[-85.2445116,42.0098811]},"n1820939632":{"id":"n1820939632","loc":[-85.1271448,42.0725077]},"n1820939633":{"id":"n1820939633","loc":[-85.0345751,42.099724]},"n1820939634":{"id":"n1820939634","loc":[-85.4217476,42.0089986]},"n1820939635":{"id":"n1820939635","loc":[-85.3121848,41.9689433]},"n1820939636":{"id":"n1820939636","loc":[-85.2826419,41.9929985]},"n1820939637":{"id":"n1820939637","loc":[-85.3160257,41.9706344]},"n1820939638":{"id":"n1820939638","loc":[-85.5684967,41.9657919]},"n1820939640":{"id":"n1820939640","loc":[-85.225131,42.0356194]},"n1820939642":{"id":"n1820939642","loc":[-85.1324124,42.0693328]},"n1820939644":{"id":"n1820939644","loc":[-84.9994073,42.0878843]},"n1820939645":{"id":"n1820939645","loc":[-85.1087596,42.0863329]},"n1820939646":{"id":"n1820939646","loc":[-85.2915532,41.9782996]},"n1820939647":{"id":"n1820939647","loc":[-84.9988708,42.0877808]},"n1820939648":{"id":"n1820939648","loc":[-85.2243628,42.0356728]},"n1820939649":{"id":"n1820939649","loc":[-85.0427397,42.1020524]},"n1820939650":{"id":"n1820939650","loc":[-85.6388392,41.9100752]},"n1820939651":{"id":"n1820939651","loc":[-85.0133709,42.0888557]},"n1820939652":{"id":"n1820939652","loc":[-85.318798,41.9701211]},"n1820939653":{"id":"n1820939653","loc":[-85.6335778,41.9190602]},"n1820939654":{"id":"n1820939654","loc":[-85.6338396,41.9370247]},"n1820939655":{"id":"n1820939655","loc":[-85.0939069,42.0931988]},"n1820939656":{"id":"n1820939656","loc":[-85.5702347,41.9651378]},"n1820939657":{"id":"n1820939657","loc":[-85.4235286,42.0088392]},"n1820939658":{"id":"n1820939658","loc":[-85.2740856,41.9972206]},"n1820939659":{"id":"n1820939659","loc":[-85.4824299,41.9934195]},"n1820939660":{"id":"n1820939660","loc":[-85.3857846,42.0014408]},"n1820939661":{"id":"n1820939661","loc":[-85.0451658,42.10028]},"n1820939662":{"id":"n1820939662","loc":[-85.3893036,42.001377]},"n1820939664":{"id":"n1820939664","loc":[-85.2455845,42.0088607]},"n1820939665":{"id":"n1820939665","loc":[-85.2741071,41.9951116]},"n1820939666":{"id":"n1820939666","loc":[-85.1298375,42.0677718]},"n1820939667":{"id":"n1820939667","loc":[-85.5491848,41.9707377]},"n1820939669":{"id":"n1820939669","loc":[-85.2780298,41.995238]},"n1820939670":{"id":"n1820939670","loc":[-85.1330068,42.0716926]},"n1820939671":{"id":"n1820939671","loc":[-85.0811342,42.1025129]},"n1820939672":{"id":"n1820939672","loc":[-85.2325124,42.0290135]},"n1820939673":{"id":"n1820939673","loc":[-85.2975077,41.9716953]},"n1820939674":{"id":"n1820939674","loc":[-85.0951729,42.0922394]},"n1820939676":{"id":"n1820939676","loc":[-85.0363252,42.1043119]},"n1820939677":{"id":"n1820939677","loc":[-85.2960057,41.97349]},"n1820939678":{"id":"n1820939678","loc":[-85.3701849,41.9982515]},"n1820939679":{"id":"n1820939679","loc":[-85.3381486,41.9848861]},"n1820939680":{"id":"n1820939680","loc":[-85.2058448,42.0417286]},"n1820939682":{"id":"n1820939682","loc":[-85.0819335,42.1034443]},"n1820939683":{"id":"n1820939683","loc":[-85.3872223,41.9993359]},"n1820939684":{"id":"n1820939684","loc":[-85.095366,42.091909]},"n1820939685":{"id":"n1820939685","loc":[-85.2327914,42.0291888]},"n1820939686":{"id":"n1820939686","loc":[-85.0433459,42.1018773]},"n1820939687":{"id":"n1820939687","loc":[-85.0585339,42.1027318]},"n1820939688":{"id":"n1820939688","loc":[-85.0062885,42.0876347]},"n1820939689":{"id":"n1820939689","loc":[-85.246299,42.017377]},"n1820939690":{"id":"n1820939690","loc":[-85.2932376,41.9742877]},"n1820939691":{"id":"n1820939691","loc":[-85.2962846,41.9736815]},"n1820939692":{"id":"n1820939692","loc":[-85.6052365,41.9409193]},"n1820939693":{"id":"n1820939693","loc":[-85.2570536,42.0003341]},"n1820939694":{"id":"n1820939694","loc":[-85.0488458,42.1014064]},"n1820939695":{"id":"n1820939695","loc":[-85.4050321,42.0069578]},"n1820939696":{"id":"n1820939696","loc":[-85.4847517,41.9845894]},"n1820939697":{"id":"n1820939697","loc":[-85.0844655,42.1013826]},"n1820939698":{"id":"n1820939698","loc":[-85.1437206,42.0650008]},"n1820939699":{"id":"n1820939699","loc":[-85.1168183,42.0864034]},"n1820939700":{"id":"n1820939700","loc":[-85.5479831,41.9711366]},"n1820939701":{"id":"n1820939701","loc":[-85.0349948,42.1034124]},"n1820939702":{"id":"n1820939702","loc":[-85.0835589,42.1038821]},"n1820939703":{"id":"n1820939703","loc":[-85.0203875,42.0902649]},"n1820939704":{"id":"n1820939704","loc":[-85.0371191,42.1038184]},"n1820939705":{"id":"n1820939705","loc":[-85.1273312,42.0735681]},"n1820939707":{"id":"n1820939707","loc":[-85.1272239,42.0730226]},"n1820939710":{"id":"n1820939710","loc":[-85.0349881,42.1019012]},"n1820939712":{"id":"n1820939712","loc":[-85.2440459,42.0178313]},"n1820939713":{"id":"n1820939713","loc":[-85.2444751,42.0182618]},"n1820939714":{"id":"n1820939714","loc":[-85.0539996,42.1032863]},"n1820939715":{"id":"n1820939715","loc":[-85.2215905,42.0373246]},"n1820939716":{"id":"n1820939716","loc":[-85.0649712,42.1051994]},"n1820939717":{"id":"n1820939717","loc":[-85.0927146,42.0927581]},"n1820939718":{"id":"n1820939718","loc":[-85.3884668,42.0042312]},"n1820939719":{"id":"n1820939719","loc":[-85.0840672,42.1013241]},"n1820939720":{"id":"n1820939720","loc":[-85.304739,41.9725408]},"n1820939721":{"id":"n1820939721","loc":[-85.2243585,42.0371334]},"n1820939722":{"id":"n1820939722","loc":[-85.0599823,42.1049686]},"n1820939723":{"id":"n1820939723","loc":[-85.0298825,42.0944288]},"n1820939724":{"id":"n1820939724","loc":[-85.0366095,42.1042443]},"n1820939725":{"id":"n1820939725","loc":[-85.0698783,42.1058135]},"n1820939726":{"id":"n1820939726","loc":[-85.1054551,42.0873361]},"n1820939727":{"id":"n1820939727","loc":[-84.9952324,42.0864285]},"n1820939728":{"id":"n1820939728","loc":[-85.3442211,41.9897993]},"n1820939729":{"id":"n1820939729","loc":[-85.4386134,42.0056822]},"n1820939730":{"id":"n1820939730","loc":[-85.2438528,42.0146589]},"n1820939731":{"id":"n1820939731","loc":[-85.0355581,42.1041846]},"n1820939732":{"id":"n1820939732","loc":[-85.557682,41.9724447]},"n1820939734":{"id":"n1820939734","loc":[-85.2299418,42.033314]},"n1820939735":{"id":"n1820939735","loc":[-85.6297412,41.9419088]},"n1820939736":{"id":"n1820939736","loc":[-85.2645101,41.9980259]},"n1820939738":{"id":"n1820939738","loc":[-85.082195,42.1035649]},"n1820939739":{"id":"n1820939739","loc":[-85.234272,42.0267102]},"n1820939740":{"id":"n1820939740","loc":[-85.0130758,42.0895006]},"n1820939741":{"id":"n1820939741","loc":[-85.4594702,42.0000375]},"n1820939742":{"id":"n1820939742","loc":[-84.9946745,42.0863687]},"n1820939743":{"id":"n1820939743","loc":[-85.6438775,41.9120186]},"n1820939744":{"id":"n1820939744","loc":[-85.6372685,41.9168089]},"n1820939745":{"id":"n1820939745","loc":[-85.2789468,41.9893208]},"n1820939747":{"id":"n1820939747","loc":[-85.3775019,41.998427]},"n1820939749":{"id":"n1820939749","loc":[-85.0993571,42.0909178]},"n1820939750":{"id":"n1820939750","loc":[-85.1308503,42.0669339]},"n1820939751":{"id":"n1820939751","loc":[-85.4802566,41.9856659]},"n1820939752":{"id":"n1820939752","loc":[-85.2543563,42.0108804]},"n1820939753":{"id":"n1820939753","loc":[-85.1041033,42.0878815]},"n1820939755":{"id":"n1820939755","loc":[-85.4000969,42.0071651]},"n1820939757":{"id":"n1820939757","loc":[-85.3858275,42.0022381]},"n1820939758":{"id":"n1820939758","loc":[-85.3653998,41.996609]},"n1820939759":{"id":"n1820939759","loc":[-85.2432949,42.0202305]},"n1820939760":{"id":"n1820939760","loc":[-85.3878874,42.0042472]},"n1820939761":{"id":"n1820939761","loc":[-85.2516741,42.0114145]},"n1820939762":{"id":"n1820939762","loc":[-85.2788825,41.9865142]},"n1820939763":{"id":"n1820939763","loc":[-85.0009147,42.0886686]},"n1820939764":{"id":"n1820939764","loc":[-85.3918142,42.003434]},"n1820939765":{"id":"n1820939765","loc":[-85.5532832,41.9696848]},"n1820939766":{"id":"n1820939766","loc":[-85.5545063,41.969254]},"n1820939768":{"id":"n1820939768","loc":[-85.1327989,42.0704769]},"n1820939770":{"id":"n1820939770","loc":[-85.0588558,42.1047696]},"n1820939772":{"id":"n1820939772","loc":[-85.555798,41.9713017]},"n1820939773":{"id":"n1820939773","loc":[-85.0565853,42.1023589]},"n1820939774":{"id":"n1820939774","loc":[-85.2582941,41.9992765]},"n1820939775":{"id":"n1820939775","loc":[-85.3007264,41.9727642]},"n1820939776":{"id":"n1820939776","loc":[-85.2477045,42.0082652]},"n1820939777":{"id":"n1820939777","loc":[-85.2415247,42.0104973]},"n1821006698":{"id":"n1821006698","loc":[-85.6345227,41.9382009]},"n1821006700":{"id":"n1821006700","loc":[-85.6344894,41.938975]},"n1821006704":{"id":"n1821006704","loc":[-85.6351181,41.9370157]},"n1821006706":{"id":"n1821006706","loc":[-85.6357554,41.9361657]},"n1821006708":{"id":"n1821006708","loc":[-85.6351235,41.9368481]},"n1821006710":{"id":"n1821006710","loc":[-85.6352844,41.9364211]},"n1821006712":{"id":"n1821006712","loc":[-85.6351503,41.937307]},"n1821006716":{"id":"n1821006716","loc":[-85.6350366,41.9379774]},"n1821006725":{"id":"n1821006725","loc":[-85.6352147,41.9375903]},"n1821137607":{"id":"n1821137607","loc":[-85.5297057,41.9669915]},"n1821137608":{"id":"n1821137608","loc":[-85.5288598,41.9673094]},"n1821139530":{"id":"n1821139530","loc":[-85.4832228,41.9881686]},"n1821139531":{"id":"n1821139531","loc":[-85.4812101,41.9851258]},"n1821139532":{"id":"n1821139532","loc":[-85.4799127,41.9860244]},"n1821139533":{"id":"n1821139533","loc":[-85.4800313,41.9865555]},"n1841425201":{"id":"n1841425201","loc":[-85.4334577,42.0063713]},"n1841425222":{"id":"n1841425222","loc":[-85.4382449,42.0055785]},"n1914861007":{"id":"n1914861007","loc":[-85.394959,42.0057472]},"n1914861057":{"id":"n1914861057","loc":[-85.3967185,42.0049695]},"n1914861112":{"id":"n1914861112","loc":[-85.394179,42.0056906]},"n1914861306":{"id":"n1914861306","loc":[-85.3900226,42.0028488]},"n2114807565":{"id":"n2114807565","loc":[-85.6385979,41.9577824]},"n2114807568":{"id":"n2114807568","loc":[-85.6325097,41.9775713]},"n2114807572":{"id":"n2114807572","loc":[-85.6328996,41.9980965]},"n2114807578":{"id":"n2114807578","loc":[-85.6344818,41.9696956]},"n2114807583":{"id":"n2114807583","loc":[-85.6326289,41.9757853]},"n2114807593":{"id":"n2114807593","loc":[-85.6360828,41.9650674]},"n2130304159":{"id":"n2130304159","loc":[-85.6352537,41.9450015],"tags":{"railway":"level_crossing"}},"n2139795852":{"id":"n2139795852","loc":[-85.6374708,41.9311633]},"n2139858882":{"id":"n2139858882","loc":[-85.635178,41.9356158]},"n2139858883":{"id":"n2139858883","loc":[-85.63533,41.9355886]},"n2139858884":{"id":"n2139858884","loc":[-85.6353819,41.93556]},"n2139858885":{"id":"n2139858885","loc":[-85.6353665,41.9355157]},"n2139858886":{"id":"n2139858886","loc":[-85.6353165,41.9354971]},"n2139858887":{"id":"n2139858887","loc":[-85.6352454,41.9355328]},"n2139858888":{"id":"n2139858888","loc":[-85.6350184,41.9357846]},"n2139858889":{"id":"n2139858889","loc":[-85.634978,41.9359448]},"n2139858890":{"id":"n2139858890","loc":[-85.6347723,41.9361523]},"n2139858891":{"id":"n2139858891","loc":[-85.6347165,41.9362667]},"n2139858892":{"id":"n2139858892","loc":[-85.6346992,41.9364312]},"n2139858893":{"id":"n2139858893","loc":[-85.634603,41.9366329]},"n2139858894":{"id":"n2139858894","loc":[-85.6345973,41.9367488]},"n2139858895":{"id":"n2139858895","loc":[-85.6345127,41.9369734]},"n2139858896":{"id":"n2139858896","loc":[-85.634478,41.9371923]},"n2139858897":{"id":"n2139858897","loc":[-85.6344838,41.9373768]},"n2139858898":{"id":"n2139858898","loc":[-85.6346242,41.9375299]},"n2139858899":{"id":"n2139858899","loc":[-85.6347723,41.9376357]},"n2139858900":{"id":"n2139858900","loc":[-85.6347607,41.9377788]},"n2139858901":{"id":"n2139858901","loc":[-85.6346204,41.9379533]},"n2139858902":{"id":"n2139858902","loc":[-85.6344184,41.9380105]},"n2139858903":{"id":"n2139858903","loc":[-85.6341627,41.9380406]},"n2139858904":{"id":"n2139858904","loc":[-85.634005,41.9381679]},"n2139858905":{"id":"n2139858905","loc":[-85.63393,41.9383353]},"n2139858906":{"id":"n2139858906","loc":[-85.6338588,41.9384597]},"n2139858907":{"id":"n2139858907","loc":[-85.6336627,41.9387759]},"n2139858908":{"id":"n2139858908","loc":[-85.6335127,41.9389361]},"n2139858933":{"id":"n2139858933","loc":[-85.6353118,41.9432646]},"n2139858934":{"id":"n2139858934","loc":[-85.6353952,41.9433002]},"n2139858935":{"id":"n2139858935","loc":[-85.6356496,41.9433255]},"n2139858936":{"id":"n2139858936","loc":[-85.6363128,41.9433373]},"n2139858937":{"id":"n2139858937","loc":[-85.6365467,41.9433779]},"n2139858938":{"id":"n2139858938","loc":[-85.6368692,41.9435265]},"n2139858939":{"id":"n2139858939","loc":[-85.6370986,41.9437039]},"n2139858940":{"id":"n2139858940","loc":[-85.6372371,41.9437732]},"n2139858941":{"id":"n2139858941","loc":[-85.6374756,41.9438171]},"n2139858942":{"id":"n2139858942","loc":[-85.6376164,41.9439286]},"n2139858943":{"id":"n2139858943","loc":[-85.6377504,41.944138]},"n2139858944":{"id":"n2139858944","loc":[-85.6384204,41.9443137]},"n2139858945":{"id":"n2139858945","loc":[-85.6385726,41.9444506]},"n2139858946":{"id":"n2139858946","loc":[-85.638702,41.9445739]},"n2139858947":{"id":"n2139858947","loc":[-85.6387179,41.9446516]},"n2139858948":{"id":"n2139858948","loc":[-85.6387088,41.9447985]},"n2139858949":{"id":"n2139858949","loc":[-85.6387656,41.9449877]},"n2139858950":{"id":"n2139858950","loc":[-85.638777,41.9451448]},"n2139858951":{"id":"n2139858951","loc":[-85.6387088,41.9452631]},"n2139858964":{"id":"n2139858964","loc":[-85.6383346,41.9442912]},"n2139858966":{"id":"n2139858966","loc":[-85.6384724,41.9443605]},"n2139858967":{"id":"n2139858967","loc":[-85.6354078,41.9434285]},"n2139858968":{"id":"n2139858968","loc":[-85.635271,41.943654]},"n2139858969":{"id":"n2139858969","loc":[-85.6352657,41.9437437]},"n2139858970":{"id":"n2139858970","loc":[-85.635271,41.9438195]},"n2139858971":{"id":"n2139858971","loc":[-85.6351563,41.9438906]},"n2139858972":{"id":"n2139858972","loc":[-85.6351384,41.9438882]},"n2139858973":{"id":"n2139858973","loc":[-85.6351514,41.9438034]},"n2139858974":{"id":"n2139858974","loc":[-85.6351237,41.9436641]},"n2139858975":{"id":"n2139858975","loc":[-85.6351498,41.9436108]},"n2139858976":{"id":"n2139858976","loc":[-85.6351058,41.9435345]},"n2139858977":{"id":"n2139858977","loc":[-85.6349641,41.9432051]},"n2139858986":{"id":"n2139858986","loc":[-85.6341205,41.9380746]},"n2139858990":{"id":"n2139858990","loc":[-85.6345671,41.9381816]},"n2139858995":{"id":"n2139858995","loc":[-85.6339783,41.9382273]},"n2139859003":{"id":"n2139859003","loc":[-85.6340477,41.9373489]},"n2139859004":{"id":"n2139859004","loc":[-85.6339784,41.9374752]},"n2139870406":{"id":"n2139870406","loc":[-85.6342265,41.9432605]},"n2139877106":{"id":"n2139877106","loc":[-85.6346323,41.9438746]},"n2139982399":{"id":"n2139982399","loc":[-85.6324055,41.9408537]},"n2139982400":{"id":"n2139982400","loc":[-85.632488,41.941063],"tags":{"leisure":"slipway"}},"n2139982401":{"id":"n2139982401","loc":[-85.6327261,41.9415366]},"n2139982402":{"id":"n2139982402","loc":[-85.6326391,41.9413598]},"n2139982403":{"id":"n2139982403","loc":[-85.6327041,41.9414391]},"n2139982405":{"id":"n2139982405","loc":[-85.6322891,41.9406009]},"n2139982406":{"id":"n2139982406","loc":[-85.6325412,41.9425257]},"n2139989333":{"id":"n2139989333","loc":[-85.6340584,41.9431731]},"n2140006331":{"id":"n2140006331","loc":[-85.6361751,41.9459744]},"n2140006334":{"id":"n2140006334","loc":[-85.636528,41.9459751]},"n2140006336":{"id":"n2140006336","loc":[-85.6370918,41.9458926]},"n2140006338":{"id":"n2140006338","loc":[-85.6378806,41.9456474]},"n2140006340":{"id":"n2140006340","loc":[-85.6385831,41.9454343]},"n2140006342":{"id":"n2140006342","loc":[-85.639341,41.945157]},"n2140006344":{"id":"n2140006344","loc":[-85.6393497,41.9450232]},"n2140006346":{"id":"n2140006346","loc":[-85.6388245,41.9450145]},"n2140006348":{"id":"n2140006348","loc":[-85.6388167,41.9441739]},"n2140006351":{"id":"n2140006351","loc":[-85.6382915,41.9441797]},"n2140006353":{"id":"n2140006353","loc":[-85.63828,41.9438109]},"n2140006355":{"id":"n2140006355","loc":[-85.6381949,41.9436009]},"n2140006357":{"id":"n2140006357","loc":[-85.6371904,41.9435918]},"n2140006359":{"id":"n2140006359","loc":[-85.6366966,41.9432727]},"n2140006361":{"id":"n2140006361","loc":[-85.6353755,41.9432744]},"n2140006365":{"id":"n2140006365","loc":[-85.6350906,41.9435472]},"n2140006366":{"id":"n2140006366","loc":[-85.6343461,41.9441573]},"n2140006395":{"id":"n2140006395","loc":[-85.6351171,41.9437175]},"n2140006397":{"id":"n2140006397","loc":[-85.635352,41.9450206]},"n2140006399":{"id":"n2140006399","loc":[-85.6358194,41.9454937]},"n2140006401":{"id":"n2140006401","loc":[-85.6348693,41.9445739]},"n2140006431":{"id":"n2140006431","loc":[-85.6376737,41.9438023]},"n2140006437":{"id":"n2140006437","loc":[-85.6382631,41.9442724]},"n2189123379":{"id":"n2189123379","loc":[-85.6342671,41.9352665]},"w203974076":{"id":"w203974076","tags":{"highway":"footway"},"nodes":["n2139870442","n2139870457","n2139870458","n2139870459","n2139870460","n2139870452"]},"w170989131":{"id":"w170989131","tags":{"name":"St Joseph River","waterway":"river"},"nodes":["n1820938225","n1820938712","n1820937596","n1820937574","n1820938515","n1820938330","n1820938678","n1820938240","n1820938950","n1820939226","n1820939575","n1820937913","n1820938223","n1820937668","n1820938545","n1820937584","n1820939742","n1820939727","n1820937578","n1820938149","n1820938124","n1820938888","n1820938898","n1820937922","n1820939543","n1820939370","n1820939401","n1820939647","n1820938345","n1820939644","n1820938333","n1820938370","n1820938624","n1820938493","n1820939559","n1820939763","n1820939237","n1820939416","n1820937810","n1820938317","n1820938324","n1820937558","n1820939556","n1820938298","n1820939348","n1820939125","n1820939081","n1820938859","n1820939126","n1820938881","n1820939439","n1820939324","n1820939128","n1820938101","n1820937706","n1820938382","n1820938776","n1820937815","n1820939177","n1820939688","n1820938952","n1820938216","n1820938387","n1820939333","n1820938243","n1820938248","n1820937666","n1820939051","n1820938332","n1820939438","n1820939407","n1820939361","n1820937517","n1820938770","n1820939591","n1820937857","n1820938491","n1820937993","n1820938125","n1820938166","n1820937746","n1820939028","n1820937638","n1820938676","n1820938843","n1820938844","n1820937978","n1820938730","n1820939544","n1820938304","n1820939123","n1820939494","n1820939450","n1820939555","n1820938133","n1820938129","n1820938871","n1820939408","n1820938669","n1820938260","n1820939740","n1820937625","n1820938631","n1820939651","n1820939613","n1820937850","n1820938325","n1820937736","n1820938804","n1820938837","n1820938014","n1820938991","n1820938722","n1820938935","n1820937870","n1820938432","n1820937986","n1820938756","n1820938966","n1820939159","n1820937744","n1820938334","n1820937645","n1820939394","n1820937656","n1820938392","n1820939703","n1820938385","n1820938947","n1820938854","n1820938428","n1820938488","n1820938269","n1820938668","n1820938268","n1820938707","n1820937732","n1820939144","n1820938481","n1820938771","n1820938686","n1820938948","n1820937997","n1820937769","n1820939003","n1820938083","n1820939011","n1820938803","n1820938700","n1820939723","n1820938808","n1820938262","n1820938081","n1820938926","n1820938326","n1820938102","n1820938508","n1820939590","n1820939199","n1820938084","n1820938870","n1820938895","n1820937611","n1820938918","n1820938514","n1820939610","n1820938910","n1820937523","n1820938127","n1820939108","n1820937981","n1820938938","n1820938715","n1820939016","n1820938237","n1820939623","n1820939602","n1820937734","n1820938977","n1820939633","n1820939156","n1820939406","n1820938279","n1820938301","n1820937678","n1820937671","n1820939163","n1820938356","n1820939372","n1820937568","n1820937626","n1820939710","n1820939004","n1820938253","n1820938571","n1820937513","n1820939412","n1820939701","n1820939509","n1820938839","n1820939731","n1820937798","n1820939676","n1820939724","n1820939243","n1820939704","n1820937814","n1820937599","n1820938199","n1820938995","n1820938445","n1820938069","n1820938470","n1820939074","n1820938193","n1820938740","n1820938047","n1820939507","n1820939441","n1820939160","n1820937849","n1820937840","n1820938052","n1820938988","n1820938796","n1820937724","n1820937620","n1820939304","n1820938343","n1820939649","n1820938875","n1820939686","n1820938476","n1820937801","n1820937737","n1820938264","n1820939609","n1820939496","n1820939593","n1820939566","n1820939661","n1820937782","n1820938912","n1820939173","n1820937733","n1820938953","n1820939603","n1820937607","n1820938468","n1820939601","n1820939694","n1820939133","n1820938897","n1820938893","n1820937831","n1820937730","n1820938820","n1820938046","n1820938426","n1820938347","n1820937582","n1820938954","n1820938033","n1820938104","n1820938680","n1820939563","n1820939404","n1820939714","n1820939000","n1820937992","n1820938168","n1820939510","n1820939500","n1820937509","n1820938865","n1820939773","n1820938138","n1820938905","n1820937623","n1820939418","n1820937946","n1820939577","n1820937615","n1820939687","n1820939119","n1820937988","n1820938337","n1820937750","n1820938703","n1820938339","n1820939044","n1820939770","n1820938913","n1820937672","n1820939722","n1820937768","n1820939597","n1820939612","n1820937699","n1820937682","n1820937669","n1820937657","n1820939363","n1820937800","n1820938265","n1820937760","n1820938207","n1820938115","n1820939130","n1820939716","n1820938338","n1820938239","n1820939040","n1820938064","n1820938855","n1820939015","n1820938258","n1820939042","n1820939043","n1820938443","n1820939725","n1820937675","n1820938568","n1820938280","n1820937705","n1820938775","n1820938636","n1820938626","n1820937859","n1820938096","n1820937852","n1820939039","n1820938247","n1820938585","n1820937707","n1820938117","n1820938909","n1820939115","n1820939335","n1820938805","n1820937935","n1820937876","n1820938699","n1820937869","n1820938603","n1820938100","n1820938500","n1820938283","n1820938275","n1820938923","n1820938365","n1820938349","n1820937804","n1820937903","n1820937608","n1820938688","n1820939671","n1820938092","n1820937820","n1820938753","n1820938922","n1820937990","n1820939682","n1820939738","n1820939600","n1820938167","n1820937726","n1820939702","n1820938209","n1820939456","n1820937837","n1820938222","n1820938902","n1820939162","n1820938965","n1820938461","n1820937681","n1820937514","n1820937764","n1820939719","n1820939697","n1820938899","n1820939093","n1820938702","n1820939595","n1820938749","n1820938348","n1820937606","n1820938675","n1820938830","n1820938737","n1820938758","n1820938716","n1820939107","n1820937863","n1820939033","n1820938163","n1820937867","n1820938819","n1820938034","n1820938252","n1820937563","n1820937868","n1820939032","n1820938632","n1820937982","n1820937943","n1820939568","n1820939541","n1820938215","n1820939097","n1820938812","n1820937518","n1820937952","n1820938711","n1820938736","n1820939066","n1820937591","n1820938082","n1820938108","n1820938496","n1820939410","n1820938949","n1820938327","n1820937708","n1820939023","n1820937772","n1820938256","n1820939083","n1820938378","n1820938961","n1820937610","n1820939717","n1820938695","n1820938590","n1820939655","n1820938341","n1820939054","n1820939157","n1820939674","n1820939684","n1820939511","n1820937631","n1820939458","n1820937830","n1820937709","n1820937779","n1820939749","n1820938880","n1820938856","n1820938557","n1820939557","n1820938249","n1820938818","n1820937594","n1820939114","n1820938416","n1820937508","n1820938990","n1820938201","n1820937759","n1820937987","n1820939164","n1820939753","n1820938187","n1820939067","n1820937586","n1820937941","n1820938121","n1820937807","n1820938521","n1820939726","n1820938244","n1820939014","n1820938741","n1820937629","n1820938664","n1820938747","n1820939082","n1820938709","n1820938320","n1820938270","n1820937619","n1820937777","n1820937718","n1820939138","n1820938056","n1820938155","n1820938596","n1820937775","n1820938437","n1820938128","n1820939581","n1820939145","n1820938546","n1820938184","n1820937601","n1820937794","n1820938539","n1820939645","n1820938438","n1820938436","n1820939025","n1820938915","n1820938534","n1820937605","n1820939607","n1820939101","n1820939580","n1820939268","n1820939134","n1820938849","n1820938754","n1820938079","n1820937842","n1820938781","n1820938873","n1820938495","n1820938381","n1820938503","n1820939436","n1820938502","n1820939087","n1820938996","n1820938449","n1820938907","n1820937979","n1820937780","n1820937546","n1820939699","n1820937677","n1820938957","n1820938946","n1820937776","n1820937717","n1820938718","n1820937637","n1820938510","n1820937663","n1820938941","n1820939151","n1820937603","n1820938250","n1820937951","n1820938630","n1820938821","n1820938779","n1820938497","n1820938159","n1820939536","n1820938409","n1820938386","n1820939116","n1820938340","n1820939117","n1820938291","n1820938435","n1820937819","n1820938242","n1820939078","n1820938877","n1820939104","n1820939445","n1820938367","n1820938903","n1820939420","n1820938517","n1820939508","n1820939542","n1820939326","n1820938210","n1820939020","n1820938815","n1820937832","n1820939513","n1820937818","n1820939005","n1820938717","n1820939135","n1820938384","n1820937587","n1820939024","n1820939504","n1820939120","n1820939026","n1820938015","n1820938998","n1820937648","n1820939137","n1820937761","n1820938195","n1820938535","n1820939550","n1820938725","n1820938282","n1820937781","n1820937792","n1820939705","n1820937788","n1820939707","n1820937882","n1820939632","n1820938427","n1820938276","n1820939617","n1820939013","n1820939035","n1820937543","n1820939365","n1820937752","n1820937802","n1820939183","n1820939670","n1820938450","n1820939375","n1820937813","n1820937673","n1820937783","n1820939029","n1820939768","n1820939377","n1820937974","n1820939244","n1820939642","n1820937864","n1820938255","n1820938528","n1820939666","n1820938120","n1820937812","n1820938928","n1820939750","n1820939099","n1820938073","n1820938714","n1820939140","n1820938192","n1820937844","n1820938635","n1820938742","n1820939583","n1820937887","n1820938318","n1820938816","n1820939698","n1820938273","n1820939181","n1820937652","n1820938748","n1820937651","n1820938519","n1820938019","n1820938752","n1820938235","n1820939118","n1820938562","n1820939314","n1820939570","n1820938190","n1820938342","n1820938533","n1820937977","n1820939089","n1820939146","n1820938622","n1820938297","n1820938524","n1820939283","n1820938874","n1820938832","n1820937550","n1820937843","n1820938638","n1820938116","n1820938206","n1820938319","n1820939053","n1820937845","n1820938093","n1820939217","n1820938997","n1820939355","n1820938861","n1820938726","n1820938057","n1820939373","n1820937862","n1820938518","n1820939072","n1820939680","n1820938444","n1820938217","n1820938506","n1820938393","n1820938492","n1820938852","n1820938221","n1820938773","n1820937684","n1820939060","n1820938224","n1820938203","n1820938840","n1820937525","n1820938147","n1820938433","n1820938188","n1820939359","n1820938750","n1820938016","n1820938768","n1820937621","n1820937799","n1820938951","n1820938721","n1820939037","n1820937866","n1820939715","n1820938063","n1820938446","n1820937627","n1820939624","n1820938431","n1820939721","n1820939622","n1820939239","n1820939263","n1820939648","n1820939640","n1820938867","n1820938757","n1820938439","n1820939352","n1820937740","n1820939329","n1820938229","n1820937583","n1820938180","n1820938366","n1820937767","n1820937758","n1820939374","n1820938869","n1820938292","n1820938400","n1820938399","n1820939734","n1820939289","n1820938944","n1820937755","n1820938759","n1820938434","n1820937600","n1820937825","n1820937670","n1820937793","n1820938011","n1820938246","n1820938956","n1820937770","n1820937757","n1820938059","n1820937860","n1820937569","n1820939266","n1820939685","n1820939672","n1820938606","n1820938772","n1820939038","n1820938211","n1820938359","n1820939619","n1820938708","n1820939512","n1820938065","n1820939233","n1820939739","n1820938786","n1820938879","n1820939147","n1820938563","n1820939148","n1820937839","n1820937659","n1820937786","n1820938419","n1820939565","n1820939402","n1820937710","n1820938254","n1820938271","n1820938390","n1820937680","n1820938140","n1820937817","n1820938218","n1820937985","n1820939235","n1820938441","n1820938401","n1820938719","n1820937795","n1820938971","n1820938460","n1820939759","n1820937972","n1820937841","n1820938462","n1820939320","n1820938978","n1820938360","n1820939713","n1820937676","n1820939712","n1820937939","n1820938080","n1820937754","n1820937753","n1820938530","n1820937886","n1820939689","n1820939124","n1820938697","n1820938789","n1820939105","n1820938860","n1820938853","n1820939400","n1820937561","n1820938404","n1820938774","n1820939316","n1820937696","n1820938782","n1820938975","n1820937564","n1820939730","n1820938257","n1820937853","n1820938487","n1820938848","n1820938906","n1820939230","n1820938424","n1820938051","n1820937771","n1820939587","n1820939149","n1820938792","n1820939041","n1820938934","n1820939777","n1820937515","n1820939058","n1820938312","n1820939264","n1820939631","n1820939109","n1820939403","n1820939664","n1820938724","n1820938929","n1820939399","n1820939776","n1820939369","n1820939185","n1820937701","n1820938126","n1820938336","n1820938219","n1820939080","n1820938642","n1820938043","n1820937725","n1820938548","n1820938552","n1820938035","n1820938684","n1820937778","n1820938764","n1820939021","n1820939346","n1820937712","n1820939761","n1820938397","n1820937747","n1820938566","n1820939161","n1820939090","n1820939752","n1820939271","n1820938878","n1820938110","n1820938346","n1820938499","n1820938151","n1820939538","n1820938281","n1820939153","n1820938551","n1820939285","n1820938197","n1820938408","n1820938482","n1820939036","n1820939579","n1820938489","n1820938483","n1820938189","n1820938123","n1820938087","n1820937741","n1820938485","n1820937590","n1820938972","n1820937773","n1820937520","n1820938872","n1820938131","n1820938452","n1820938328","n1820939620","n1820937641","n1820938353","n1820939693","n1820938705","n1820937640","n1820939189","n1820938144","n1820939774","n1820938694","n1820938238","n1820939397","n1820937917","n1820938454","n1820938567","n1820938979","n1820938060","n1820938204","n1820937828","n1820939232","n1820938806","n1820938857","n1820938078","n1820938105","n1820939228","n1820938604","n1820937763","n1820937854","n1820938289","n1820939736","n1820937937","n1820937714","n1820938278","n1820938058","n1820938706","n1820938989","n1820938313","n1820938520","n1820938288","n1820937689","n1820939537","n1820939531","n1820939019","n1820937527","n1820938455","n1820938814","n1820938045","n1820939627","n1820938213","n1820938161","n1820938331","n1820938024","n1820938220","n1820938062","n1820938178","n1820937796","n1820937644","n1820938490","n1820937589","n1820937879","n1820939614","n1820938882","n1820938039","n1820938538","n1820937667","n1820937719","n1820938561","n1820939658","n1820938783","n1820938601","n1820938198","n1820938388","n1820938969","n1820937687","n1820939086","n1820939665","n1820939187","n1820938498","n1820938261","n1820937983","n1820938068","n1820938136","n1820939061","n1820938137","n1820938186","n1820939071","n1820937592","n1820939669","n1820937553","n1820939357","n1820938727","n1820939371","n1820939112","n1820939079","n1820938743","n1820938467","n1820938834","n1820938022","n1820938537","n1820938122","n1820938516","n1820937614","n1820937612","n1820939469","n1820939636","n1820939050","n1820939552","n1820938157","n1820938663","n1820938955","n1820939091","n1820938430","n1820938471","n1820937809","n1820938074","n1820938208","n1820938914","n1820938858","n1820938417","n1820937531","n1820938107","n1820939100","n1820938751","n1820937711","n1820938824","n1820939745","n1820937572","n1820938602","n1820938212","n1820938097","n1820937921","n1820938090","n1820938511","n1820938876","n1820939762","n1820938234","n1820938048","n1820937774","n1820937856","n1820937749","n1820937765","n1820938286","n1820939095","n1820938480","n1820939229","n1820938277","n1820937617","n1820938311","n1820937622","n1820939196","n1820937690","n1820939006","n1820939287","n1820939131","n1820938106","n1820938784","n1820938335","n1820938095","n1820938182","n1820937715","n1820937683","n1820938070","n1820939605","n1820938527","n1820938763","n1820938398","n1820937686","n1820939621","n1820937664","n1820939277","n1820938565","n1820939539","n1820938099","n1820939646","n1820938556","n1820937548","n1820938729","n1820939336","n1820938259","n1820938728","n1820938361","n1820937643","n1820938644","n1820939007","n1820939690","n1820939227","n1820937635","n1820937950","n1820938682","n1820939150","n1820939012","n1820939261","n1820939111","n1820937805","n1820939691","n1820939677","n1820937628","n1820937811","n1820938790","n1820938251","n1820938226","n1820938942","n1820937633","n1820937984","n1820937751","n1820939673","n1820938970","n1820938415","n1820938597","n1820938309","n1820938111","n1820938472","n1820938894","n1820938402","n1820937593","n1820938570","n1820939102","n1820939775","n1820937948","n1820939121","n1820937511","n1820938787","n1820939720","n1820939075","n1820937880","n1820937742","n1820937721","n1820939535","n1820938486","n1820938354","n1820937632","n1820939010","n1820938885","n1820938089","n1820937613","n1820938442","n1820938245","n1820938272","n1820937566","n1820938295","n1820938532","n1820938883","n1820937713","n1820937674","n1820939635","n1820938448","n1820938355","n1820938587","n1820938559","n1820937787","n1820939301","n1820937723","n1820939056","n1820937560","n1820938323","n1820938230","n1820938453","n1820938377","n1820938357","n1820939637","n1820938017","n1820939540","n1820939376","n1820937639","n1820937642","n1820938075","n1820938351","n1820938766","n1820937897","n1820938973","n1820938066","n1820939547","n1820939652","n1820937944","n1820937748","n1820939234","n1820939193","n1820937891","n1820938785","n1820939132","n1820938523","n1820938884","n1820938411","n1820939554","n1820938791","n1820937655","n1820938368","n1820939152","n1820938030","n1820938447","n1820937580","n1820939628","n1820937588","n1820937894","n1820939201","n1820938086","n1820937650","n1820938379","n1820939008","n1820938999","n1820937524","n1820937872","n1820938389","n1820939197","n1820938422","n1820938936","n1820939262","n1820937634","n1820938583","n1820939589","n1820937901","n1820939034","n1820939065","n1820938290","n1820939195","n1820938228","n1820937884","n1820938797","n1820938191","n1820939191","n1820939198","n1820937892","n1820939679","n1820938507","n1820937647","n1820937909","n1820938542","n1820939598","n1820937851","n1820939084","n1820939728","n1820937688","n1820938263","n1820938670","n1820937762","n1820939310","n1820938925","n1820938862","n1820938822","n1820938547","n1820937731","n1820938594","n1820938592","n1820938214","n1820938284","n1820937835","n1820938599","n1820939437","n1820937834","n1820937576","n1820937692","n1820939586","n1820939546","n1820938403","n1820937970","n1820939561","n1820938098","n1820938851","n1820938477","n1820938892","n1820939045","n1820939758","n1820939350","n1820938321","n1820938440","n1820938595","n1820938364","n1820938962","n1820938118","n1820939678","n1820938406","n1820938549","n1820937555","n1820938823","n1820937521","n1820939471","n1820939487","n1820938799","n1820938605","n1820937928","n1820938373","n1820939747","n1820939629","n1820937557","n1820937526","n1820938958","n1820938833","n1820937636","n1820938967","n1820938760","n1820938842","n1820938067","n1820939077","n1820939224","n1820938185","n1820939110","n1820938372","n1820939757","n1820939063","n1820939660","n1820938813","n1820937528","n1820938369","n1820938896","n1820939551","n1820939683","n1820937660","n1820937873","n1820938810","n1820938478","n1820939662","n1820937595","n1820939052","n1820938113","n1820939070","n1820938733","n1820937878","n1820938300","n1820939760","n1820939718","n1820937646","n1820939057","n1820939443","n1914861306","n1820938013","n1820937529","n1820939764","n1820938826","n1820937885","n1820939588","n1820937865","n1820937833","n1914861112","n1820938761","n1914861007","n1820937905","n1820938541","n1820939092","n1914861057","n1820938153","n1820938267","n1820939265","n1820938085","n1820939018","n1820939755","n1820938474","n1820939027","n1820938593","n1820938202","n1820939599","n1820939695","n1820938077","n1820938012","n1820939545","n1820939596","n1820939337","n1820938227","n1820937698","n1820938475","n1820939465","n1820938165","n1820938698","n1820938525","n1820938529","n1820938553","n1820938940","n1820939498","n1820938501","n1820939533","n1820938924","n1820939634","n1820939220","n1820939657","n1820938887","n1820938838","n1820938114","n1820937823","n1820938778","n1820938801","n1820939096","n1820938981","n1820937953","n1820938732","n1820938980","n1820938960","n1820937949","n1820938026","n1820939273","n1841425201","n1820938629","n1820938864","n1820938554","n1820938088","n1820937685","n1841425222","n1820939729","n1820937665","n1820937838","n1820937739","n1820938780","n1820937821","n1820938825","n1820939055","n1820939485","n1820938041","n1820938746","n1820939562","n1820938459","n1820939489","n1820938050","n1820937980","n1820937695","n1820938413","n1820938555","n1820937703","n1820938536","n1820938196","n1820938287","n1820938169","n1820939279","n1820938531","n1820938959","n1820939741","n1820938665","n1820938963","n1820939611","n1820937653","n1820939618","n1820939492","n1820938600","n1820938628","n1820939312","n1820939616","n1820937738","n1820939001","n1820939062","n1820938794","n1820938558","n1820937822","n1820937532","n1820939073","n1820938200","n1820938241","n1820938968","n1820938927","n1820938306","n1820937630","n1820938456","n1820937694","n1820938908","n1820939076","n1820937522","n1820939659","n1820938522","n1820939318","n1820938932","n1820938841","n1820937579","n1820937540","n1820938560","n1821139530","n1820938964","n1820937662","n1820939281","n1821139533","n1820937797","n1821139532","n1820939751","n1821139531","n1820939291","n1820938420","n1820939696","n1820938904","n1820938484","n1820939448","n1820939009","n1820938735","n1820938986","n1820938937","n1820939030","n1820938734","n1820938745","n1820939106","n1820938987","n1820937858","n1820938673","n1820938620","n1820937808","n1820937700","n1820939573","n1820938540","n1820937661","n1820937570","n1820938396","n1820937875","n1820939048","n1820938233","n1820938793","n1820939584","n1820938412","n1820938394","n1820937846","n1820938800","n1820938690","n1820939331","n1820939630","n1820938762","n1820938710","n1820939322","n1820938992","n1821137608","n1821137607","n1820937924","n1820939139","n1820939463","n1820939574","n1820938294","n1820938071","n1820938307","n1820938061","n1820939260","n1820937899","n1820938310","n1820938983","n1820937530","n1820938993","n1820938890","n1820937915","n1820938231","n1820938040","n1820938920","n1820939585","n1820938135","n1820939700","n1820937824","n1820939667","n1820937930","n1820938134","n1820937551","n1820939405","n1820938232","n1820937716","n1820937848","n1820939765","n1820939068","n1820939766","n1820937933","n1820937720","n1820939222","n1820939772","n1820939022","n1820939732","n1820937702","n1820937691","n1820938945","n1820937756","n1820938451","n1820938410","n1820938798","n1820937945","n1820937654","n1820938598","n1820938836","n1820937571","n1820937556","n1820938994","n1820938919","n1820938863","n1820939064","n1820938018","n1820937658","n1820937537","n1820938142","n1820938666","n1820937535","n1820939571","n1820938465","n1820939638","n1820937533","n1820939656","n1820939422","n1820938109","n1820938405","n1820938028","n1820937649","n1820938829","n1820939031","n1820939155","n1820938350","n1820938463","n1820938425","n1820939047","n1820938831","n1820938494","n1820937697","n1820938504","n1820938900","n1820937784","n1820938414","n1820938076","n1820938723","n1820937722","n1820938739","n1820937791","n1820938985","n1820938352","n1820938293","n1820938274","n1820939692","n1820937871","n1820939059","n1820938868","n1820937877","n1820937743","n1820938429","n1820937545","n1820937575","n1820938302","n1820938505","n1820938916","n1820938374","n1820938329","n1820937790","n1820939735","n1820938930","n1820937995","n1820938512","n1820938130","n1820938194","n1820938671","n1820938802","n1820937542","n1820937602","n1820939069","n1820938901","n1820939654","n1820937727","n1820939569","n1820938375","n1820939306","n1820938479","n1820938376","n1820938667","n1820937766","n1820939467","n1820939567","n1820937806","n1820938943","n1820938931","n1820937745","n1820939452","n1820938738","n1820938053","n1820939653","n1820938640","n1820937604","n1820937536","n1820938701","n1820939625","n1820939744","n1820939572","n1820937577","n1820937541","n1820938891","n1820937597","n1820938469","n1820939194","n1820937539","n1820938911","n1820939017","n1820939650","n1820939103","n1820939578","n1820938132","n1820937549","n1820938634","n1820939743","n1820937544","n1820937826","n1820937598","n1820937547","n1820938032","n1820939142"]},"w17963021":{"id":"w17963021","tags":{"highway":"residential"},"nodes":["n185948706","n185948708","n185948710"]},"w203974069":{"id":"w203974069","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870431","n2139870432","n2139870433","n2139870434","n2139870431"]},"w209816575":{"id":"w209816575","tags":{"area":"yes","building":"yes"},"nodes":["n2199856288","n2199856289","n2199856290","n2199856291","n2199856292","n2199856293","n2199856294","n2199856295","n2199856296","n2199856297","n2199856298","n2199856299","n2199856300","n2199856301","n2199856302","n2199856303","n2199856288"]},"w203841838":{"id":"w203841838","tags":{"area":"yes","natural":"water"},"nodes":["n2138493826","n2138493827","n2138493828","n2138493829","n2138493830","n2138493831","n2138493833","n2138493832","n2138493826"]},"w203972937":{"id":"w203972937","tags":{"highway":"path","name":"Riverwalk Trail","surface":"asphalt","width":"3"},"nodes":["n2139858882","n2139858883","n2139858884","n2139858885","n2139858886","n2139858887","n2139858882","n2139858888","n2139858889","n2139858890","n2139858891","n2139858892","n2139858893","n2139858894","n2139858895","n2139858896","n2139858897","n2139858898","n2139858899","n2139858900","n2139858901","n2139858902","n2139858903","n2139858986","n2139858904","n2139858995","n2139858905","n2139858906","n2139858907","n2139858908","n2139858909","n2139858910","n2139858911","n2139858912","n2139858913","n2139858914","n2139858915","n2139858916","n2139858917","n2139858918","n2139858919","n2139858920","n2139858921","n2139858922","n2139858923","n2139858924","n2139858925","n2139858926","n2139858927","n2139858982","n2139858928","n2139858929","n2139858930","n2139858931","n2139858932","n2139858981","n2139858933","n2139858934","n2139858935","n2139858936","n2139858937","n2139858938","n2139858939","n2139858940","n2139858941","n2139858942","n2139858943","n2140006437","n2139858964","n2139858944","n2139858966","n2139858945","n2139858946","n2139858947","n2139858948","n2139858949","n2139858950","n2139858951"]},"w17964015":{"id":"w17964015","tags":{"highway":"residential"},"nodes":["n185954680","n185954683","n185954685","n185954687","n185954689","n185954690","n185954691","n2139870379","n2139870456","n185954692","n185954693","n185954695"]},"w17967315":{"id":"w17967315","tags":{"highway":"residential","name":"South Andrews Street"},"nodes":["n185981999","n185974477","n185964963"]},"w203974071":{"id":"w203974071","tags":{"highway":"footway"},"nodes":["n2139870439","n2139870440","n2139870441","n2139870442","n2139870443","n2139870444","n2139870445","n2139870446","n2139870447","n2139870448","n2139870449"]},"w170848824":{"id":"w170848824","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819858503","n1819858531","n1819858526","n1819858518","n1819858505","n1819858508","n1819858512","n1819858514","n1819858528","n1819858509","n1819858511","n1819858507","n1819858521"]},"w203986458":{"id":"w203986458","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989357","n2139989359","n2139989360","n2139989362","n2139989357"]},"w170844917":{"id":"w170844917","tags":{"waterway":"riverbank"},"nodes":["n1819805911","n1819805690","n1819805812","n1819805766","n1819805802","n1819805885","n1819805626","n1819805842","n1819805715","n1819805694","n1819805618","n1819805629","n1819805731","n1819805636","n1819805878","n1819805718","n1819805798","n1819849057","n1819805666","n1819805852","n1819805805","n1819805789","n1819805868","n1819805680","n1819805918","n1819848888","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n1819858521","n1819805854","n1819805876","n1819805864","n1819805922","n2139859004","n1819805702","n2139859003","n1819805614","n1819805792","n1819805786","n1819805777","n1819805645","n1819805838","n1819805889","n1819805795","n1819805707","n1819805774","n1819805808","n1819805810","n1819805724","n1819805676","n1819805728","n1819805783","n1819805687","n1819805727","n2189123379","n1819805632","n1819805641","n1819805760","n1819805887","n1819805861","n1819805722","n1819805880","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n1819805698","n1819805647","n1819805870","n1819805683","n1819805622","n1819805639","n1819805858","n1819805643","n1819805673","n1819805925","n1819805849","n1819805711","n1819805846","n1819805669","n1819805883","n1819805814","n1819805873","n1819805911"]},"w17967326":{"id":"w17967326","tags":{"highway":"residential","name":"North Constantine Street"},"nodes":["n185985217","n185985219","n185985221","n185985222","n185985223","n185985225","n2140006431","n185985227","n185985229","n185985231","n185985233","n185985235","n185985238","n185985240","n2140018998","n185964965"]},"w134150789":{"id":"w134150789","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964971","n2139870406","n185964972"]},"w17966400":{"id":"w17966400","tags":{"highway":"tertiary","name":"South Constantine Street"},"nodes":["n185958672","n185964965"]},"w203974066":{"id":"w203974066","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870417","n2139870418","n2139870420","n2139870419"]},"w17965998":{"id":"w17965998","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972775","n185972777","n185972779","n185972781","n185972783","n185972785","n185972787","n185972788","n185972789","n185972790","n185972791","n185972793","n185972795","n185972797","n185972798","n185972800","n185972802","n185972805","n185972807","n185972809","n185972811","n185972813","n185972814","n185972815","n185972816","n185972817","n185972819","n185972821","n185972824","n185972826","n185972830","n185972832","n185972834","n185972835","n185972836","n185972839","n185990434","n2114807572","n2114807568","n185972845","n2114807583","n185972847","n185972849","n185972851","n2114807578","n1475293254","n2114807593","n1475293226","n185972862","n2114807565","n185951869","n1475293234","n1475293252","n185972868","n1475293264","n1475293222","n185972878","n1475293261","n185972882","n185972885","n1475293260","n1475293240","n185972891","n185972895","n185972897","n185972899","n2130304159","n1475284023","n185972903"]},"w134150795":{"id":"w134150795","tags":{"bridge":"yes","highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964970","n185964971"]},"w203974067":{"id":"w203974067","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870420","n2139870421"]},"w170995908":{"id":"w170995908","tags":{"highway":"residential","name":"Thomas Street"},"nodes":["n1821006702","n1821006700","n1821006698","n2139858990","n1821006716","n1821006725","n1821006712","n1821006704","n1821006708","n1821006710","n1821006706"]},"w17965834":{"id":"w17965834","tags":{"highway":"residential","name":"Spring Street"},"nodes":["n185971361","n185971364","n185971366","n185971368","n185954695","n185964968"]},"w203974070":{"id":"w203974070","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870435","n2139870436","n2139870437","n2139870438","n2139870435"]},"w203989879":{"id":"w203989879","tags":{"highway":"service"},"nodes":["n2140018998","n2140018999","n2140019000"]},"w203974062":{"id":"w203974062","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870387","n2139870388","n2139870389","n2139870390","n2139870391","n2139870392","n2139870397","n2139870393","n2139870396","n2139870395","n2139870394","n2139870387"]},"w203974061":{"id":"w203974061","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2139870382","n2139870383"]},"w203049587":{"id":"w203049587","tags":{"area":"yes","name":"Scidmore Park Petting Zoo","tourism":"zoo","zoo":"petting_zoo"},"nodes":["n2130304133","n2130304136","n2130304138","n2130304140","n2130304142","n2130304144","n2130304146","n2130304147","n2130304148","n2130304149","n2130304150","n2130304151","n2130304133"]},"w203972941":{"id":"w203972941","tags":{"highway":"path"},"nodes":["n2139858982","n2139858983","n2139858984","n2139858985","n2139858927"]},"w203974065":{"id":"w203974065","tags":{"highway":"service"},"nodes":["n2139870406","n2139870407","n2139870408","n2139870417","n2139870409","n2139870410","n2139870411","n2139870412","n2139870426","n2139870413","n2139870414","n2139870415","n2139870419","n2139870416","n2139870421","n2139870408"]},"w203972940":{"id":"w203972940","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858934","n2139858967","n2139858968","n2139858969","n2139858970","n2139858971","n2139858972","n2139858973","n2139858974","n2139858975","n2139858976","n2139858977","n2139858978","n2139858979","n2139858980","n2139858981"]},"w203974072":{"id":"w203974072","tags":{"highway":"footway"},"nodes":["n2139858925","n2139870450","n2139870453","n2139870451","n2139870452","n2139870441"]},"w203974074":{"id":"w203974074","tags":{"highway":"footway"},"nodes":["n2139870454","n2139870456","n2139870429"]},"w203974060":{"id":"w203974060","tags":{"highway":"footway"},"nodes":["n2139870383","n2139870384","n2139870422","n2139870385","n2139870386","n2139870388"]},"w203841837":{"id":"w203841837","tags":{"area":"yes","natural":"water"},"nodes":["n2138493807","n2138493808","n2138493809","n2138493810","n2138493811","n2138493812","n2138493813","n2138493814","n2138493815","n2138493816","n2138493825","n2138493817","n2138493824","n2138493818","n2138493819","n2138493820","n2138493821","n2138493822","n2138493823","n2138493807"]},"w134150845":{"id":"w134150845","tags":{"bridge":"yes","name":"Conrail Railroad","railway":"rail"},"nodes":["n185972903","n185972905"]},"w203974059":{"id":"w203974059","tags":{"highway":"footway"},"nodes":["n2139870430","n2139870439","n2139870429","n2139870428","n2139870379","n2139870455","n2139870380","n2139870381","n2139858925","n2139870382"]},"w203986457":{"id":"w203986457","tags":{"area":"yes","leisure":"park","name":"Scidmore Park"},"nodes":["n2139989333","n2139989335","n2139989337","n2139989339","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n2139989341","n2139989344","n2139989346","n2139989348","n2139989350","n2139989351","n2139989353","n2139989355","n2139989333"]},"w170848331":{"id":"w170848331","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819848937","n1819849104","n1819849076","n1819849183","n1819848928","n1819848972","n1819848948","n1819848971","n1819848859","n1819849008","n1819848889","n1819849026","n1819849094","n1819849083","n1819849079","n1819849187","n1819848992","n1819849060","n1819849056","n1819849071","n1819849067","n1819849048","n1819849036","n1819849150","n1819849075","n1819849051","n1819849062","n1819848926","n1819849035","n1819848987","n1819849012","n1819848933","n1819848996","n1819848990","n1819849005","n1819849021","n1819848892","n1819849092","n1819848863","n1819848922","n1819848858","n1819848855","n1819848974","n1819848953","n1819849019","n1819849049","n1819848979","n1819849140","n1819849193","n1819849147","n1819849151","n1819849163","n1819849023","n1819848878","n1819849004","n1819848857","n1819848879","n1819849041","n1819849165","n1819849107","n1819849156","n1819848934","n1819848914","n1819848955","n1819848931","n1819848927","n1819849084","n1819849169","n1819849045","n1819848945","n1819849095","n1819848924","n1819849171","n1819849141","n1819849046","n1819849197","n1819849011","n1819849108","n1819849158","n1819849160","n1819848870","n1819849006","n1819849157","n1819848993","n1819848970","n1819849202","n1819848903","n1819848975","n1819848849","n1819849025","n1819849105","n1819849033","n1819849176","n1819849099","n1819849086","n1819848960","n1819848961","n1819849001","n1819848980","n1819849038","n1819848854","n1819849127","n1819849170","n1819849139","n1819848873","n1819848929","n1819849201","n1819849121","n1819849031","n1819849131","n1819848875","n1819849080","n1819849066","n1819849081","n1819849096","n1819849172","n1819849114","n1819849182","n1819848905","n1819849054","n1819848920","n1819848851","n1819848968","n1819848917","n1819849111","n1819849119","n1819849074","n1819848893","n1819849129","n1819848850","n1819848956","n1819849154","n1819848877","n1819848986","n1819849191","n1819848952","n1819848954","n1819848942","n1819849028","n1819849195","n1819848938","n1819848962","n1819849070","n1819849034","n1819849052","n1819849059","n1819848916","n1819849162","n1819849167","n1819849093","n1819849030","n1819849002","n1819849161","n1819848886","n1819848958","n1819849064","n1819849112","n1819849148","n1819848856","n1819848976","n1819848977","n1819849144","n1819848918","n1819849200","n1819848919","n1819849042","n1819849166","n1819849186","n1819849152","n1819849058","n1819849185","n1819849199","n1819849053","n1819849194","n1819849068","n1819849146","n1819849174","n1819848967","n1819848932","n1819849155","n1819849198","n1819848964","n1819848894","n1819848969","n1819849184","n1819849055","n1819849179","n1819848865","n1819848860","n1819849082","n1819848966","n1819849040","n1819849069","n1819849078","n1819849077","n1819848904","n1819848959","n1819849133","n1819849089","n1819849000","n1819849124","n1819849032","n1819849097","n1819848939","n1819849072","n1819848915","n1819849196","n1819848946","n1819849047","n1819849029","n1819849164","n1819848994","n1819849022","n1819858513","n1819849126","n1819849063","n1819848941","n1819849085","n1819848871","n1819848943","n1819849192","n1819858501","n1819849159","n1819858523","n1819848901","n1819849189","n1819858503","n1819849065","n2139877106","n1819848909","n1819848930","n1819848888"]},"w17967397":{"id":"w17967397","tags":{"highway":"residential","name":"North Andrews Street"},"nodes":["n185964963","n185985217"]},"w17964497":{"id":"w17964497","tags":{"highway":"tertiary","name":"Constantine St"},"nodes":["n185958643","n185958645","n2139795852","n185958647","n185958649","n185958651","n185958653","n185958656","n185958658","n185958660","n185958662","n185958664","n185958666","n185958668","n185958670","n185948710","n185958672"]},"w203974068":{"id":"w203974068","tags":{"highway":"footway"},"nodes":["n2139870422","n2139870423","n2139870424","n2139870425","n2139870426","n2139870427"]},"w203974063":{"id":"w203974063","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870398","n2139870399","n2139870400","n2139870401","n2139870398"]},"w203986459":{"id":"w203986459","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989364","n2139989366","n2139989368","n2139989370","n2139989364"]},"w203988286":{"id":"w203988286","tags":{"area":"yes","leisure":"park","name":"Memory Isle Park"},"nodes":["n2140006331","n2140006334","n2140006336","n2140006338","n2140006340","n2140006342","n2140006344","n2140006346","n2140006348","n2140006351","n2140006353","n2140006355","n2140006357","n2140006359","n2140006361","n2140006363","n2140006364","n2140006365","n2140006395","n2140006366","n2140006401","n2140006397","n2140006399","n2140006331"]},"w203974073":{"id":"w203974073","tags":{"highway":"footway"},"nodes":["n2139870453","n2139870454","n2139870455"]},"w203974064":{"id":"w203974064","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870402","n2139870403","n2139870404","n2139870405","n2139870402"]},"n185966959":{"id":"n185966959","loc":[-85.642185,41.946411]},"n1475283980":{"id":"n1475283980","loc":[-85.6398249,41.9451425]},"n1475284013":{"id":"n1475284013","loc":[-85.6396448,41.9451666]},"n1475284042":{"id":"n1475284042","loc":[-85.6386382,41.9454789]},"n185975925":{"id":"n185975925","loc":[-85.6393332,41.9452388]},"n185975919":{"id":"n185975919","loc":[-85.6391279,41.9453044]},"n185975917":{"id":"n185975917","loc":[-85.6389034,41.9453872]},"n2140006369":{"id":"n2140006369","loc":[-85.6386163,41.9451631]},"n2140006370":{"id":"n2140006370","loc":[-85.6385144,41.9449357]},"n2140006417":{"id":"n2140006417","loc":[-85.6385785,41.9450299]},"n2140006419":{"id":"n2140006419","loc":[-85.6385781,41.9452152]},"n2189123361":{"id":"n2189123361","loc":[-85.6404948,41.947015]},"n2189123363":{"id":"n2189123363","loc":[-85.6395765,41.946495]},"n2189123365":{"id":"n2189123365","loc":[-85.6389347,41.9460875]},"n185966962":{"id":"n185966962","loc":[-85.644417,41.946364]},"n185975911":{"id":"n185975911","loc":[-85.637532,41.9458276]},"n185975913":{"id":"n185975913","loc":[-85.6376323,41.9457936]},"n185975915":{"id":"n185975915","loc":[-85.6383596,41.9455425]},"n185975932":{"id":"n185975932","loc":[-85.644403,41.945088]},"n185975934":{"id":"n185975934","loc":[-85.645486,41.945084]},"n185979974":{"id":"n185979974","loc":[-85.644381,41.943831]},"n2139795809":{"id":"n2139795809","loc":[-85.6464756,41.9450813]},"n2139795810":{"id":"n2139795810","loc":[-85.6466646,41.945174]},"n2139858952":{"id":"n2139858952","loc":[-85.6383567,41.9454039]},"n2139858953":{"id":"n2139858953","loc":[-85.6380506,41.9455301]},"n2139858954":{"id":"n2139858954","loc":[-85.6377321,41.9455546]},"n2139858955":{"id":"n2139858955","loc":[-85.6376571,41.9455245]},"n2139858956":{"id":"n2139858956","loc":[-85.6375859,41.9454544]},"n2139858957":{"id":"n2139858957","loc":[-85.6376686,41.9453185]},"n2139858958":{"id":"n2139858958","loc":[-85.6378936,41.9451712]},"n2139858959":{"id":"n2139858959","loc":[-85.6379225,41.9450825]},"n2139858960":{"id":"n2139858960","loc":[-85.6379302,41.9447564]},"n2139858961":{"id":"n2139858961","loc":[-85.6379763,41.9446963]},"n2139858962":{"id":"n2139858962","loc":[-85.6380436,41.9446706]},"n2139858963":{"id":"n2139858963","loc":[-85.6381286,41.9445969]},"n2139858965":{"id":"n2139858965","loc":[-85.6382523,41.9444134]},"n2140006367":{"id":"n2140006367","loc":[-85.6380923,41.9454418]},"n2140006368":{"id":"n2140006368","loc":[-85.6384089,41.9453146]},"n2140006372":{"id":"n2140006372","loc":[-85.6383252,41.9447706]},"n2140006374":{"id":"n2140006374","loc":[-85.6381033,41.9447436]},"n2140006376":{"id":"n2140006376","loc":[-85.6379759,41.9447815]},"n2140006378":{"id":"n2140006378","loc":[-85.6379832,41.9448654]},"n2140006380":{"id":"n2140006380","loc":[-85.6380632,41.9450738]},"n2140006382":{"id":"n2140006382","loc":[-85.6380414,41.9452064]},"n2140006389":{"id":"n2140006389","loc":[-85.6379068,41.9453092]},"n2140006391":{"id":"n2140006391","loc":[-85.637925,41.9453904]},"n2140006393":{"id":"n2140006393","loc":[-85.6379977,41.94545]},"n2189123275":{"id":"n2189123275","loc":[-85.6371346,41.9462544]},"n2189123278":{"id":"n2189123278","loc":[-85.6368371,41.9466153]},"n2189123280":{"id":"n2189123280","loc":[-85.6379537,41.9489088]},"n2189123282":{"id":"n2189123282","loc":[-85.6383816,41.9497858]},"n2189123285":{"id":"n2189123285","loc":[-85.6393673,41.9512417]},"n2189123287":{"id":"n2189123287","loc":[-85.640554,41.9517766]},"n2189123289":{"id":"n2189123289","loc":[-85.6411,41.9522344]},"n2189123291":{"id":"n2189123291","loc":[-85.6417418,41.9526574]},"n2189123293":{"id":"n2189123293","loc":[-85.642321,41.9529407]},"n2189123295":{"id":"n2189123295","loc":[-85.6427697,41.9532278]},"n2189123297":{"id":"n2189123297","loc":[-85.6433332,41.9538254]},"n2189123300":{"id":"n2189123300","loc":[-85.6435785,41.9543648]},"n2189123301":{"id":"n2189123301","loc":[-85.6444394,41.9541048]},"n2189123303":{"id":"n2189123303","loc":[-85.6450603,41.954]},"n2189123312":{"id":"n2189123312","loc":[-85.6454829,41.9539108]},"n2189123314":{"id":"n2189123314","loc":[-85.6460464,41.9538526]},"n2189123315":{"id":"n2189123315","loc":[-85.6463178,41.9537167]},"n2189123316":{"id":"n2189123316","loc":[-85.646276,41.9534141]},"n2189123317":{"id":"n2189123317","loc":[-85.6459995,41.9531541]},"n2189123318":{"id":"n2189123318","loc":[-85.645222,41.9531929]},"n2189123319":{"id":"n2189123319","loc":[-85.6447316,41.9531813]},"n2189123320":{"id":"n2189123320","loc":[-85.6440637,41.9532977]},"n2189123321":{"id":"n2189123321","loc":[-85.6438185,41.9531774]},"n2189123322":{"id":"n2189123322","loc":[-85.6440011,41.9528398]},"n2189123323":{"id":"n2189123323","loc":[-85.6442672,41.9525914]},"n2189123324":{"id":"n2189123324","loc":[-85.6442881,41.9523276]},"n2189123326":{"id":"n2189123326","loc":[-85.644262,41.952153]},"n2189123328":{"id":"n2189123328","loc":[-85.6441681,41.9520404]},"n2189123330":{"id":"n2189123330","loc":[-85.6442098,41.9517494]},"n2189123333":{"id":"n2189123333","loc":[-85.6438498,41.9515864]},"n2189123336":{"id":"n2189123336","loc":[-85.6435889,41.9513225]},"n2189123339":{"id":"n2189123339","loc":[-85.6425349,41.9510315]},"n2189123342":{"id":"n2189123342","loc":[-85.6422688,41.9508802]},"n2189123345":{"id":"n2189123345","loc":[-85.6418775,41.9508142]},"n2189123348":{"id":"n2189123348","loc":[-85.6415488,41.9508064]},"n2189123351":{"id":"n2189123351","loc":[-85.6411027,41.9505488]},"n2189123353":{"id":"n2189123353","loc":[-85.6410374,41.9498208]},"n2189123355":{"id":"n2189123355","loc":[-85.6410061,41.9494327]},"n2189123357":{"id":"n2189123357","loc":[-85.6411522,41.9482569]},"n2189123359":{"id":"n2189123359","loc":[-85.6410548,41.9473036]},"n2189123368":{"id":"n2189123368","loc":[-85.6380216,41.9458974]},"n2189123370":{"id":"n2189123370","loc":[-85.6386721,41.9507782]},"w17968193":{"id":"w17968193","tags":{"highway":"residential","name":"French St"},"nodes":["n185970906","n185982877","n185967774","n185985823","n185979974"]},"w203972939":{"id":"w203972939","tags":{"highway":"path"},"nodes":["n2139858965","n2139858966"]},"w203988289":{"id":"w203988289","tags":{"area":"yes","natural":"water"},"nodes":["n2140006367","n2140006368","n2140006419","n2140006369","n2140006417","n2140006370","n2140006372","n2140006374","n2140006376","n2140006378","n2140006380","n2140006382","n2140006389","n2140006391","n2140006393","n2140006367"]},"w208640157":{"id":"w208640157","tags":{"area":"yes","natural":"wetland"},"nodes":["n1819849029","n2189123275","n2189123278","n2189123280","n2189123282","n2189123370","n2189123285","n2189123287","n2189123289","n2189123291","n2189123293","n2189123295","n2189123297","n2189123300","n2189123301","n2189123303","n2189123312","n2189123314","n2189123315","n2189123316","n2189123317","n2189123318","n2189123319","n2189123320","n2189123321","n2189123322","n2189123323","n2189123324","n2189123326","n2189123328","n2189123330","n2189123333","n2189123336","n2189123339","n2189123342","n2189123345","n2189123348","n2189123351","n2189123353","n2189123355","n2189123357","n2189123359","n2189123361","n2189123363","n2189123365","n2189123368","n1819849029"]},"w17966281":{"id":"w17966281","tags":{"highway":"residential","name":"Pealer St"},"nodes":["n185975911","n185975913","n185975915","n1475284042","n185975917","n185975919","n185975925","n185970909","n1475284013","n1475283980","n185975928","n185967775","n185975930","n185975932","n185975934","n2139795809","n2139795810"]},"w17965353":{"id":"w17965353","tags":{"highway":"residential","name":"Yauney St"},"nodes":["n185966958","n185966959","n185966960","n185966962"]},"w203972938":{"id":"w203972938","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858964","n2139858965","n2139858963","n2139858962","n2139858961","n2139858960","n2139858959","n2139858958","n2139858957","n2139858956","n2139858955","n2139858954","n2139858953","n2139858952","n2139858951"]},"n354002665":{"id":"n354002665","loc":[-85.6366599,41.9444923],"tags":{"name":"Memory Isle","place":"island"}},"n354031301":{"id":"n354031301","loc":[-85.635,41.9463889],"tags":{"amenity":"post_office","name":"Three Rivers Post Office"}},"n185963454":{"id":"n185963454","loc":[-85.633686,41.946072]},"n185963455":{"id":"n185963455","loc":[-85.633815,41.946131]},"n185963456":{"id":"n185963456","loc":[-85.633951,41.946174]},"n185978375":{"id":"n185978375","loc":[-85.634385,41.94559]},"n185978377":{"id":"n185978377","loc":[-85.634544,41.945725]},"n185978379":{"id":"n185978379","loc":[-85.634573,41.945764]},"n185978381":{"id":"n185978381","loc":[-85.634616,41.945849]},"n185978383":{"id":"n185978383","loc":[-85.634629,41.945893]},"n185984011":{"id":"n185984011","loc":[-85.636058,41.946201]},"n185984013":{"id":"n185984013","loc":[-85.636112,41.946366]},"n185984015":{"id":"n185984015","loc":[-85.636143,41.946551]},"n185988237":{"id":"n185988237","loc":[-85.6354162,41.946044]},"n185988969":{"id":"n185988969","loc":[-85.635374,41.945325]},"n185988971":{"id":"n185988971","loc":[-85.635643,41.945585]},"n185988972":{"id":"n185988972","loc":[-85.635853,41.94586]},"n1475283992":{"id":"n1475283992","loc":[-85.6372968,41.9459007]},"n1475284011":{"id":"n1475284011","loc":[-85.6359415,41.9459797]},"n1475284019":{"id":"n1475284019","loc":[-85.6364433,41.9460423]},"n185984009":{"id":"n185984009","loc":[-85.6360524,41.9460485]},"n185988239":{"id":"n185988239","loc":[-85.6358187,41.9460423]},"n185988243":{"id":"n185988243","loc":[-85.6366156,41.9460282]},"n185988244":{"id":"n185988244","loc":[-85.6368316,41.9460046]},"n185988245":{"id":"n185988245","loc":[-85.6370133,41.9459704]},"n185988241":{"id":"n185988241","loc":[-85.636291,41.9460461]},"n185964976":{"id":"n185964976","loc":[-85.633923,41.9434157]},"n185964980":{"id":"n185964980","loc":[-85.6333656,41.9437293]},"n185978388":{"id":"n185978388","loc":[-85.6346449,41.9460571]},"n1819858504":{"id":"n1819858504","loc":[-85.6365343,41.9447926]},"n1819858506":{"id":"n1819858506","loc":[-85.6370546,41.9451882]},"n1819858516":{"id":"n1819858516","loc":[-85.6358369,41.9444654]},"n1819858519":{"id":"n1819858519","loc":[-85.6361534,41.9446176]},"n1819858525":{"id":"n1819858525","loc":[-85.6368025,41.9449442]},"n1819858527":{"id":"n1819858527","loc":[-85.6334199,41.9457495]},"n185963452":{"id":"n185963452","loc":[-85.633564,41.9458519]},"n185963453":{"id":"n185963453","loc":[-85.6336152,41.9459804]},"n185963451":{"id":"n185963451","loc":[-85.6332888,41.9456871]},"n2130304152":{"id":"n2130304152","loc":[-85.6359466,41.9454599]},"n2130304153":{"id":"n2130304153","loc":[-85.6362773,41.9452683]},"n2130304154":{"id":"n2130304154","loc":[-85.6352028,41.9442868]},"n2130304155":{"id":"n2130304155","loc":[-85.6348756,41.9444769]},"n2130304156":{"id":"n2130304156","loc":[-85.6349723,41.9444207]},"n2130304157":{"id":"n2130304157","loc":[-85.6338698,41.9434443]},"n2130304158":{"id":"n2130304158","loc":[-85.635094,41.9451026]},"n2130304160":{"id":"n2130304160","loc":[-85.6353716,41.9449322]},"n2130304162":{"id":"n2130304162","loc":[-85.6365942,41.9459352]},"n2130304163":{"id":"n2130304163","loc":[-85.6369006,41.9457469]},"n2130304164":{"id":"n2130304164","loc":[-85.6363292,41.9452278]},"n2130304165":{"id":"n2130304165","loc":[-85.6360248,41.9454175]},"n2139824683":{"id":"n2139824683","loc":[-85.6339825,41.9446441]},"n2139824689":{"id":"n2139824689","loc":[-85.6340437,41.9446925]},"n2139824702":{"id":"n2139824702","loc":[-85.6340961,41.9447551]},"n2139824705":{"id":"n2139824705","loc":[-85.6337467,41.944809]},"n2139824707":{"id":"n2139824707","loc":[-85.6341598,41.9448129]},"n2139824710":{"id":"n2139824710","loc":[-85.6342771,41.9448223]},"n2139824712":{"id":"n2139824712","loc":[-85.6346058,41.944841]},"n2139824713":{"id":"n2139824713","loc":[-85.633808,41.9448574]},"n2139824714":{"id":"n2139824714","loc":[-85.6340889,41.9448589]},"n2139824716":{"id":"n2139824716","loc":[-85.6343335,41.944871]},"n2139824717":{"id":"n2139824717","loc":[-85.6343341,41.9448717]},"n2139824720":{"id":"n2139824720","loc":[-85.6338757,41.9449069]},"n2139824721":{"id":"n2139824721","loc":[-85.6341445,41.9449071]},"n2139824724":{"id":"n2139824724","loc":[-85.6334787,41.9449262]},"n2139824726":{"id":"n2139824726","loc":[-85.6347119,41.9449332]},"n2139824727":{"id":"n2139824727","loc":[-85.6347175,41.9449418]},"n2139824728":{"id":"n2139824728","loc":[-85.6344284,41.9449538]},"n2139824729":{"id":"n2139824729","loc":[-85.6339339,41.9449573]},"n2139824730":{"id":"n2139824730","loc":[-85.6339179,41.9449682]},"n2139824732":{"id":"n2139824732","loc":[-85.6335472,41.9449895]},"n2139824733":{"id":"n2139824733","loc":[-85.6339736,41.9450164]},"n2139824735":{"id":"n2139824735","loc":[-85.6336034,41.9450415]},"n2139824736":{"id":"n2139824736","loc":[-85.6348317,41.945043]},"n2139824737":{"id":"n2139824737","loc":[-85.63403,41.9450651]},"n2139824738":{"id":"n2139824738","loc":[-85.6336611,41.9450949]},"n2139824740":{"id":"n2139824740","loc":[-85.6336582,41.9450966]},"n2139824744":{"id":"n2139824744","loc":[-85.6331702,41.9451107]},"n2139824745":{"id":"n2139824745","loc":[-85.6333388,41.9451142]},"n2139824746":{"id":"n2139824746","loc":[-85.6337131,41.9451341]},"n2139824747":{"id":"n2139824747","loc":[-85.6337021,41.9451372]},"n2139824748":{"id":"n2139824748","loc":[-85.6341244,41.9451472]},"n2139824749":{"id":"n2139824749","loc":[-85.6333952,41.945166]},"n2139824750":{"id":"n2139824750","loc":[-85.633395,41.9451661]},"n2139824751":{"id":"n2139824751","loc":[-85.6346258,41.9451725]},"n2139824752":{"id":"n2139824752","loc":[-85.6332387,41.9451741]},"n2139824753":{"id":"n2139824753","loc":[-85.6346901,41.9451853]},"n2139824754":{"id":"n2139824754","loc":[-85.6346611,41.9452035]},"n2139824755":{"id":"n2139824755","loc":[-85.6346574,41.9452059]},"n2139824756":{"id":"n2139824756","loc":[-85.6345611,41.9452133]},"n2139824757":{"id":"n2139824757","loc":[-85.633453,41.9452194]},"n2139824758":{"id":"n2139824758","loc":[-85.6335508,41.9452283]},"n2139824759":{"id":"n2139824759","loc":[-85.6347424,41.9452312]},"n2139824760":{"id":"n2139824760","loc":[-85.6342305,41.9452395]},"n2139824761":{"id":"n2139824761","loc":[-85.6342319,41.9452449]},"n2139824762":{"id":"n2139824762","loc":[-85.6334969,41.94526]},"n2139824763":{"id":"n2139824763","loc":[-85.63468,41.9452706]},"n2139824764":{"id":"n2139824764","loc":[-85.6346772,41.9452724]},"n2139824765":{"id":"n2139824765","loc":[-85.6338611,41.9452763]},"n2139824766":{"id":"n2139824766","loc":[-85.6347811,41.9452939]},"n2139824767":{"id":"n2139824767","loc":[-85.6347375,41.9453211]},"n2139824768":{"id":"n2139824768","loc":[-85.6339171,41.9453301]},"n2139824769":{"id":"n2139824769","loc":[-85.6348307,41.9453377]},"n2139824770":{"id":"n2139824770","loc":[-85.6347067,41.9453405]},"n2139824771":{"id":"n2139824771","loc":[-85.6343461,41.9453461]},"n2139824772":{"id":"n2139824772","loc":[-85.6343481,41.9453475]},"n2139824773":{"id":"n2139824773","loc":[-85.634805,41.9453538]},"n2139824774":{"id":"n2139824774","loc":[-85.6336997,41.9453692]},"n2139824775":{"id":"n2139824775","loc":[-85.6339709,41.9453818]},"n2139824776":{"id":"n2139824776","loc":[-85.6336229,41.9454134]},"n2139824777":{"id":"n2139824777","loc":[-85.6349022,41.9454141]},"n2139824778":{"id":"n2139824778","loc":[-85.6348854,41.9454246]},"n2139824779":{"id":"n2139824779","loc":[-85.6340286,41.9454373]},"n2139824780":{"id":"n2139824780","loc":[-85.6336963,41.9454572]},"n2139824781":{"id":"n2139824781","loc":[-85.6336789,41.9454672]},"n2139824782":{"id":"n2139824782","loc":[-85.6344933,41.945475]},"n2139824783":{"id":"n2139824783","loc":[-85.6340854,41.9454918]},"n2139824784":{"id":"n2139824784","loc":[-85.6350036,41.9455034]},"n2139824785":{"id":"n2139824785","loc":[-85.6337501,41.9455089]},"n2139824786":{"id":"n2139824786","loc":[-85.6337497,41.9455091]},"n2139824787":{"id":"n2139824787","loc":[-85.6345425,41.9455186]},"n2139824788":{"id":"n2139824788","loc":[-85.6341459,41.9455372]},"n2139824789":{"id":"n2139824789","loc":[-85.6341376,41.945542]},"n2139824790":{"id":"n2139824790","loc":[-85.6338394,41.9455462]},"n2139824791":{"id":"n2139824791","loc":[-85.6349171,41.9455588]},"n2139824792":{"id":"n2139824792","loc":[-85.6338074,41.9455646]},"n2139824793":{"id":"n2139824793","loc":[-85.6346229,41.9455894]},"n2139824794":{"id":"n2139824794","loc":[-85.6338983,41.9455995]},"n2139824795":{"id":"n2139824795","loc":[-85.6338962,41.9456007]},"n2139824796":{"id":"n2139824796","loc":[-85.6342475,41.9456348]},"n2139824797":{"id":"n2139824797","loc":[-85.6339505,41.9456497]},"n2139824798":{"id":"n2139824798","loc":[-85.6347243,41.9456788]},"n2139824799":{"id":"n2139824799","loc":[-85.635057,41.9456831]},"n2139824800":{"id":"n2139824800","loc":[-85.635287,41.9457056]},"n2139824801":{"id":"n2139824801","loc":[-85.6350753,41.9457068]},"n2139824802":{"id":"n2139824802","loc":[-85.6347753,41.9457252]},"n2139824803":{"id":"n2139824803","loc":[-85.6340521,41.9457473]},"n2139824804":{"id":"n2139824804","loc":[-85.6352875,41.9457611]},"n2139824805":{"id":"n2139824805","loc":[-85.6352941,41.9457611]},"n2139824806":{"id":"n2139824806","loc":[-85.6350758,41.9457623]},"n2139824807":{"id":"n2139824807","loc":[-85.6348194,41.9457638]},"n2139824808":{"id":"n2139824808","loc":[-85.635296,41.9459428]},"n2139824809":{"id":"n2139824809","loc":[-85.6348212,41.9459455]},"n2139832635":{"id":"n2139832635","loc":[-85.6354612,41.9448791]},"n2139832636":{"id":"n2139832636","loc":[-85.6360241,41.9453844]},"n2139832637":{"id":"n2139832637","loc":[-85.6361452,41.9453121]},"n2139832639":{"id":"n2139832639","loc":[-85.6355997,41.944797]},"n2139832641":{"id":"n2139832641","loc":[-85.6351346,41.9443541]},"n2139832647":{"id":"n2139832647","loc":[-85.6329883,41.9453692]},"n2139832653":{"id":"n2139832653","loc":[-85.6333643,41.9456293]},"n2139832663":{"id":"n2139832663","loc":[-85.6335394,41.9455339]},"n2139832665":{"id":"n2139832665","loc":[-85.6332375,41.9452476]},"n2139832667":{"id":"n2139832667","loc":[-85.6331664,41.9452161]},"n2139832669":{"id":"n2139832669","loc":[-85.6331144,41.9451875]},"n2139832671":{"id":"n2139832671","loc":[-85.6330779,41.9451274]},"n2139832673":{"id":"n2139832673","loc":[-85.6330664,41.9450802]},"n2139832678":{"id":"n2139832678","loc":[-85.6332218,41.9453585]},"n2139832686":{"id":"n2139832686","loc":[-85.6334246,41.945541]},"n2139832691":{"id":"n2139832691","loc":[-85.6329898,41.9454997]},"n2139832693":{"id":"n2139832693","loc":[-85.6343554,41.9443274]},"n2139832694":{"id":"n2139832694","loc":[-85.6336339,41.9437089]},"n2139832696":{"id":"n2139832696","loc":[-85.633532,41.9437708]},"n2139832697":{"id":"n2139832697","loc":[-85.6338316,41.9440868]},"n2139832698":{"id":"n2139832698","loc":[-85.6342258,41.9444141]},"n2139832699":{"id":"n2139832699","loc":[-85.6339164,41.9442166]},"n2139832700":{"id":"n2139832700","loc":[-85.6341389,41.944384]},"n2139832701":{"id":"n2139832701","loc":[-85.634235,41.9443259]},"n2139832702":{"id":"n2139832702","loc":[-85.633613,41.9437875]},"n2139832703":{"id":"n2139832703","loc":[-85.633915,41.9436132]},"n2139832704":{"id":"n2139832704","loc":[-85.6340019,41.9435613]},"n2139832706":{"id":"n2139832706","loc":[-85.6343197,41.9438427]},"n2139832708":{"id":"n2139832708","loc":[-85.6342361,41.9438936]},"n2139832709":{"id":"n2139832709","loc":[-85.6353839,41.9460401]},"n2139832710":{"id":"n2139832710","loc":[-85.6354032,41.9456763]},"n2139832711":{"id":"n2139832711","loc":[-85.6356839,41.9459252]},"n2139832712":{"id":"n2139832712","loc":[-85.6356109,41.945735]},"n2139832713":{"id":"n2139832713","loc":[-85.6353997,41.9457421]},"n2139832714":{"id":"n2139832714","loc":[-85.6353895,41.9459347]},"n2139832715":{"id":"n2139832715","loc":[-85.6334777,41.9436628]},"n2139832716":{"id":"n2139832716","loc":[-85.6333137,41.9435382]},"n2139832717":{"id":"n2139832717","loc":[-85.6330938,41.9435406]},"n2139832721":{"id":"n2139832721","loc":[-85.6333023,41.9434922]},"n2139832722":{"id":"n2139832722","loc":[-85.6330466,41.943623]},"n2139832723":{"id":"n2139832723","loc":[-85.6332746,41.9435624]},"n2139832724":{"id":"n2139832724","loc":[-85.6333511,41.9435176]},"n2139832725":{"id":"n2139832725","loc":[-85.6332241,41.9434001]},"n2139832726":{"id":"n2139832726","loc":[-85.6332355,41.9433686]},"n2139870373":{"id":"n2139870373","loc":[-85.6351783,41.9439117]},"n2139870374":{"id":"n2139870374","loc":[-85.6351431,41.9439217]},"n2139870375":{"id":"n2139870375","loc":[-85.6348853,41.9439117]},"n2139870376":{"id":"n2139870376","loc":[-85.6348317,41.9439105]},"n2139870377":{"id":"n2139870377","loc":[-85.6346384,41.944007]},"n2139870378":{"id":"n2139870378","loc":[-85.6345563,41.9440523]},"n2140006403":{"id":"n2140006403","loc":[-85.6359942,41.9450097]},"n2140006405":{"id":"n2140006405","loc":[-85.6363884,41.9446079]},"n2140006407":{"id":"n2140006407","loc":[-85.6362148,41.9447874]},"n2140006409":{"id":"n2140006409","loc":[-85.6379476,41.9445869]},"n2140006411":{"id":"n2140006411","loc":[-85.6378485,41.9445674]},"n2140006413":{"id":"n2140006413","loc":[-85.6378952,41.9444547]},"n2140006415":{"id":"n2140006415","loc":[-85.6379962,41.944477]},"n2140006421":{"id":"n2140006421","loc":[-85.6355248,41.9433702]},"n2140006423":{"id":"n2140006423","loc":[-85.6378471,41.9439233]},"n2140006425":{"id":"n2140006425","loc":[-85.6378913,41.9441238]},"n2140006426":{"id":"n2140006426","loc":[-85.6381674,41.9442289]},"n2140006427":{"id":"n2140006427","loc":[-85.6382359,41.9440975]},"n2140006428":{"id":"n2140006428","loc":[-85.6382071,41.9440252]},"n2140006429":{"id":"n2140006429","loc":[-85.6381409,41.9439973]},"n2140006430":{"id":"n2140006430","loc":[-85.6380569,41.9440153]},"n2140006433":{"id":"n2140006433","loc":[-85.6379071,41.9442467]},"n2140006435":{"id":"n2140006435","loc":[-85.6381634,41.9443125]},"n2140006436":{"id":"n2140006436","loc":[-85.6382407,41.944301]},"n2140006438":{"id":"n2140006438","loc":[-85.6382761,41.9442188]},"n2140006439":{"id":"n2140006439","loc":[-85.6382429,41.9441761]},"n2140006440":{"id":"n2140006440","loc":[-85.6382016,41.9441632]},"n2140006441":{"id":"n2140006441","loc":[-85.6378185,41.9439835]},"n2166205688":{"id":"n2166205688","loc":[-85.6349963,41.9444392]},"n2168544780":{"id":"n2168544780","loc":[-85.633944,41.945807]},"n2168544781":{"id":"n2168544781","loc":[-85.6340783,41.9458621]},"n2168544782":{"id":"n2168544782","loc":[-85.6338184,41.9457548]},"n2168544783":{"id":"n2168544783","loc":[-85.6339925,41.9459777]},"n2168544784":{"id":"n2168544784","loc":[-85.6337317,41.9458698]},"n2168544785":{"id":"n2168544785","loc":[-85.6337297,41.9460042]},"n2168544786":{"id":"n2168544786","loc":[-85.633919,41.9460797]},"n2168544787":{"id":"n2168544787","loc":[-85.6338672,41.9459263]},"n2168544788":{"id":"n2168544788","loc":[-85.6338246,41.9459853]},"n2168544789":{"id":"n2168544789","loc":[-85.6337615,41.9459601]},"n2168544790":{"id":"n2168544790","loc":[-85.6342079,41.9460399]},"n2168544791":{"id":"n2168544791","loc":[-85.6343346,41.9458503]},"n2168544792":{"id":"n2168544792","loc":[-85.6343759,41.9458116]},"n2168544793":{"id":"n2168544793","loc":[-85.6344394,41.9458109]},"n2168544795":{"id":"n2168544795","loc":[-85.6344827,41.945851]},"n2168544797":{"id":"n2168544797","loc":[-85.6344807,41.945969]},"n2168544798":{"id":"n2168544798","loc":[-85.6344404,41.9459697]},"n2168544799":{"id":"n2168544799","loc":[-85.6344413,41.9460333]},"n2168544800":{"id":"n2168544800","loc":[-85.6342173,41.9460705]},"n2168544801":{"id":"n2168544801","loc":[-85.6342162,41.9460392]},"n2168544802":{"id":"n2168544802","loc":[-85.6344251,41.9460351]},"n2168544805":{"id":"n2168544805","loc":[-85.6344257,41.9460507]},"n2168544807":{"id":"n2168544807","loc":[-85.6344721,41.9460498]},"n2168544809":{"id":"n2168544809","loc":[-85.6344754,41.9461427]},"n2168544811":{"id":"n2168544811","loc":[-85.6344311,41.9461435]},"n2168544813":{"id":"n2168544813","loc":[-85.6344317,41.9461592]},"n2168544815":{"id":"n2168544815","loc":[-85.6343708,41.9461604]},"n2168544817":{"id":"n2168544817","loc":[-85.6343715,41.9461786]},"n2168544819":{"id":"n2168544819","loc":[-85.6343229,41.9461795]},"n2168544821":{"id":"n2168544821","loc":[-85.6343222,41.9461606]},"n2168544823":{"id":"n2168544823","loc":[-85.6342476,41.9461621]},"n2168544825":{"id":"n2168544825","loc":[-85.6342444,41.94607]},"n2168544827":{"id":"n2168544827","loc":[-85.634138,41.9461632]},"n2168544829":{"id":"n2168544829","loc":[-85.6342016,41.9460703]},"n2168544830":{"id":"n2168544830","loc":[-85.6332929,41.9463092]},"n2168544831":{"id":"n2168544831","loc":[-85.633122,41.946239]},"n2168544832":{"id":"n2168544832","loc":[-85.6332954,41.9460055]},"n2168544833":{"id":"n2168544833","loc":[-85.6333954,41.9460466]},"n2168544834":{"id":"n2168544834","loc":[-85.6334044,41.9460345]},"n2168544835":{"id":"n2168544835","loc":[-85.6334594,41.9460571]},"n2168544836":{"id":"n2168544836","loc":[-85.6333871,41.9461544]},"n2168544837":{"id":"n2168544837","loc":[-85.633403,41.9461609]},"n2168544838":{"id":"n2168544838","loc":[-85.6341683,41.9464167]},"n2168544839":{"id":"n2168544839","loc":[-85.6341711,41.9463411]},"n2168544840":{"id":"n2168544840","loc":[-85.6344471,41.9463469]},"n2168544841":{"id":"n2168544841","loc":[-85.6344441,41.9464243]},"n2168544842":{"id":"n2168544842","loc":[-85.6343622,41.9464226]},"n2168544843":{"id":"n2168544843","loc":[-85.6343593,41.9464989]},"n2168544844":{"id":"n2168544844","loc":[-85.6342812,41.9464973]},"n2168544845":{"id":"n2168544845","loc":[-85.634283,41.9464504]},"n2168544846":{"id":"n2168544846","loc":[-85.6342609,41.9464499]},"n2168544847":{"id":"n2168544847","loc":[-85.6342621,41.9464187]},"n2168544848":{"id":"n2168544848","loc":[-85.6348414,41.9463396]},"n2168544849":{"id":"n2168544849","loc":[-85.6348387,41.9461872]},"n2168544850":{"id":"n2168544850","loc":[-85.6351186,41.9461844]},"n2168544851":{"id":"n2168544851","loc":[-85.635119,41.9462112]},"n2168544852":{"id":"n2168544852","loc":[-85.6351918,41.9462104]},"n2168544853":{"id":"n2168544853","loc":[-85.6351944,41.9463515]},"n2168544854":{"id":"n2168544854","loc":[-85.6351049,41.9463524]},"n2168544855":{"id":"n2168544855","loc":[-85.6351046,41.946337]},"n2189153180":{"id":"n2189153180","loc":[-85.6340369,41.9469572]},"n2189153181":{"id":"n2189153181","loc":[-85.6342531,41.946953]},"n2189153183":{"id":"n2189153183","loc":[-85.6348115,41.9465468]},"n2189153184":{"id":"n2189153184","loc":[-85.6348105,41.9464569]},"n2189153185":{"id":"n2189153185","loc":[-85.6351431,41.9464549]},"n2189153186":{"id":"n2189153186","loc":[-85.6351441,41.9465448]},"n2189153187":{"id":"n2189153187","loc":[-85.6350077,41.9465456]},"n2189153188":{"id":"n2189153188","loc":[-85.635008,41.9465721]},"n2189153189":{"id":"n2189153189","loc":[-85.6348965,41.9465727]},"n2189153190":{"id":"n2189153190","loc":[-85.6348962,41.9465463]},"n2189153191":{"id":"n2189153191","loc":[-85.6348963,41.9471586]},"n2189153192":{"id":"n2189153192","loc":[-85.6348944,41.947032]},"n2189153193":{"id":"n2189153193","loc":[-85.6350241,41.947031]},"n2189153194":{"id":"n2189153194","loc":[-85.635026,41.9471575]},"n2189153195":{"id":"n2189153195","loc":[-85.6352328,41.9471053]},"n2189153196":{"id":"n2189153196","loc":[-85.6352359,41.9469906]},"n2189153197":{"id":"n2189153197","loc":[-85.6353694,41.9469925]},"n2189153198":{"id":"n2189153198","loc":[-85.6353664,41.9471072]},"n2189153199":{"id":"n2189153199","loc":[-85.6348241,41.9469287]},"n2189153200":{"id":"n2189153200","loc":[-85.6348248,41.9468185]},"n2189153201":{"id":"n2189153201","loc":[-85.6351199,41.9468195]},"n2189153202":{"id":"n2189153202","loc":[-85.6351192,41.9469298]},"n2189153203":{"id":"n2189153203","loc":[-85.6347965,41.9468057]},"n2189153204":{"id":"n2189153204","loc":[-85.634792,41.9466044]},"n2189153205":{"id":"n2189153205","loc":[-85.6349483,41.9466025]},"n2189153206":{"id":"n2189153206","loc":[-85.6349493,41.9466448]},"n2189153207":{"id":"n2189153207","loc":[-85.6349753,41.9466445]},"n2189153208":{"id":"n2189153208","loc":[-85.6349743,41.9465995]},"n2189153209":{"id":"n2189153209","loc":[-85.6351173,41.9465977]},"n2189153210":{"id":"n2189153210","loc":[-85.6351219,41.9468015]},"n2189153211":{"id":"n2189153211","loc":[-85.6349806,41.9468032]},"n2189153212":{"id":"n2189153212","loc":[-85.6349794,41.9467519]},"n2189153213":{"id":"n2189153213","loc":[-85.6349521,41.9467523]},"n2189153214":{"id":"n2189153214","loc":[-85.6349532,41.9468037]},"n2189153215":{"id":"n2189153215","loc":[-85.6346302,41.9468381]},"n2189153216":{"id":"n2189153216","loc":[-85.6343028,41.9468449]},"n2189153217":{"id":"n2189153217","loc":[-85.6342006,41.9468297]},"n2189153218":{"id":"n2189153218","loc":[-85.6336698,41.9465918]},"n2189153219":{"id":"n2189153219","loc":[-85.6344663,41.9466639]},"n2189153220":{"id":"n2189153220","loc":[-85.6344639,41.9466015]},"n2189153221":{"id":"n2189153221","loc":[-85.6342283,41.9466065]},"n2189153222":{"id":"n2189153222","loc":[-85.6342303,41.9466587]},"n2189153223":{"id":"n2189153223","loc":[-85.6342843,41.9466575]},"n2189153224":{"id":"n2189153224","loc":[-85.6342851,41.9466794]},"n2189153225":{"id":"n2189153225","loc":[-85.6343475,41.9466781]},"n2189153226":{"id":"n2189153226","loc":[-85.634347,41.9466664]},"n2189153227":{"id":"n2189153227","loc":[-85.6354428,41.9470148]},"n2189153228":{"id":"n2189153228","loc":[-85.6354432,41.9468005]},"n2189153229":{"id":"n2189153229","loc":[-85.6360277,41.9468011]},"n2189153230":{"id":"n2189153230","loc":[-85.6360273,41.9470154]},"n2189153231":{"id":"n2189153231","loc":[-85.6354565,41.9465823]},"n2189153232":{"id":"n2189153232","loc":[-85.6354496,41.946218]},"n2189153233":{"id":"n2189153233","loc":[-85.6356355,41.9465788]},"n2189153234":{"id":"n2189153234","loc":[-85.6357155,41.9468008]},"n2189153235":{"id":"n2189153235","loc":[-85.6359539,41.9467969]},"n2189153236":{"id":"n2189153236","loc":[-85.6359561,41.9463036]},"n2189153237":{"id":"n2189153237","loc":[-85.6360129,41.9464793]},"n2189153238":{"id":"n2189153238","loc":[-85.6360152,41.9463898]},"n2189153239":{"id":"n2189153239","loc":[-85.6359607,41.9464928]},"n2189153240":{"id":"n2189153240","loc":[-85.6356903,41.9462227]},"n2189153242":{"id":"n2189153242","loc":[-85.6354163,41.946142]},"n2189153243":{"id":"n2189153243","loc":[-85.6357546,41.9462214]},"n2189153244":{"id":"n2189153244","loc":[-85.6357937,41.9462542]},"n2189153245":{"id":"n2189153245","loc":[-85.6358723,41.9467048]},"n2189153246":{"id":"n2189153246","loc":[-85.6361494,41.946757]},"n2189153247":{"id":"n2189153247","loc":[-85.6354173,41.9469082]},"n2189153248":{"id":"n2189153248","loc":[-85.635443,41.9469079]},"n2189153249":{"id":"n2189153249","loc":[-85.6360275,41.9469093]},"n2189153250":{"id":"n2189153250","loc":[-85.6361542,41.946915]},"n2189153251":{"id":"n2189153251","loc":[-85.6358654,41.9464843]},"n2189153252":{"id":"n2189153252","loc":[-85.6359549,41.9467499]},"n2189153253":{"id":"n2189153253","loc":[-85.6357172,41.9466335]},"n2189153254":{"id":"n2189153254","loc":[-85.6355644,41.9461768]},"n2189153255":{"id":"n2189153255","loc":[-85.6355655,41.946528]},"n2189153256":{"id":"n2189153256","loc":[-85.6357055,41.9465971]},"n2189153257":{"id":"n2189153257","loc":[-85.635869,41.9465971]},"n2189153259":{"id":"n2189153259","loc":[-85.6354561,41.9470278]},"n2189153260":{"id":"n2189153260","loc":[-85.6357961,41.9470233]},"n2189153261":{"id":"n2189153261","loc":[-85.6357977,41.9470907]},"n2189153262":{"id":"n2189153262","loc":[-85.6357297,41.9470916]},"n2189153263":{"id":"n2189153263","loc":[-85.635733,41.947233]},"n2189153264":{"id":"n2189153264","loc":[-85.6362674,41.9468637]},"n2189153265":{"id":"n2189153265","loc":[-85.6362646,41.9467047]},"n2189153266":{"id":"n2189153266","loc":[-85.6363267,41.9467047]},"n2189153267":{"id":"n2189153267","loc":[-85.6362633,41.9465848]},"n2189153268":{"id":"n2189153268","loc":[-85.6363805,41.9465468]},"n2189153269":{"id":"n2189153269","loc":[-85.6364604,41.9466842]},"n2189153270":{"id":"n2189153270","loc":[-85.6364604,41.9468647]},"n2199109756":{"id":"n2199109756","loc":[-85.6337134,41.9471841]},"n2199109757":{"id":"n2199109757","loc":[-85.6336514,41.94716]},"n2199109758":{"id":"n2199109758","loc":[-85.6337043,41.9470847]},"n2199109759":{"id":"n2199109759","loc":[-85.6335997,41.9470441]},"n2199109760":{"id":"n2199109760","loc":[-85.6335064,41.9471771]},"n185960195":{"id":"n185960195","loc":[-85.6295992,41.9524346]},"n185960796":{"id":"n185960796","loc":[-85.634723,41.953681]},"n185961396":{"id":"n185961396","loc":[-85.634767,41.959009]},"n185962625":{"id":"n185962625","loc":[-85.635175,41.97201]},"n185964982":{"id":"n185964982","loc":[-85.632799,41.9440543]},"n185965289":{"id":"n185965289","loc":[-85.634621,41.947323]},"n185965291":{"id":"n185965291","loc":[-85.636166,41.947296]},"n185965399":{"id":"n185965399","loc":[-85.634776,41.959834]},"n185966937":{"id":"n185966937","loc":[-85.633183,41.947315]},"n185966948":{"id":"n185966948","loc":[-85.626406,41.957188]},"n185967422":{"id":"n185967422","loc":[-85.6320229,41.9490123]},"n185967917":{"id":"n185967917","loc":[-85.634763,41.958292]},"n185967918":{"id":"n185967918","loc":[-85.636271,41.958311]},"n185968100":{"id":"n185968100","loc":[-85.630835,41.950656]},"n185970515":{"id":"n185970515","loc":[-85.634832,41.963866]},"n185971578":{"id":"n185971578","loc":[-85.634641,41.948627]},"n185971580":{"id":"n185971580","loc":[-85.6361818,41.9486135]},"n185971631":{"id":"n185971631","loc":[-85.634729,41.954667]},"n185971632":{"id":"n185971632","loc":[-85.636236,41.954656]},"n185972155":{"id":"n185972155","loc":[-85.623333,41.961987]},"n185974583":{"id":"n185974583","loc":[-85.634686,41.951158]},"n185974585":{"id":"n185974585","loc":[-85.6362059,41.9511457]},"n185975064":{"id":"n185975064","loc":[-85.636218,41.953667]},"n185975735":{"id":"n185975735","loc":[-85.634923,41.969269]},"n185978390":{"id":"n185978390","loc":[-85.634668,41.949875]},"n185978392":{"id":"n185978392","loc":[-85.634686,41.952415]},"n185978394":{"id":"n185978394","loc":[-85.634726,41.955921]},"n185978399":{"id":"n185978399","loc":[-85.6347861,41.9606613]},"n185978402":{"id":"n185978402","loc":[-85.634806,41.961485]},"n185978406":{"id":"n185978406","loc":[-85.6348298,41.964783]},"n185978410":{"id":"n185978410","loc":[-85.6348766,41.9677088]},"n185978414":{"id":"n185978414","loc":[-85.634938,41.971566]},"n185978415":{"id":"n185978415","loc":[-85.634942,41.971611]},"n185978417":{"id":"n185978417","loc":[-85.634952,41.971655]},"n185978419":{"id":"n185978419","loc":[-85.634989,41.971741]},"n185978420":{"id":"n185978420","loc":[-85.635063,41.971864]},"n185978787":{"id":"n185978787","loc":[-85.627936,41.954693]},"n185978790":{"id":"n185978790","loc":[-85.626832,41.954677]},"n185978967":{"id":"n185978967","loc":[-85.632278,41.948613]},"n185980735":{"id":"n185980735","loc":[-85.628639,41.953725]},"n185982163":{"id":"n185982163","loc":[-85.636233,41.952398]},"n185982193":{"id":"n185982193","loc":[-85.6313855,41.9499125]},"n185982195":{"id":"n185982195","loc":[-85.6304857,41.9511945]},"n185982196":{"id":"n185982196","loc":[-85.626336,41.957291]},"n185982197":{"id":"n185982197","loc":[-85.625578,41.958664]},"n185982198":{"id":"n185982198","loc":[-85.624619,41.960145]},"n185982200":{"id":"n185982200","loc":[-85.624494,41.960338]},"n185984017":{"id":"n185984017","loc":[-85.636163,41.947382]},"n185984020":{"id":"n185984020","loc":[-85.636188,41.9498803]},"n185984022":{"id":"n185984022","loc":[-85.636276,41.955919]},"n185984024":{"id":"n185984024","loc":[-85.636279,41.956901]},"n185988036":{"id":"n185988036","loc":[-85.631422,41.948294]},"n185988867":{"id":"n185988867","loc":[-85.63102,41.948805]},"n185988869":{"id":"n185988869","loc":[-85.630773,41.949209]},"n185988871":{"id":"n185988871","loc":[-85.63005,41.95016]},"n185988872":{"id":"n185988872","loc":[-85.629423,41.951016]},"n185988873":{"id":"n185988873","loc":[-85.629252,41.951256]},"n185988875":{"id":"n185988875","loc":[-85.629126,41.951489]},"n185988877":{"id":"n185988877","loc":[-85.628991,41.951704]},"n185988878":{"id":"n185988878","loc":[-85.628689,41.952112]},"n185988879":{"id":"n185988879","loc":[-85.628313,41.952666]},"n185988880":{"id":"n185988880","loc":[-85.627687,41.953529]},"n185988882":{"id":"n185988882","loc":[-85.627394,41.953947]},"n185988884":{"id":"n185988884","loc":[-85.627287,41.954128]},"n1819858502":{"id":"n1819858502","loc":[-85.6328435,41.9455473]},"n1819858510":{"id":"n1819858510","loc":[-85.6324841,41.9453438]},"n1819858515":{"id":"n1819858515","loc":[-85.6318511,41.9446409]},"n1819858520":{"id":"n1819858520","loc":[-85.6326558,41.9454708]},"n1819858522":{"id":"n1819858522","loc":[-85.6319048,41.9447407]},"n1819858524":{"id":"n1819858524","loc":[-85.6317718,41.9443666]},"n1819858530":{"id":"n1819858530","loc":[-85.632055,41.9449128]},"n2139795768":{"id":"n2139795768","loc":[-85.6243023,41.9606102]},"n2139832645":{"id":"n2139832645","loc":[-85.6324455,41.9448607]},"n2139832649":{"id":"n2139832649","loc":[-85.6328043,41.9454773]},"n2139832651":{"id":"n2139832651","loc":[-85.6322547,41.9449621]},"n2139832675":{"id":"n2139832675","loc":[-85.6327356,41.944757]},"n2139832677":{"id":"n2139832677","loc":[-85.6325433,41.9448599]},"n2139832680":{"id":"n2139832680","loc":[-85.6328885,41.9455614]},"n2139832682":{"id":"n2139832682","loc":[-85.6320913,41.9449492]},"n2139832684":{"id":"n2139832684","loc":[-85.6325366,41.9447133]},"n2139832688":{"id":"n2139832688","loc":[-85.6322786,41.94485]},"n2139832718":{"id":"n2139832718","loc":[-85.6327486,41.9432475]},"n2139832719":{"id":"n2139832719","loc":[-85.6327926,41.9431773]},"n2139832720":{"id":"n2139832720","loc":[-85.6329033,41.943153]},"n2139832727":{"id":"n2139832727","loc":[-85.6328975,41.9430783]},"n2139844839":{"id":"n2139844839","loc":[-85.6326261,41.9432308]},"n2189015992":{"id":"n2189015992","loc":[-85.6347706,41.9593383]},"n2189153179":{"id":"n2189153179","loc":[-85.6340476,41.9472565]},"n2189153182":{"id":"n2189153182","loc":[-85.6342638,41.9472522]},"n2189153241":{"id":"n2189153241","loc":[-85.6354184,41.9473091]},"n2189153258":{"id":"n2189153258","loc":[-85.6354611,41.9472366]},"n2189153277":{"id":"n2189153277","loc":[-85.6328948,41.9462374]},"n2199109755":{"id":"n2199109755","loc":[-85.6336729,41.9472417]},"w203970139":{"id":"w203970139","tags":{"building":"yes"},"nodes":["n2139824793","n2139824787","n2139824773","n2139824778","n2139824793"]},"w203970098":{"id":"w203970098","tags":{"building":"yes"},"nodes":["n2139824748","n2139824712","n2139824726","n2139824760","n2139824748"]},"w208643132":{"id":"w208643132","tags":{"area":"yes","building":"yes"},"nodes":["n2189153195","n2189153196","n2189153197","n2189153198","n2189153195"]},"w203970094":{"id":"w203970094","tags":{"building":"yes"},"nodes":["n2139824755","n2139824753","n2139824759","n2139824764","n2139824763","n2139824767","n2139824770","n2139824782","n2139824772","n2139824756","n2139824751","n2139824754","n2139824755"]},"w208643138":{"id":"w208643138","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153231","n2189153232","n2189153240","n2189153244","n2189153236","n2189153238","n2189153237","n2189153239","n2189153252","n2189153235","n2189153234","n2189153253","n2189153233","n2189153231"]},"w203970125":{"id":"w203970125","tags":{"building":"yes"},"nodes":["n2139824735","n2139824738","n2139824757","n2139824749","n2139824735"]},"w170848823":{"id":"w170848823","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819849189","n1819858516","n1819858519","n1819858504","n1819858525","n1819858506","n1819858513"]},"w203970898":{"id":"w203970898","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832645","n2139832647","n2139832649","n2139832651","n2139832645"]},"w203970134":{"id":"w203970134","tags":{"building":"yes"},"nodes":["n2139824796","n2139824803","n2139824797","n2139824788","n2139824796"]},"w203970104":{"id":"w203970104","tags":{"building":"yes"},"nodes":["n2139824733","n2139824730","n2139824714","n2139824721","n2139824733"]},"w206805245":{"id":"w206805245","tags":{"area":"yes","building":"yes"},"nodes":["n2168544780","n2168544781","n2139824796","n2139824803","n2168544780"]},"w206805252":{"id":"w206805252","tags":{"area":"yes","building":"yes"},"nodes":["n2168544838","n2168544839","n2168544840","n2168544841","n2168544842","n2168544843","n2168544844","n2168544845","n2168544846","n2168544847","n2168544838"]},"w203970099":{"id":"w203970099","tags":{"building":"yes"},"nodes":["n2139824783","n2139824795","n2139824790","n2139824779","n2139824783"]},"w17967730":{"id":"w17967730","tags":{"highway":"residential","name":"Water St"},"nodes":["n185963451","n2189153277","n185988036","n185988867","n185988869","n185988871","n185988872","n185988873","n185988875","n185988877","n185988878","n185988879","n185988880","n185988882","n185988884","n185978790"]},"w208643133":{"id":"w208643133","tags":{"area":"yes","building":"yes"},"nodes":["n2189153199","n2189153200","n2189153201","n2189153202","n2189153199"]},"w203970127":{"id":"w203970127","tags":{"building":"yes"},"nodes":["n2139824794","n2139824783","n2139824789","n2139824797","n2139824794"]},"w208643139":{"id":"w208643139","tags":{"highway":"service"},"nodes":["n185988237","n2189153242","n2189153247","n2189153241"]},"w203988297":{"id":"w203988297","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140006423","n2140006441","n2140006425","n2140006426","n2140006440","n2140006427","n2140006428","n2140006429","n2140006430","n2140006423"]},"w206805250":{"id":"w206805250","tags":{"area":"yes","building":"yes"},"nodes":["n2168544827","n2168544823","n2168544825","n2168544800","n2168544829","n2168544827"]},"w208643140":{"id":"w208643140","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153242","n2189153254","n2189153243","n2189153244","n2189153251","n2189153257","n2189153245","n2189153252","n2189153246"]},"w203974055":{"id":"w203974055","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870376","n2139870377"]},"w206805247":{"id":"w206805247","tags":{"area":"yes","building":"yes"},"nodes":["n2168544785","n2168544786","n2168544783","n2168544787","n2168544788","n2168544789","n2168544785"]},"w17964996":{"id":"w17964996","tags":{"highway":"residential","name":"Foster St"},"nodes":["n1819858524","n1819858515","n1819858522","n1819858530","n2139832682","n1819858510","n1819858520","n1819858502","n2139832680","n185963451","n1819858527","n185963452","n185963453","n185963454","n185963455","n185963456"]},"w208643144":{"id":"w208643144","tags":{"area":"yes","building":"yes"},"nodes":["n2189153264","n2189153265","n2189153266","n2189153267","n2189153268","n2189153269","n2189153270","n2189153264"]},"w203970914":{"id":"w203970914","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832722","n2139832723","n2139832724","n2139832725","n2139832726","n2139832727","n2139844839","n2139832722"]},"w208643143":{"id":"w208643143","tags":{"area":"yes","building":"yes"},"nodes":["n2189153258","n2189153259","n2189153260","n2189153261","n2189153262","n2189153263","n2189153258"]},"w203049590":{"id":"w203049590","tags":{"amenity":"parking","area":"yes"},"nodes":["n2130304152","n2130304153","n2140006403","n2130304154","n2130304156","n2130304155","n2130304160","n2130304152"]},"w203974054":{"id":"w203974054","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858971","n2139870373","n2139870374"]},"w203049595":{"id":"w203049595","tags":{"highway":"service"},"nodes":["n2130304158","n2130304159","n2130304160","n2139832635","n2139832639"]},"w203970913":{"id":"w203970913","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832715","n2139832716","n2139832717","n2139832718","n2139832719","n2139832720","n2139832721","n2139832716"]},"w208643134":{"id":"w208643134","tags":{"area":"yes","building":"yes"},"nodes":["n2189153203","n2189153204","n2189153205","n2189153206","n2189153207","n2189153208","n2189153209","n2189153210","n2189153211","n2189153212","n2189153213","n2189153214","n2189153203"]},"w134150808":{"id":"w134150808","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988239","n185984009","n185988241","n1475284019"]},"w203970115":{"id":"w203970115","tags":{"building":"yes"},"nodes":["n2139824761","n2139824727","n2139824736","n2139824771","n2139824761"]},"w208643130":{"id":"w208643130","tags":{"area":"yes","building":"yes"},"nodes":["n2189153183","n2189153184","n2189153185","n2189153186","n2189153187","n2189153188","n2189153189","n2189153190","n2189153183"]},"w206805246":{"id":"w206805246","tags":{"area":"yes","building":"yes"},"nodes":["n2168544782","n2168544780","n2168544781","n2168544783","n2168544787","n2168544784","n2168544782"]},"w203970138":{"id":"w203970138","tags":{"building":"yes"},"nodes":["n2139824729","n2139824720","n2139824702","n2139824707","n2139824729"]},"w203970133":{"id":"w203970133","tags":{"building":"yes"},"nodes":["n2139824748","n2139824737","n2139824717","n2139824728","n2139824748"]},"w203970907":{"id":"w203970907","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832700","n2139832701","n2139832702"]},"w203974056":{"id":"w203974056","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870377","n2139870378"]},"w203970897":{"id":"w203970897","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2130304156","n2166205688","n2139832635","n2139832636","n2139832637","n2139832639","n2139832641","n2166205688"]},"w203974057":{"id":"w203974057","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870375","n2139870376"]},"w203049594":{"id":"w203049594","tags":{"highway":"service"},"nodes":["n2130304156","n2139870378","n2139832706","n2139832704","n2130304157"]},"w203970122":{"id":"w203970122","tags":{"building":"yes"},"nodes":["n2139824757","n2139824740","n2139824747","n2139824762","n2139824757"]},"w208643136":{"id":"w208643136","tags":{"area":"yes","building":"yes"},"nodes":["n2189153219","n2189153220","n2189153221","n2189153222","n2189153223","n2189153224","n2189153225","n2189153226","n2189153219"]},"w203970128":{"id":"w203970128","tags":{"building":"yes"},"nodes":["n2139824732","n2139824752","n2139824744","n2139824724","n2139824732"]},"w203970097":{"id":"w203970097","tags":{"building":"yes"},"nodes":["n2139824737","n2139824733","n2139824710","n2139824716","n2139824737"]},"w203970137":{"id":"w203970137","tags":{"building":"yes"},"nodes":["n2139824765","n2139824774","n2139824758","n2139824746","n2139824765"]},"w134150840":{"id":"w134150840","tags":{"highway":"residential","name":"Moore St"},"nodes":["n1475284019","n185988243","n185988244","n185988245"]},"w17967628":{"id":"w17967628","tags":{"highway":"residential","name":"Moore St"},"nodes":["n185978388","n2139832709","n185988237","n185988239"]},"w203988292":{"id":"w203988292","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2140006407","n2140006405"]},"w203970118":{"id":"w203970118","tags":{"building":"yes"},"nodes":["n2139824775","n2139824785","n2139824780","n2139824768","n2139824775"]},"w203970121":{"id":"w203970121","tags":{"building":"yes"},"nodes":["n2139824768","n2139824781","n2139824776","n2139824765","n2139824768"]},"w17967752":{"id":"w17967752","tags":{"highway":"residential","name":"Railroad Drive"},"nodes":["n185964980","n2139832699","n2139832700","n2130304158","n185988969","n185988971","n185988972","n1475284011"]},"w203970136":{"id":"w203970136","tags":{"building":"yes"},"nodes":["n2139824798","n2139824793","n2139824777","n2139824784","n2139824798"]},"w203970142":{"id":"w203970142","tags":{"building":"yes"},"nodes":["n2139824808","n2139824809","n2139824807","n2139824806","n2139824801","n2139824800","n2139824804","n2139824805","n2139824808"]},"w208643137":{"id":"w208643137","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153227","n2189153248","n2189153228","n2189153234","n2189153235","n2189153229","n2189153249","n2189153230","n2189153227"]},"w208643129":{"id":"w208643129","tags":{"area":"yes","building":"yes"},"nodes":["n2189153179","n2189153180","n2189153181","n2189153182","n2189153179"]},"w203970909":{"id":"w203970909","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832703","n2139832704","n2139832706","n2139832708","n2139832703"]},"w203970905":{"id":"w203970905","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832688","n2139832691"]},"w203988298":{"id":"w203988298","tags":{"highway":"service"},"nodes":["n2140006431","n2140006433","n2140006435","n2140006436","n2140006437","n2140006438","n2140006439","n2140006440"]},"w203970106":{"id":"w203970106","tags":{"building":"yes"},"nodes":["n2139824798","n2139824791","n2139824799","n2139824802","n2139824798"]},"w203970129":{"id":"w203970129","tags":{"building":"yes"},"nodes":["n2139824787","n2139824782","n2139824766","n2139824769","n2139824787"]},"w208643131":{"id":"w208643131","tags":{"area":"yes","building":"yes"},"nodes":["n2189153191","n2189153192","n2189153193","n2189153194","n2189153191"]},"w206805249":{"id":"w206805249","tags":{"area":"yes","building":"yes"},"nodes":["n2168544800","n2168544801","n2168544802","n2168544805","n2168544807","n2168544809","n2168544811","n2168544813","n2168544815","n2168544817","n2168544819","n2168544821","n2168544823","n2168544825","n2168544800"]},"w134150800":{"id":"w134150800","tags":{"bridge":"yes","highway":"primary","name":"W Michigan Ave","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964972","n185964976"]},"w17966984":{"id":"w17966984","tags":{"highway":"residential","name":"Portage Avenue"},"nodes":["n185978375","n185963456","n2189153218","n185966937","n185978967","n185967422","n185982193","n185968100","n185982195","n185960195","n185980735","n185978787","n185966948","n185982196","n185982197","n185982198","n185982200","n2139795768","n185972155"]},"w203988294":{"id":"w203988294","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2140006409","n2140006411","n2140006413","n2140006415","n2140006409"]},"w203970912":{"id":"w203970912","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832711","n2139832712","n2139832713","n2139832714","n2139832711"]},"w203970119":{"id":"w203970119","tags":{"building":"yes"},"nodes":["n2139824713","n2139824705","n2139824683","n2139824689","n2139824713"]},"w203970114":{"id":"w203970114","tags":{"building":"yes"},"nodes":["n2139824735","n2139824750","n2139824745","n2139824732","n2139824735"]},"w208643142":{"id":"w208643142","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153254","n2189153255","n2189153256","n2189153257"]},"w206805253":{"id":"w206805253","tags":{"area":"yes","building":"yes"},"nodes":["n2168544848","n2168544849","n2168544850","n2168544851","n2168544852","n2168544853","n2168544854","n2168544855","n2168544848"]},"w143497377":{"id":"w143497377","tags":{"highway":"primary","name":"North Main Street","old_ref":"US 131","ref":"US 131 Business"},"nodes":["n185962625","n185978420","n185978419","n185978417","n185978415","n185978414","n185975735","n1475293254","n185978410","n185978406","n185970515","n185978402","n185978399","n185965399","n2189015992","n185961396","n185967917","n185978394","n185971631","n185960796","n185978392","n185974583","n185978390","n185971578","n185965289","n2189153215","n185978388","n185978383","n185978381","n185978379","n185978377","n185978375","n185964982"]},"w134150811":{"id":"w134150811","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964976","n2130304157","n1475284023","n2139832715","n185964980","n185964982"]},"w208643135":{"id":"w208643135","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153215","n2189153216","n2189153217","n2189153218"]},"w17967183":{"id":"w17967183","tags":{"highway":"residential","name":"West Street"},"nodes":["n1475284011","n185984011","n185984013","n185984015","n2189153246","n2189153250","n185965291","n185984017","n185971580","n185984020","n185974585","n185982163","n185975064","n185971632","n185984022","n185984024","n185967918"]},"w134150778":{"id":"w134150778","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988245","n1475283992","n185975911"]},"w206805248":{"id":"w206805248","tags":{"area":"yes","building":"yes"},"nodes":["n2168544790","n2168544791","n2168544792","n2168544793","n2168544795","n2168544797","n2168544798","n2168544799","n2168544802","n2168544801","n2168544790"]},"w203974058":{"id":"w203974058","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870374","n2139870375"]},"w203970902":{"id":"w203970902","tags":{"highway":"service"},"nodes":["n2139832678","n2139832691","n2139832680"]},"w203988296":{"id":"w203988296","tags":{"highway":"path"},"nodes":["n2139858967","n2140006421","n2139858935"]},"w206805251":{"id":"w206805251","tags":{"area":"yes","building":"yes"},"nodes":["n2168544830","n2168544831","n2168544832","n2168544833","n2168544834","n2168544835","n2168544836","n2168544837","n2168544830"]},"w203970906":{"id":"w203970906","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832693","n2139832694","n2139832696","n2139832697","n2139832698","n2139832693"]},"w203049598":{"id":"w203049598","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2130304162","n2130304163","n2130304164","n2130304165","n2130304162"]},"w203970911":{"id":"w203970911","tags":{"highway":"service"},"nodes":["n2139832709","n2139832714","n2139832713","n2139832710","n185988971"]},"w203970105":{"id":"w203970105","tags":{"building":"yes"},"nodes":["n2139824779","n2139824792","n2139824786","n2139824775","n2139824779"]},"w203988290":{"id":"w203988290","tags":{"highway":"footway"},"nodes":["n2140006403","n2140006407"]},"w203970900":{"id":"w203970900","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832653","n2139832663","n2139832665","n2139832667","n2139832669","n2139832671","n2139832673","n2139832675","n2139832677","n2139832653"]},"w209717048":{"id":"w209717048","tags":{"area":"yes","building":"yes"},"nodes":["n2199109755","n2199109756","n2199109757","n2199109758","n2199109759","n2199109760","n2199109755"]},"w208643141":{"id":"w208643141","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153247","n2189153248","n2189153249","n2189153250"]},"w203970903":{"id":"w203970903","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832682","n2139832688","n2139832684","n2139832678","n2139832686"]},"n354002527":{"id":"n354002527","loc":[-85.6236039,41.9458813],"tags":{"amenity":"school","name":"Barrows School"}},"n185963396":{"id":"n185963396","loc":[-85.627401,41.943496]},"n185963397":{"id":"n185963397","loc":[-85.627403,41.943625]},"n185965101":{"id":"n185965101","loc":[-85.626409,41.943215]},"n185971474":{"id":"n185971474","loc":[-85.624884,41.943508]},"n185971475":{"id":"n185971475","loc":[-85.625191,41.943509]},"n185971482":{"id":"n185971482","loc":[-85.624882,41.94382]},"n185983135":{"id":"n185983135","loc":[-85.624893,41.945616]},"n185983137":{"id":"n185983137","loc":[-85.624912,41.946524]},"n185988027":{"id":"n185988027","loc":[-85.622721,41.946535]},"n185963398":{"id":"n185963398","loc":[-85.6273993,41.9446899]},"n185983238":{"id":"n185983238","loc":[-85.6227157,41.9456321]},"n185980374":{"id":"n185980374","loc":[-85.6248856,41.9447242]},"n185980373":{"id":"n185980373","loc":[-85.6226744,41.9447371]},"n2196831342":{"id":"n2196831342","loc":[-85.6250924,41.945063]},"n2196831343":{"id":"n2196831343","loc":[-85.6252335,41.9450636]},"n2196831344":{"id":"n2196831344","loc":[-85.6252286,41.9448707]},"n2196831345":{"id":"n2196831345","loc":[-85.6250661,41.9448707]},"n2196831346":{"id":"n2196831346","loc":[-85.6250243,41.9449012]},"n2196831347":{"id":"n2196831347","loc":[-85.6250251,41.9449244]},"n2196831348":{"id":"n2196831348","loc":[-85.6250867,41.9449257]},"n2196831349":{"id":"n2196831349","loc":[-85.625349,41.9445058]},"n2196831350":{"id":"n2196831350","loc":[-85.6253471,41.9443882]},"n2196831351":{"id":"n2196831351","loc":[-85.6251516,41.94439]},"n2196831352":{"id":"n2196831352","loc":[-85.6251522,41.9444308]},"n2196831353":{"id":"n2196831353","loc":[-85.6251344,41.9444309]},"n2196831354":{"id":"n2196831354","loc":[-85.6251356,41.9445077]},"n2196831355":{"id":"n2196831355","loc":[-85.6232357,41.9463406]},"n2196831356":{"id":"n2196831356","loc":[-85.6232409,41.9460668]},"n2196831357":{"id":"n2196831357","loc":[-85.6232072,41.9460665]},"n2196831358":{"id":"n2196831358","loc":[-85.6232117,41.9458272]},"n2196831359":{"id":"n2196831359","loc":[-85.6229808,41.9458248]},"n2196831360":{"id":"n2196831360","loc":[-85.6229763,41.9460627]},"n2196831361":{"id":"n2196831361","loc":[-85.623006,41.946063]},"n2196831362":{"id":"n2196831362","loc":[-85.6230023,41.9462557]},"n2196831363":{"id":"n2196831363","loc":[-85.6230755,41.9462565]},"n2196831364":{"id":"n2196831364","loc":[-85.6230739,41.9463389]},"n185947349":{"id":"n185947349","loc":[-85.618327,41.945607]},"n185947359":{"id":"n185947359","loc":[-85.615453,41.945597]},"n185947378":{"id":"n185947378","loc":[-85.617231,41.945603]},"n185947474":{"id":"n185947474","loc":[-85.616136,41.945602]},"n185948972":{"id":"n185948972","loc":[-85.615273,41.945637]},"n185955019":{"id":"n185955019","loc":[-85.620172,41.945627]},"n185960682":{"id":"n185960682","loc":[-85.622759,41.951845]},"n185961369":{"id":"n185961369","loc":[-85.622758,41.947444]},"n185961371":{"id":"n185961371","loc":[-85.624908,41.947416]},"n185963392":{"id":"n185963392","loc":[-85.6270462,41.9409953]},"n185963393":{"id":"n185963393","loc":[-85.627295,41.941304]},"n185963394":{"id":"n185963394","loc":[-85.627352,41.94148]},"n185963395":{"id":"n185963395","loc":[-85.62737,41.942261]},"n185965099":{"id":"n185965099","loc":[-85.6264,41.942263]},"n185965108":{"id":"n185965108","loc":[-85.622769,41.949224]},"n185965110":{"id":"n185965110","loc":[-85.624937,41.949237]},"n185966295":{"id":"n185966295","loc":[-85.6299942,41.9446689]},"n185966342":{"id":"n185966342","loc":[-85.624873,41.942022]},"n185970222":{"id":"n185970222","loc":[-85.622761,41.948357]},"n185970224":{"id":"n185970224","loc":[-85.624924,41.9483338]},"n185971477":{"id":"n185971477","loc":[-85.620051,41.94383]},"n185971478":{"id":"n185971478","loc":[-85.621219,41.943801]},"n185971481":{"id":"n185971481","loc":[-85.621812,41.943807]},"n185973866":{"id":"n185973866","loc":[-85.627629,41.946498]},"n185974699":{"id":"n185974699","loc":[-85.6227688,41.950119]},"n185978800":{"id":"n185978800","loc":[-85.623953,41.954684]},"n185980372":{"id":"n185980372","loc":[-85.621459,41.944756]},"n185980378":{"id":"n185980378","loc":[-85.6286375,41.9446764]},"n185980380":{"id":"n185980380","loc":[-85.630139,41.944661]},"n185980382":{"id":"n185980382","loc":[-85.630298,41.944635]},"n185980384":{"id":"n185980384","loc":[-85.630759,41.94454]},"n185980386":{"id":"n185980386","loc":[-85.6312369,41.9444548]},"n185983133":{"id":"n185983133","loc":[-85.6248672,41.9415903]},"n185983139":{"id":"n185983139","loc":[-85.624951,41.950239]},"n185983140":{"id":"n185983140","loc":[-85.624934,41.950681]},"n185983141":{"id":"n185983141","loc":[-85.624813,41.950983]},"n185983143":{"id":"n185983143","loc":[-85.6246225,41.951591]},"n185983144":{"id":"n185983144","loc":[-85.623908,41.9539165]},"n185983145":{"id":"n185983145","loc":[-85.6238903,41.9540956]},"n185983146":{"id":"n185983146","loc":[-85.623898,41.95431]},"n185983236":{"id":"n185983236","loc":[-85.628481,41.945611]},"n185985914":{"id":"n185985914","loc":[-85.620072,41.946538]},"n185986812":{"id":"n185986812","loc":[-85.6227785,41.9510005]},"n185988028":{"id":"n185988028","loc":[-85.6281401,41.9469632]},"n185988030":{"id":"n185988030","loc":[-85.6282451,41.9470314]},"n185988032":{"id":"n185988032","loc":[-85.6283312,41.9470656]},"w17964989":{"id":"w17964989","tags":{"highway":"residential","name":"Middle St"},"nodes":["n185963392","n185963393","n185963394","n185963395","n185963396","n185963397","n185963398"]},"w17965158":{"id":"w17965158","tags":{"access":"private","highway":"service","name":"Battle St"},"nodes":["n185965099","n185965101"]},"w41074896":{"id":"w41074896","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980372","n185980373","n185980374","n185963398","n185980378","n185966295","n185980380","n185980382","n185980384","n185980386"]},"w17965846":{"id":"w17965846","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971477","n185971478","n185971481","n185971482"]},"w209470306":{"id":"w209470306","tags":{"area":"yes","building":"yes"},"nodes":["n2196831349","n2196831350","n2196831351","n2196831352","n2196831353","n2196831354","n2196831349"]},"w17965845":{"id":"w17965845","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971474","n185971475","n185963396"]},"w209470307":{"id":"w209470307","tags":{"area":"yes","building":"yes"},"nodes":["n2196831355","n2196831356","n2196831357","n2196831358","n2196831359","n2196831360","n2196831361","n2196831362","n2196831363","n2196831364","n2196831355"]},"w17968192":{"id":"w17968192","tags":{"highway":"residential","name":"Washington St"},"nodes":["n185980373","n185983238","n185988027","n185961369","n185970222","n185965108","n185974699","n185986812","n185960682"]},"w17967603":{"id":"w17967603","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185985914","n185988027","n185983137","n185973866","n185988028","n185988030","n185988032"]},"w209470305":{"id":"w209470305","tags":{"area":"yes","building":"yes"},"nodes":["n2196831342","n2196831343","n2196831344","n2196831345","n2196831346","n2196831347","n2196831348","n2196831342"]},"w17967092":{"id":"w17967092","tags":{"highway":"residential","name":"Wood St"},"nodes":["n185983133","n185966342","n185971474","n185971482","n185980374","n185983135","n185983137","n185961371","n185970224","n185965110","n185983139","n185983140","n185983141","n185983143","n185983144","n185983145","n185983146","n185978800"]},"w17967107":{"id":"w17967107","tags":{"highway":"residential","name":"4th Ave"},"nodes":["n185983236","n185983135","n185983238","n185955019","n185947349","n185947378","n185947474","n185947359","n185948972"]},"n354030330":{"id":"n354030330","loc":[-85.6297222,41.9444444],"tags":{"leisure":"park","name":"Scouter Park"}},"n185966296":{"id":"n185966296","loc":[-85.629998,41.944078]},"n185966298":{"id":"n185966298","loc":[-85.629972,41.943927]},"n185966300":{"id":"n185966300","loc":[-85.629948,41.943783]},"n185980391":{"id":"n185980391","loc":[-85.6322992,41.9442766]},"n185980393":{"id":"n185980393","loc":[-85.6324925,41.9442136]},"n185980389":{"id":"n185980389","loc":[-85.6320272,41.9443281]},"n185980388":{"id":"n185980388","loc":[-85.6315778,41.9443959]},"n354031320":{"id":"n354031320","loc":[-85.6280556,41.9447222],"tags":{"amenity":"place_of_worship","name":"Riverside Church","religion":"christian"}},"n185987309":{"id":"n185987309","loc":[-85.6286497,41.9453531]},"n185987311":{"id":"n185987311","loc":[-85.6285942,41.9454805]},"n185988034":{"id":"n185988034","loc":[-85.6285815,41.9471692]},"n185988896":{"id":"n185988896","loc":[-85.6318433,41.9437929]},"n185977764":{"id":"n185977764","loc":[-85.6322988,41.943472]},"n1819848852":{"id":"n1819848852","loc":[-85.6315188,41.9448808]},"n1819848912":{"id":"n1819848912","loc":[-85.6284289,41.9472189]},"n1819848925":{"id":"n1819848925","loc":[-85.6314501,41.9451617]},"n1819848949":{"id":"n1819848949","loc":[-85.6309394,41.9455192]},"n1819848951":{"id":"n1819848951","loc":[-85.6290297,41.9457187]},"n1819848963":{"id":"n1819848963","loc":[-85.630521,41.9455591]},"n1819848981":{"id":"n1819848981","loc":[-85.6292936,41.9455846]},"n1819848989":{"id":"n1819848989","loc":[-85.6298451,41.9455431]},"n1819848998":{"id":"n1819848998","loc":[-85.6314973,41.9446254]},"n1819849018":{"id":"n1819849018","loc":[-85.6302807,41.9455527]},"n1819849043":{"id":"n1819849043","loc":[-85.6285533,41.9469731]},"n1819849087":{"id":"n1819849087","loc":[-85.6314501,41.9453532]},"n1819849090":{"id":"n1819849090","loc":[-85.628843,41.9461033]},"n1819849109":{"id":"n1819849109","loc":[-85.6311926,41.9454729]},"n1819849116":{"id":"n1819849116","loc":[-85.6288967,41.9459437]},"n1819849177":{"id":"n1819849177","loc":[-85.6287894,41.9464544]},"n1819858529":{"id":"n1819858529","loc":[-85.6325485,41.9445625]},"n2189112797":{"id":"n2189112797","loc":[-85.6275271,41.944555]},"n2189112798":{"id":"n2189112798","loc":[-85.6275196,41.9437258]},"n2189112799":{"id":"n2189112799","loc":[-85.6278937,41.943723]},"n2189112800":{"id":"n2189112800","loc":[-85.6278969,41.9439191]},"n2189112801":{"id":"n2189112801","loc":[-85.6279907,41.9439345]},"n2189112802":{"id":"n2189112802","loc":[-85.6280817,41.9439663]},"n2189112803":{"id":"n2189112803","loc":[-85.6281768,41.9440145]},"n2189112804":{"id":"n2189112804","loc":[-85.6281933,41.9440483]},"n2189112805":{"id":"n2189112805","loc":[-85.6281671,41.9440535]},"n2189112806":{"id":"n2189112806","loc":[-85.6281933,41.9440935]},"n2189112807":{"id":"n2189112807","loc":[-85.6282126,41.9441437]},"n2189112808":{"id":"n2189112808","loc":[-85.628214,41.9441991]},"n2189112809":{"id":"n2189112809","loc":[-85.6283298,41.944196]},"n2189112810":{"id":"n2189112810","loc":[-85.6283285,41.9442616]},"n2189112811":{"id":"n2189112811","loc":[-85.6281727,41.9442616]},"n2189112812":{"id":"n2189112812","loc":[-85.6281713,41.9442934]},"n2189112813":{"id":"n2189112813","loc":[-85.6280386,41.9442963]},"n2189112814":{"id":"n2189112814","loc":[-85.6280405,41.9443292]},"n2189112815":{"id":"n2189112815","loc":[-85.627829,41.9443349]},"n2189112816":{"id":"n2189112816","loc":[-85.6278347,41.9445495]},"n2189153271":{"id":"n2189153271","loc":[-85.6321053,41.9460342]},"n2189153272":{"id":"n2189153272","loc":[-85.632278,41.9457841]},"n2189153273":{"id":"n2189153273","loc":[-85.632823,41.9459936]},"n2189153274":{"id":"n2189153274","loc":[-85.6326845,41.9461963]},"n2189153275":{"id":"n2189153275","loc":[-85.6325664,41.9461507]},"n2189153276":{"id":"n2189153276","loc":[-85.6325323,41.946198]},"n2189153278":{"id":"n2189153278","loc":[-85.6321916,41.9459733]},"n2189153279":{"id":"n2189153279","loc":[-85.6322598,41.9458703]},"n2189153280":{"id":"n2189153280","loc":[-85.6327208,41.9460358]},"n2189153281":{"id":"n2189153281","loc":[-85.6326413,41.9461422]},"n185959079":{"id":"n185959079","loc":[-85.6293702,41.9474668]},"n185966301":{"id":"n185966301","loc":[-85.629692,41.943136]},"n185966304":{"id":"n185966304","loc":[-85.629565,41.942916]},"n185966308":{"id":"n185966308","loc":[-85.629501,41.942751]},"n185966315":{"id":"n185966315","loc":[-85.629472,41.942578]},"n185966319":{"id":"n185966319","loc":[-85.629444,41.942414]},"n185966321":{"id":"n185966321","loc":[-85.629391,41.94205]},"n185966323":{"id":"n185966323","loc":[-85.629369,41.941858]},"n185966327":{"id":"n185966327","loc":[-85.629297,41.941604]},"n185966331":{"id":"n185966331","loc":[-85.629233,41.941549]},"n185966336":{"id":"n185966336","loc":[-85.628504,41.941364]},"n185966338":{"id":"n185966338","loc":[-85.628275,41.941303]},"n185966340":{"id":"n185966340","loc":[-85.6269038,41.9410983]},"n185973867":{"id":"n185973867","loc":[-85.626843,41.947333]},"n185977762":{"id":"n185977762","loc":[-85.6318441,41.9429453]},"n1819848853":{"id":"n1819848853","loc":[-85.625854,41.9492218]},"n1819848861":{"id":"n1819848861","loc":[-85.6251459,41.9552376]},"n1819848874":{"id":"n1819848874","loc":[-85.6267445,41.9482961]},"n1819848882":{"id":"n1819848882","loc":[-85.6257209,41.9552396]},"n1819848883":{"id":"n1819848883","loc":[-85.624706,41.9523173]},"n1819848907":{"id":"n1819848907","loc":[-85.62609,41.9561471]},"n1819848908":{"id":"n1819848908","loc":[-85.6244013,41.9549284]},"n1819848911":{"id":"n1819848911","loc":[-85.6265578,41.9553672]},"n1819848923":{"id":"n1819848923","loc":[-85.6246802,41.9550959]},"n1819848936":{"id":"n1819848936","loc":[-85.6241588,41.9539291]},"n1819848940":{"id":"n1819848940","loc":[-85.62506,41.9511129]},"n1819848944":{"id":"n1819848944","loc":[-85.624942,41.9515912]},"n1819848950":{"id":"n1819848950","loc":[-85.6273989,41.9475461]},"n1819848957":{"id":"n1819848957","loc":[-85.627695,41.947404]},"n1819849009":{"id":"n1819849009","loc":[-85.6259248,41.94896]},"n1819849037":{"id":"n1819849037","loc":[-85.6257252,41.9502112]},"n1819849061":{"id":"n1819849061","loc":[-85.6270084,41.9479626]},"n1819849073":{"id":"n1819849073","loc":[-85.6243734,41.9534583]},"n1819849091":{"id":"n1819849091","loc":[-85.6241373,41.9543918]},"n1819849130":{"id":"n1819849130","loc":[-85.6282572,41.9473067]},"n1819849143":{"id":"n1819849143","loc":[-85.625281,41.9506596]},"n1819849153":{"id":"n1819849153","loc":[-85.6258647,41.9498043]},"n1819849168":{"id":"n1819849168","loc":[-85.6265084,41.9559317]},"n1819849173":{"id":"n1819849173","loc":[-85.6263325,41.9552156]},"n1819849175":{"id":"n1819849175","loc":[-85.6266372,41.9556764]},"n1819849178":{"id":"n1819849178","loc":[-85.6242232,41.9545993]},"n1819849181":{"id":"n1819849181","loc":[-85.6262187,41.9486712]},"n1819849188":{"id":"n1819849188","loc":[-85.6245558,41.9530434]},"n1819849190":{"id":"n1819849190","loc":[-85.6255982,41.9563017]},"n2168544738":{"id":"n2168544738","loc":[-85.6245707,41.9529711]},"w208643145":{"id":"w208643145","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153271","n2189153272","n2189153273","n2189153274","n2189153275","n2189153276","n2189153271"]},"w17967561":{"id":"w17967561","tags":{"highway":"residential","name":"Garden St"},"nodes":["n185980378","n185987309","n185987311","n185983236","n185973866"]},"w134150802":{"id":"w134150802","tags":{"bridge":"yes","highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980386","n185980388"]},"w208639462":{"id":"w208639462","tags":{"area":"yes","building":"yes"},"nodes":["n2189112797","n2189112798","n2189112799","n2189112800","n2189112801","n2189112802","n2189112803","n2189112804","n2189112805","n2189112806","n2189112807","n2189112808","n2189112809","n2189112810","n2189112811","n2189112812","n2189112813","n2189112814","n2189112815","n2189112816","n2189112797"]},"w134150830":{"id":"w134150830","tags":{"bridge":"yes","highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977762","n185977764"]},"w134150801":{"id":"w134150801","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977764","n185964982"]},"w208643146":{"id":"w208643146","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153277","n2189153281","n2189153278","n2189153279","n2189153280","n2189153281"]},"w17966061":{"id":"w17966061","tags":{"highway":"residential","name":"John Glenn Ct"},"nodes":["n185973866","n185973867"]},"w134150772":{"id":"w134150772","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185988034","n185959079","n185988036","n185978967"]},"w134150836":{"id":"w134150836","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980388","n1819858524","n185980389","n185980391","n185980393","n185964982"]},"w17967734":{"id":"w17967734","tags":{"highway":"residential","name":"Water Street"},"nodes":["n185988896","n185980391","n1819858529"]},"w17965305":{"id":"w17965305","tags":{"highway":"residential","name":"River Dr"},"nodes":["n185966295","n185966296","n185966298","n185966300","n185966301","n185966304","n185966308","n185966315","n185966319","n185966321","n185966323","n185966327","n185966331","n185966336","n185966338","n185963392","n185966340","n185966342"]},"w134150826":{"id":"w134150826","tags":{"bridge":"yes","highway":"residential","name":"5th Ave"},"nodes":["n185988032","n185988034"]},"w170848330":{"id":"w170848330","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1819849190","n1819848907","n1819849168","n1819849175","n1819848911","n1819849173","n1819848882","n1819848861","n1819848923","n1819848908","n1819849178","n1819849091","n1819848936","n1819849073","n1819849188","n2168544738","n1819848883","n1819848944","n1819848940","n1819849143","n1819849037","n1819849153","n1819848853","n1819849009","n1819849181","n1819848874","n1819849061","n1819848950","n1819848957","n1819849130","n1819848912","n1819849043","n1819849177","n1819849090","n1819849116","n1819848951","n1819848981","n1819848989","n1819849018","n1819848963","n1819848949","n1819849109","n1819849087","n1819848925","n1819848852","n1819848998","n1819849057"]},"r270264":{"id":"r270264","tags":{"network":"US:MI","ref":"86","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-86_%28Michigan_highway%29"},"members":[{"id":"w17737723","type":"way","role":""},{"id":"w17735949","type":"way","role":""},{"id":"w17740404","type":"way","role":""},{"id":"w17966273","type":"way","role":""},{"id":"w17964745","type":"way","role":""},{"id":"w151538068","type":"way","role":""},{"id":"w151538067","type":"way","role":""},{"id":"w17964960","type":"way","role":""},{"id":"w17966099","type":"way","role":""},{"id":"w17968009","type":"way","role":""},{"id":"w41259499","type":"way","role":""},{"id":"w151540401","type":"way","role":""},{"id":"w151540418","type":"way","role":""},{"id":"w17967997","type":"way","role":""},{"id":"w17966029","type":"way","role":""},{"id":"w17964801","type":"way","role":""},{"id":"w41259496","type":"way","role":""},{"id":"w151540399","type":"way","role":""},{"id":"w17968004","type":"way","role":""},{"id":"w17966462","type":"way","role":""},{"id":"w134150830","type":"way","role":""},{"id":"w134150801","type":"way","role":""},{"id":"w17732295","type":"way","role":""}]},"n185980093":{"id":"n185980093","loc":[-85.6271414,41.9407274]},"n185964330":{"id":"n185964330","loc":[-85.6235688,41.9399111]},"n185964328":{"id":"n185964328","loc":[-85.6235609,41.9391301]},"n185958034":{"id":"n185958034","loc":[-85.627102,41.939125]},"n185964331":{"id":"n185964331","loc":[-85.623571,41.940124]},"n185964329":{"id":"n185964329","loc":[-85.623562,41.9392411]},"n185972756":{"id":"n185972756","loc":[-85.623802,41.939102]},"n185972757":{"id":"n185972757","loc":[-85.623584,41.93913]},"n185975325":{"id":"n185975325","loc":[-85.624835,41.939318]},"n185975326":{"id":"n185975326","loc":[-85.624811,41.939435]},"n185975327":{"id":"n185975327","loc":[-85.624635,41.939703]},"n185975328":{"id":"n185975328","loc":[-85.624366,41.940055]},"n185975330":{"id":"n185975330","loc":[-85.624287,41.940113]},"n185975332":{"id":"n185975332","loc":[-85.624215,41.940134]},"n185980088":{"id":"n185980088","loc":[-85.627127,41.940086]},"n185988943":{"id":"n185988943","loc":[-85.622643,41.940128]},"n185988961":{"id":"n185988961","loc":[-85.627263,41.940082]},"n185990192":{"id":"n185990192","loc":[-85.622933,41.939224]},"n185990194":{"id":"n185990194","loc":[-85.621976,41.939203]},"n185991378":{"id":"n185991378","loc":[-85.622643,41.940635]},"n1475283999":{"id":"n1475283999","loc":[-85.6271165,41.9408429]},"n185980090":{"id":"n185980090","loc":[-85.6271315,41.9402001]},"n185958036":{"id":"n185958036","loc":[-85.6248366,41.9391615]},"n1819800188":{"id":"n1819800188","loc":[-85.6246947,41.9401644]},"n1819800199":{"id":"n1819800199","loc":[-85.6233686,41.9430896]},"n1819800204":{"id":"n1819800204","loc":[-85.6223236,41.9408587]},"n1819800213":{"id":"n1819800213","loc":[-85.6247526,41.9414138]},"n1819800216":{"id":"n1819800216","loc":[-85.6230961,41.9407151]},"n1819800218":{"id":"n1819800218","loc":[-85.621991,41.9429336]},"n1819800221":{"id":"n1819800221","loc":[-85.6246088,41.9424708]},"n1819800227":{"id":"n1819800227","loc":[-85.6241368,41.9403081]},"n1819800230":{"id":"n1819800230","loc":[-85.6226776,41.9431012]},"n1819800231":{"id":"n1819800231","loc":[-85.6243728,41.9401644]},"n1819800232":{"id":"n1819800232","loc":[-85.6249629,41.9408907]},"n1819800248":{"id":"n1819800248","loc":[-85.6238685,41.9405555]},"n1819800266":{"id":"n1819800266","loc":[-85.6246882,41.9418367]},"n1819800271":{"id":"n1819800271","loc":[-85.62492,41.9413695]},"n1819800294":{"id":"n1819800294","loc":[-85.6243556,41.9427465]},"n1819800304":{"id":"n1819800304","loc":[-85.6251453,41.94117]},"n1819800325":{"id":"n1819800325","loc":[-85.6248234,41.9405714]},"n1819800362":{"id":"n1819800362","loc":[-85.6239544,41.9429416]},"n1819800368":{"id":"n1819800368","loc":[-85.6243406,41.9402283]},"n1819800375":{"id":"n1819800375","loc":[-85.6226562,41.940755]},"n1819800377":{"id":"n1819800377","loc":[-85.6232033,41.9406512]},"n185945133":{"id":"n185945133","loc":[-85.623501,41.933232]},"n185945135":{"id":"n185945135","loc":[-85.624776,41.933205]},"n185945395":{"id":"n185945395","loc":[-85.624741,41.93019]},"n185952239":{"id":"n185952239","loc":[-85.615166,41.9382]},"n185954490":{"id":"n185954490","loc":[-85.624721,41.929278]},"n185957831":{"id":"n185957831","loc":[-85.625041,41.938662]},"n185958030":{"id":"n185958030","loc":[-85.629033,41.93913]},"n185958032":{"id":"n185958032","loc":[-85.628429,41.939143]},"n185958498":{"id":"n185958498","loc":[-85.621605,41.940143]},"n185961186":{"id":"n185961186","loc":[-85.624792,41.935214]},"n185963099":{"id":"n185963099","loc":[-85.6204461,41.9401485]},"n185963698":{"id":"n185963698","loc":[-85.6297342,41.9400783]},"n185964320":{"id":"n185964320","loc":[-85.623511,41.934216]},"n185964322":{"id":"n185964322","loc":[-85.6235312,41.9362084]},"n185964324":{"id":"n185964324","loc":[-85.6235488,41.9371726]},"n185964326":{"id":"n185964326","loc":[-85.6235512,41.9381718]},"n185967077":{"id":"n185967077","loc":[-85.617359,41.940161]},"n185967634":{"id":"n185967634","loc":[-85.6248039,41.9362012]},"n185970833":{"id":"n185970833","loc":[-85.6248019,41.9381684]},"n185972752":{"id":"n185972752","loc":[-85.624582,41.938848]},"n185972754":{"id":"n185972754","loc":[-85.6242,41.939008]},"n185973251":{"id":"n185973251","loc":[-85.602727,41.936012]},"n185974509":{"id":"n185974509","loc":[-85.62478,41.93217]},"n185975315":{"id":"n185975315","loc":[-85.624703,41.925597]},"n185975316":{"id":"n185975316","loc":[-85.624716,41.927359]},"n185975317":{"id":"n185975317","loc":[-85.62475,41.93119]},"n185975318":{"id":"n185975318","loc":[-85.624782,41.934218]},"n185975320":{"id":"n185975320","loc":[-85.6247949,41.9371708]},"n185977754":{"id":"n185977754","loc":[-85.6276,41.937412]},"n185980075":{"id":"n185980075","loc":[-85.627451,41.937549]},"n185980077":{"id":"n185980077","loc":[-85.627375,41.937618]},"n185980078":{"id":"n185980078","loc":[-85.627278,41.937728]},"n185980079":{"id":"n185980079","loc":[-85.627199,41.937842]},"n185980081":{"id":"n185980081","loc":[-85.627141,41.937981]},"n185980083":{"id":"n185980083","loc":[-85.627109,41.938153]},"n185980085":{"id":"n185980085","loc":[-85.627101,41.938699]},"n185981173":{"id":"n185981173","loc":[-85.61433,41.940167]},"n185987021":{"id":"n185987021","loc":[-85.628311,41.942261]},"n185988963":{"id":"n185988963","loc":[-85.628439,41.940086]},"n185990195":{"id":"n185990195","loc":[-85.621225,41.939143]},"n185990196":{"id":"n185990196","loc":[-85.620576,41.939033]},"n185990198":{"id":"n185990198","loc":[-85.619081,41.938804]},"n185990200":{"id":"n185990200","loc":[-85.617593,41.938552]},"n185990202":{"id":"n185990202","loc":[-85.617372,41.938535]},"n185990204":{"id":"n185990204","loc":[-85.616087,41.93832]},"n185990206":{"id":"n185990206","loc":[-85.615754,41.938289]},"n185990209":{"id":"n185990209","loc":[-85.615438,41.938251]},"n185990211":{"id":"n185990211","loc":[-85.613469,41.937867]},"n185990212":{"id":"n185990212","loc":[-85.610172,41.937298]},"n185990213":{"id":"n185990213","loc":[-85.605537,41.936497]},"n185990214":{"id":"n185990214","loc":[-85.604014,41.936234]},"n1819800180":{"id":"n1819800180","loc":[-85.588775,41.9455032]},"n1819800181":{"id":"n1819800181","loc":[-85.6074212,41.9408827]},"n1819800182":{"id":"n1819800182","loc":[-85.6131397,41.9427022]},"n1819800183":{"id":"n1819800183","loc":[-85.6171523,41.9416807]},"n1819800184":{"id":"n1819800184","loc":[-85.602465,41.9397415]},"n1819800185":{"id":"n1819800185","loc":[-85.6109296,41.9410583]},"n1819800186":{"id":"n1819800186","loc":[-85.6165729,41.9418004]},"n1819800189":{"id":"n1819800189","loc":[-85.5866293,41.9458224]},"n1819800191":{"id":"n1819800191","loc":[-85.5853311,41.9466603]},"n1819800201":{"id":"n1819800201","loc":[-85.6101142,41.9433406]},"n1819800202":{"id":"n1819800202","loc":[-85.600963,41.9428618]},"n1819800206":{"id":"n1819800206","loc":[-85.6154357,41.9427501]},"n1819800207":{"id":"n1819800207","loc":[-85.6040309,41.9414094]},"n1819800209":{"id":"n1819800209","loc":[-85.6113694,41.943189]},"n1819800211":{"id":"n1819800211","loc":[-85.618032,41.9416408]},"n1819800214":{"id":"n1819800214","loc":[-85.5959419,41.9402602]},"n1819800219":{"id":"n1819800219","loc":[-85.5972117,41.9420043]},"n1819800223":{"id":"n1819800223","loc":[-85.6117171,41.9430019]},"n1819800224":{"id":"n1819800224","loc":[-85.5977873,41.9395579]},"n1819800226":{"id":"n1819800226","loc":[-85.5917362,41.9432209]},"n1819800228":{"id":"n1819800228","loc":[-85.6055759,41.9419122]},"n1819800229":{"id":"n1819800229","loc":[-85.6203395,41.9425595]},"n1819800233":{"id":"n1819800233","loc":[-85.6107579,41.9433007]},"n1819800234":{"id":"n1819800234","loc":[-85.6039773,41.9412498]},"n1819800235":{"id":"n1819800235","loc":[-85.6000977,41.9412861]},"n1819800236":{"id":"n1819800236","loc":[-85.6026689,41.9407231]},"n1819800237":{"id":"n1819800237","loc":[-85.615161,41.9428662]},"n1819800238":{"id":"n1819800238","loc":[-85.5878953,41.9454314]},"n1819800239":{"id":"n1819800239","loc":[-85.6035267,41.941569]},"n1819800240":{"id":"n1819800240","loc":[-85.5929738,41.9450208]},"n1819800241":{"id":"n1819800241","loc":[-85.6186329,41.9416488]},"n1819800242":{"id":"n1819800242","loc":[-85.5881136,41.9483963]},"n1819800243":{"id":"n1819800243","loc":[-85.5909208,41.9466922]},"n1819800244":{"id":"n1819800244","loc":[-85.5997721,41.9394941]},"n1819800245":{"id":"n1819800245","loc":[-85.6202064,41.9425712]},"n1819800246":{"id":"n1819800246","loc":[-85.591071,41.9448808]},"n1819800247":{"id":"n1819800247","loc":[-85.5866078,41.9490622]},"n1819800250":{"id":"n1819800250","loc":[-85.602383,41.9420841]},"n1819800251":{"id":"n1819800251","loc":[-85.5957418,41.9426906]},"n1819800255":{"id":"n1819800255","loc":[-85.6157039,41.9416727]},"n1819800256":{"id":"n1819800256","loc":[-85.6080328,41.9410982]},"n1819800258":{"id":"n1819800258","loc":[-85.6192551,41.9414892]},"n1819800260":{"id":"n1819800260","loc":[-85.6104253,41.94117]},"n1819800261":{"id":"n1819800261","loc":[-85.6204503,41.9425709]},"n1819800263":{"id":"n1819800263","loc":[-85.5872194,41.9455431]},"n1819800264":{"id":"n1819800264","loc":[-85.616176,41.9418244]},"n1819800268":{"id":"n1819800268","loc":[-85.6120883,41.9426703]},"n1819800269":{"id":"n1819800269","loc":[-85.5894547,41.9474946]},"n1819800272":{"id":"n1819800272","loc":[-85.6209181,41.9425027]},"n1819800274":{"id":"n1819800274","loc":[-85.6122814,41.9412817]},"n1819800276":{"id":"n1819800276","loc":[-85.5895153,41.9452798]},"n1819800277":{"id":"n1819800277","loc":[-85.5884317,41.9455272]},"n1819800279":{"id":"n1819800279","loc":[-85.5884103,41.9480966]},"n1819800287":{"id":"n1819800287","loc":[-85.5904917,41.9453915]},"n1819800288":{"id":"n1819800288","loc":[-85.6212292,41.9412977]},"n1819800289":{"id":"n1819800289","loc":[-85.5954377,41.9406832]},"n1819800290":{"id":"n1819800290","loc":[-85.593721,41.9420957]},"n1819800291":{"id":"n1819800291","loc":[-85.6162832,41.9427102]},"n1819800292":{"id":"n1819800292","loc":[-85.605018,41.9401804]},"n1819800293":{"id":"n1819800293","loc":[-85.6086443,41.941146]},"n1819800296":{"id":"n1819800296","loc":[-85.6204675,41.9413775]},"n1819800297":{"id":"n1819800297","loc":[-85.612496,41.9424947]},"n1819800299":{"id":"n1819800299","loc":[-85.6065629,41.9423431]},"n1819800301":{"id":"n1819800301","loc":[-85.6036125,41.9398452]},"n1819800303":{"id":"n1819800303","loc":[-85.6114767,41.94117]},"n1819800306":{"id":"n1819800306","loc":[-85.592616,41.9428139]},"n1819800308":{"id":"n1819800308","loc":[-85.6023041,41.9419521]},"n1819800310":{"id":"n1819800310","loc":[-85.6218944,41.9411061]},"n1819800311":{"id":"n1819800311","loc":[-85.6097816,41.941162]},"n1819800312":{"id":"n1819800312","loc":[-85.5922549,41.9457869]},"n1819800313":{"id":"n1819800313","loc":[-85.5986027,41.9417206]},"n1819800314":{"id":"n1819800314","loc":[-85.5918687,41.946138]},"n1819800315":{"id":"n1819800315","loc":[-85.5872875,41.948883]},"n1819800316":{"id":"n1819800316","loc":[-85.594272,41.9436642]},"n1819800317":{"id":"n1819800317","loc":[-85.6176351,41.941577]},"n1819800318":{"id":"n1819800318","loc":[-85.6137834,41.9430853]},"n1819800319":{"id":"n1819800319","loc":[-85.6195383,41.942622],"tags":{"leisure":"slipway"}},"n1819800320":{"id":"n1819800320","loc":[-85.5971006,41.9398053]},"n1819800321":{"id":"n1819800321","loc":[-85.601714,41.9406752]},"n1819800322":{"id":"n1819800322","loc":[-85.5908028,41.9453117]},"n1819800323":{"id":"n1819800323","loc":[-85.6062732,41.9404597]},"n1819800324":{"id":"n1819800324","loc":[-85.62124,41.9425905]},"n1819800327":{"id":"n1819800327","loc":[-85.6008664,41.942766]},"n1819800328":{"id":"n1819800328","loc":[-85.6179355,41.9428538]},"n1819800330":{"id":"n1819800330","loc":[-85.6045566,41.9415131]},"n1819800331":{"id":"n1819800331","loc":[-85.5944935,41.9414653]},"n1819800333":{"id":"n1819800333","loc":[-85.6088911,41.943181]},"n1819800334":{"id":"n1819800334","loc":[-85.5946367,41.943369]},"n1819800336":{"id":"n1819800336","loc":[-85.6150494,41.9429656]},"n1819800343":{"id":"n1819800343","loc":[-85.6096099,41.9433326]},"n1819800345":{"id":"n1819800345","loc":[-85.5915216,41.9435401]},"n1819800347":{"id":"n1819800347","loc":[-85.607786,41.9428698]},"n1819800349":{"id":"n1819800349","loc":[-85.6187616,41.9426623]},"n1819800350":{"id":"n1819800350","loc":[-85.6012527,41.9426064]},"n1819800352":{"id":"n1819800352","loc":[-85.6214867,41.9428379]},"n1819800354":{"id":"n1819800354","loc":[-85.61338,41.94293]},"n1819800355":{"id":"n1819800355","loc":[-85.5923156,41.9428139]},"n1819800357":{"id":"n1819800357","loc":[-85.5901591,41.9453197]},"n1819800359":{"id":"n1819800359","loc":[-85.6033979,41.9408827]},"n1819800360":{"id":"n1819800360","loc":[-85.6186543,41.9414653]},"n1819800363":{"id":"n1819800363","loc":[-85.6128607,41.9425665]},"n1819800365":{"id":"n1819800365","loc":[-85.614234,41.9412977]},"n1819800367":{"id":"n1819800367","loc":[-85.6089662,41.9410902]},"n1819800369":{"id":"n1819800369","loc":[-85.6197379,41.9413695]},"n1819800370":{"id":"n1819800370","loc":[-85.6037348,41.941733]},"n1819800371":{"id":"n1819800371","loc":[-85.5993467,41.9415654]},"n1819800372":{"id":"n1819800372","loc":[-85.598077,41.94196]},"n1819800373":{"id":"n1819800373","loc":[-85.5984203,41.9394781]},"n1819800374":{"id":"n1819800374","loc":[-85.6013315,41.9427066]},"n1819800376":{"id":"n1819800376","loc":[-85.5934673,41.944167]},"n1819800378":{"id":"n1819800378","loc":[-85.6011062,41.9407753]},"n1819800379":{"id":"n1819800379","loc":[-85.6150602,41.9415131]},"n1819800380":{"id":"n1819800380","loc":[-85.6132148,41.9412338]},"n1819800381":{"id":"n1819800381","loc":[-85.5889038,41.9453835]},"n2139966621":{"id":"n2139966621","loc":[-85.6198719,41.9426184]},"n2139966622":{"id":"n2139966622","loc":[-85.6197551,41.9426123]},"n2139966623":{"id":"n2139966623","loc":[-85.6196467,41.9426279]},"n2139966624":{"id":"n2139966624","loc":[-85.6191519,41.9426221]},"n2139966625":{"id":"n2139966625","loc":[-85.6194153,41.9426256]},"n2139966626":{"id":"n2139966626","loc":[-85.6200497,41.9425812]},"n2139966629":{"id":"n2139966629","loc":[-85.6192123,41.9426229]},"n2203933101":{"id":"n2203933101","loc":[-85.6030009,41.9360592]},"w17967539":{"id":"w17967539","tags":{"highway":"residential","name":"1st Ave"},"nodes":["n185965099","n185963395","n185987021"]},"w17967751":{"id":"w17967751","tags":{"highway":"residential","name":"River St"},"nodes":["n185980088","n185988961","n185988963","n185963698"]},"w17965088":{"id":"w17965088","tags":{"highway":"residential","name":"9th St"},"nodes":["n185945133","n185964320","n185964322","n185964324","n185964326","n185964328","n185964329","n185964330","n185964331"]},"w17964467":{"id":"w17964467","tags":{"highway":"residential","name":"Mechanic St"},"nodes":["n185958030","n185958032","n185958034","n185958036"]},"w134150842":{"id":"w134150842","tags":{"bridge":"yes","highway":"residential","name":"6th St"},"nodes":["n185980090","n185980093"]},"w17966740":{"id":"w17966740","tags":{"highway":"residential","name":"6th St"},"nodes":["n185977754","n185980075","n185980077","n185980078","n185980079","n185980081","n185980083","n185980085","n185958034","n185980088","n185980090"]},"w170844765":{"id":"w170844765","tags":{"waterway":"dam"},"nodes":["n1819800304","n1819800232","n1819800325","n1819800188"]},"w17967745":{"id":"w17967745","tags":{"highway":"residential","name":"River St"},"nodes":["n185981173","n185967077","n185963099","n185958498","n185988943","n185964331","n185975332"]},"w17968113":{"id":"w17968113","tags":{"highway":"residential","name":"Green St"},"nodes":["n185988943","n185991378"]},"w134150833":{"id":"w134150833","tags":{"highway":"residential","name":"6th St"},"nodes":["n185980093","n1475283999","n185963392"]},"w17967935":{"id":"w17967935","tags":{"name":"Michigan Central Railroad","railway":"abandoned"},"nodes":["n185972757","n185990192","n185990194","n185990195","n185990196","n185990198","n185990200","n185990202","n185990204","n185990206","n185990209","n185952239","n185990211","n185990212","n185990213","n185990214","n2203933101","n185973251"]},"w17965993":{"id":"w17965993","tags":{"name":"Conrail Railroad","railway":"abandoned"},"nodes":["n185957831","n185972752","n185972754","n185972756","n185972757"]},"w17966211":{"id":"w17966211","tags":{"highway":"residential","name":"8th St"},"nodes":["n185975315","n185975316","n185954490","n185945395","n185975317","n185974509","n185945135","n185975318","n185961186","n185967634","n185975320","n185970833","n185958036","n185975325","n185975326","n185975327","n185975328","n185975330","n185975332"]},"w170844766":{"id":"w170844766","tags":{"waterway":"riverbank"},"nodes":["n1819800229","n1819800245","n2139966626","n2139966621","n2139966622","n2139966623","n1819800319","n2139966625","n2139966629","n2139966624","n1819800349","n1819800328","n1819800291","n1819800206","n1819800237","n1819800336","n1819800318","n1819800354","n1819800182","n1819800363","n1819800297","n1819800268","n1819800223","n1819800209","n1819800233","n1819800201","n1819800343","n1819800333","n1819800347","n1819800299","n1819800228","n1819800330","n1819800370","n1819800250","n1819800374","n1819800202","n1819800327","n1819800350","n1819800308","n1819800239","n1819800207","n1819800234","n1819800359","n1819800236","n1819800321","n1819800378","n1819800235","n1819800371","n1819800313","n1819800372","n1819800219","n1819800251","n1819800334","n1819800316","n1819800376","n1819800240","n1819800312","n1819800314","n1819800243","n1819800269","n1819800279","n1819800242","n1819800315","n1819800247","n1819800191","n1819800189","n1819800263","n1819800238","n1819800277","n1819800180","n1819800381","n1819800276","n1819800357","n1819800287","n1819800322","n1819800246","n1819800345","n1819800226","n1819800355","n1819800306","n1819800290","n1819800331","n1819800289","n1819800214","n1819800320","n1819800224","n1819800373","n1819800244","n1819800184","n1819800301","n1819800292","n1819800323","n1819800181","n1819800256","n1819800293","n1819800367","n1819800311","n1819800260","n1819800185","n1819800303","n1819800274","n1819800380","n1819800365","n1819800379","n1819800255","n1819800264","n1819800186","n1819800183","n1819800317","n1819800211","n1819800241","n1819800360","n1819800258","n1819800369","n1819800296","n1819800288","n1819800310","n1819800204","n1819800375","n1819800216","n1819800377","n1819800248","n1819800227","n1819800368","n1819800231","n1819800188","n1819800325","n1819800232","n1819800304","n1819800271","n1819800213","n1819800266","n1819800221","n1819800294","n1819800362","n1819800199","n1819800230","n1819800218","n1819800352","n1819800324","n1819800272","n1819800261","n1819800229"]},"n1875654132":{"id":"n1875654132","loc":[-85.6297439,41.939808]},"n1475293263":{"id":"n1475293263","loc":[-85.6296235,41.939922]},"n185947850":{"id":"n185947850","loc":[-85.631594,41.942613]},"n185952745":{"id":"n185952745","loc":[-85.630986,41.941786]},"n185972907":{"id":"n185972907","loc":[-85.631797,41.9420055]},"n185972911":{"id":"n185972911","loc":[-85.6309723,41.9411623]},"n185972915":{"id":"n185972915","loc":[-85.6295971,41.939267]},"n1475293223":{"id":"n1475293223","loc":[-85.6313962,41.9416114],"tags":{"railway":"level_crossing"}},"n1475293231":{"id":"n1475293231","loc":[-85.6318779,41.9415447]},"n1475293241":{"id":"n1475293241","loc":[-85.6304613,41.9405499]},"n1475293246":{"id":"n1475293246","loc":[-85.6297512,41.9395393],"tags":{"railway":"level_crossing"}},"n1475293251":{"id":"n1475293251","loc":[-85.6316633,41.9415128]},"n2139982404":{"id":"n2139982404","loc":[-85.6313283,41.9413748]},"n2139982407":{"id":"n2139982407","loc":[-85.6325545,41.9417787]},"n2139982408":{"id":"n2139982408","loc":[-85.6324499,41.9417693]},"n2139982409":{"id":"n2139982409","loc":[-85.6324753,41.9416444]},"n2139982410":{"id":"n2139982410","loc":[-85.6325814,41.9416538]},"n2139982411":{"id":"n2139982411","loc":[-85.6319572,41.9413515]},"n2139982412":{"id":"n2139982412","loc":[-85.6322925,41.941139]},"n2139982413":{"id":"n2139982413","loc":[-85.6323153,41.941153]},"n2139982414":{"id":"n2139982414","loc":[-85.6323019,41.9412617]},"n2139982415":{"id":"n2139982415","loc":[-85.6323703,41.9412667]},"n2139982416":{"id":"n2139982416","loc":[-85.6323555,41.941538]},"n2139982417":{"id":"n2139982417","loc":[-85.6321343,41.9416777]},"n2139982418":{"id":"n2139982418","loc":[-85.6319425,41.9416866]},"n2139982419":{"id":"n2139982419","loc":[-85.6320303,41.9416941]},"n2139982420":{"id":"n2139982420","loc":[-85.6321665,41.9415554]},"n2139982421":{"id":"n2139982421","loc":[-85.632412,41.9414164]},"n2139982422":{"id":"n2139982422","loc":[-85.6324801,41.9413421]},"n2139982423":{"id":"n2139982423","loc":[-85.6325023,41.9412585]},"n2139982424":{"id":"n2139982424","loc":[-85.6324532,41.9411607]},"n2139982425":{"id":"n2139982425","loc":[-85.6323502,41.941103]},"n2139982426":{"id":"n2139982426","loc":[-85.6322362,41.9411183]},"n2139982427":{"id":"n2139982427","loc":[-85.6318941,41.9413551]},"n2139982428":{"id":"n2139982428","loc":[-85.6318592,41.9414105]},"n2139982429":{"id":"n2139982429","loc":[-85.6320111,41.9415866]},"n2139982430":{"id":"n2139982430","loc":[-85.632446,41.9413792]},"n2139982431":{"id":"n2139982431","loc":[-85.6325112,41.941416]},"n2139982432":{"id":"n2139982432","loc":[-85.6325449,41.9416345]},"n2139982433":{"id":"n2139982433","loc":[-85.6326122,41.94164]},"n2139982434":{"id":"n2139982434","loc":[-85.6325954,41.9421966]},"n2139982435":{"id":"n2139982435","loc":[-85.6325655,41.9422411]},"n2139982436":{"id":"n2139982436","loc":[-85.632515,41.9422564]},"n2139982437":{"id":"n2139982437","loc":[-85.6324495,41.94223]},"n2139982438":{"id":"n2139982438","loc":[-85.6324009,41.9421743]},"n2139982439":{"id":"n2139982439","loc":[-85.6323915,41.9421145]},"n2139982440":{"id":"n2139982440","loc":[-85.6320287,41.9418585]},"n2139982441":{"id":"n2139982441","loc":[-85.6318285,41.9416387]},"n1475293258":{"id":"n1475293258","loc":[-85.6318289,41.9415077]},"n2168544754":{"id":"n2168544754","loc":[-85.6312814,41.9431198]},"n2168544755":{"id":"n2168544755","loc":[-85.6314212,41.9430646]},"n2168544756":{"id":"n2168544756","loc":[-85.6313387,41.942949]},"n2168544757":{"id":"n2168544757","loc":[-85.6311989,41.9430041]},"n2168544758":{"id":"n2168544758","loc":[-85.6311024,41.9429313]},"n2168544759":{"id":"n2168544759","loc":[-85.6310087,41.9428087]},"n2168544760":{"id":"n2168544760","loc":[-85.6313831,41.9426504]},"n2168544761":{"id":"n2168544761","loc":[-85.6314768,41.9427729]},"n2168544762":{"id":"n2168544762","loc":[-85.6306376,41.942809]},"n2168544763":{"id":"n2168544763","loc":[-85.6307378,41.9429427]},"n2168544764":{"id":"n2168544764","loc":[-85.630841,41.9428998]},"n2168544765":{"id":"n2168544765","loc":[-85.6307408,41.9427662]},"n2168544766":{"id":"n2168544766","loc":[-85.6305404,41.9426029]},"n2168544767":{"id":"n2168544767","loc":[-85.6304976,41.9426194]},"n2168544768":{"id":"n2168544768","loc":[-85.6305673,41.9427184]},"n2168544769":{"id":"n2168544769","loc":[-85.6306164,41.9426984]},"n2168544770":{"id":"n2168544770","loc":[-85.6306418,41.9427302]},"n2168544771":{"id":"n2168544771","loc":[-85.6306861,41.9427137]},"n2168544772":{"id":"n2168544772","loc":[-85.6307146,41.9427537]},"n2168544773":{"id":"n2168544773","loc":[-85.6308999,41.9426807]},"n2168544774":{"id":"n2168544774","loc":[-85.6308429,41.9426053]},"n2168544775":{"id":"n2168544775","loc":[-85.6308999,41.9425806]},"n2168544776":{"id":"n2168544776","loc":[-85.6308318,41.9424875]},"n2168544777":{"id":"n2168544777","loc":[-85.6307732,41.9425087]},"n2168544778":{"id":"n2168544778","loc":[-85.6307178,41.9424357]},"n2168544779":{"id":"n2168544779","loc":[-85.630485,41.942524]},"n2189099387":{"id":"n2189099387","loc":[-85.631203,41.9393371]},"n2189099404":{"id":"n2189099404","loc":[-85.6301963,41.9391363]},"n2189099405":{"id":"n2189099405","loc":[-85.6304447,41.9391352]},"n2189099406":{"id":"n2189099406","loc":[-85.6304463,41.9393391]},"n2189099407":{"id":"n2189099407","loc":[-85.6308435,41.9393373]},"n2189099408":{"id":"n2189099408","loc":[-85.6308418,41.9391251]},"n2189099409":{"id":"n2189099409","loc":[-85.6310929,41.939124]},"n2189099410":{"id":"n2189099410","loc":[-85.6310946,41.9393376]},"n2189112720":{"id":"n2189112720","loc":[-85.6314677,41.9412327]},"n2189112721":{"id":"n2189112721","loc":[-85.6313337,41.9411397]},"n2189112722":{"id":"n2189112722","loc":[-85.6320521,41.9405678]},"n2189112723":{"id":"n2189112723","loc":[-85.6321899,41.9406633]},"n2189112724":{"id":"n2189112724","loc":[-85.6313229,41.9408344]},"n2189112725":{"id":"n2189112725","loc":[-85.6311223,41.9410018]},"n2189112726":{"id":"n2189112726","loc":[-85.6313205,41.9411333]},"n2189112727":{"id":"n2189112727","loc":[-85.6315211,41.9409659]},"n2189112728":{"id":"n2189112728","loc":[-85.6311035,41.9402529]},"n2189112729":{"id":"n2189112729","loc":[-85.631226,41.9402107]},"n2189112730":{"id":"n2189112730","loc":[-85.6315966,41.9408051]},"n2189112731":{"id":"n2189112731","loc":[-85.6314741,41.9408473]},"n2189112732":{"id":"n2189112732","loc":[-85.6318114,41.940534]},"n2189112733":{"id":"n2189112733","loc":[-85.631588,41.94061]},"n2189112734":{"id":"n2189112734","loc":[-85.6314379,41.940366]},"n2189112735":{"id":"n2189112735","loc":[-85.6316613,41.94029]},"n2189112736":{"id":"n2189112736","loc":[-85.6306214,41.9400415]},"n2189112737":{"id":"n2189112737","loc":[-85.6304362,41.9397728]},"n2189112738":{"id":"n2189112738","loc":[-85.6305899,41.9397142]},"n2189112739":{"id":"n2189112739","loc":[-85.6307751,41.9399828]},"n2189112740":{"id":"n2189112740","loc":[-85.6304695,41.9401673]},"n2189112741":{"id":"n2189112741","loc":[-85.6301298,41.9396855]},"n2189112742":{"id":"n2189112742","loc":[-85.6303016,41.9396184]},"n2189112743":{"id":"n2189112743","loc":[-85.6306413,41.9401003]},"n2189112744":{"id":"n2189112744","loc":[-85.6309656,41.9406189]},"n2189112745":{"id":"n2189112745","loc":[-85.6308738,41.940493]},"n2189112746":{"id":"n2189112746","loc":[-85.6309333,41.940469]},"n2189112747":{"id":"n2189112747","loc":[-85.6307634,41.9402358]},"n2189112748":{"id":"n2189112748","loc":[-85.6308798,41.9401889]},"n2189112749":{"id":"n2189112749","loc":[-85.6311416,41.940548]},"n2189112750":{"id":"n2189112750","loc":[-85.6309577,41.9408708]},"n2189112751":{"id":"n2189112751","loc":[-85.630874,41.9407777]},"n2189112752":{"id":"n2189112752","loc":[-85.6310622,41.9406841]},"n2189112753":{"id":"n2189112753","loc":[-85.6311459,41.9407772]},"n2189112754":{"id":"n2189112754","loc":[-85.6320308,41.9405747]},"n2189112755":{"id":"n2189112755","loc":[-85.6317769,41.9401857]},"n2189112756":{"id":"n2189112756","loc":[-85.6313462,41.9401785]},"n2189112757":{"id":"n2189112757","loc":[-85.6313423,41.9401199]},"n2189112758":{"id":"n2189112758","loc":[-85.6318308,41.9401184]},"n2189112759":{"id":"n2189112759","loc":[-85.6321154,41.9405433]},"n2189112760":{"id":"n2189112760","loc":[-85.6310307,41.941683]},"n2189112761":{"id":"n2189112761","loc":[-85.6309808,41.9416078]},"n2189112762":{"id":"n2189112762","loc":[-85.6312094,41.9415156]},"n2189112763":{"id":"n2189112763","loc":[-85.6312636,41.9415865]},"n2189112764":{"id":"n2189112764","loc":[-85.6309384,41.94155]},"n2189112765":{"id":"n2189112765","loc":[-85.631156,41.9414619]},"n2189112766":{"id":"n2189112766","loc":[-85.6311968,41.94152]},"n2189112767":{"id":"n2189112767","loc":[-85.6308946,41.9414851]},"n2189112768":{"id":"n2189112768","loc":[-85.6308237,41.9413888]},"n2189112769":{"id":"n2189112769","loc":[-85.6309858,41.9413228]},"n2189112770":{"id":"n2189112770","loc":[-85.6310567,41.9414192]},"n2189112771":{"id":"n2189112771","loc":[-85.6307774,41.9413276]},"n2189112772":{"id":"n2189112772","loc":[-85.6309068,41.9412735]},"n2189112773":{"id":"n2189112773","loc":[-85.6309531,41.9413347]},"n2189112774":{"id":"n2189112774","loc":[-85.6307975,41.9412466]},"n2189112775":{"id":"n2189112775","loc":[-85.6307006,41.9411699]},"n2189112776":{"id":"n2189112776","loc":[-85.6308289,41.941113]},"n2189112777":{"id":"n2189112777","loc":[-85.6308997,41.9412012]},"n2189112778":{"id":"n2189112778","loc":[-85.630765,41.9412062]},"n2189112779":{"id":"n2189112779","loc":[-85.630739,41.9412177]},"n2189112780":{"id":"n2189112780","loc":[-85.6305822,41.9410391]},"n2189112781":{"id":"n2189112781","loc":[-85.6304117,41.9408177]},"n2189112782":{"id":"n2189112782","loc":[-85.6305086,41.9407769]},"n2189112783":{"id":"n2189112783","loc":[-85.6306779,41.9410044]},"n2189112784":{"id":"n2189112784","loc":[-85.6307734,41.9421663]},"n2189112785":{"id":"n2189112785","loc":[-85.630708,41.9420741]},"n2189112786":{"id":"n2189112786","loc":[-85.630863,41.9420133]},"n2189112787":{"id":"n2189112787","loc":[-85.6309285,41.9421055]},"n2189112788":{"id":"n2189112788","loc":[-85.6307014,41.9420263]},"n2189112789":{"id":"n2189112789","loc":[-85.6306648,41.941971]},"n2189112790":{"id":"n2189112790","loc":[-85.6307927,41.9419178]},"n2189112791":{"id":"n2189112791","loc":[-85.6308366,41.9419696]},"n2189112792":{"id":"n2189112792","loc":[-85.6307574,41.9418708]},"n2189112793":{"id":"n2189112793","loc":[-85.6306288,41.9419231]},"n2189112794":{"id":"n2189112794","loc":[-85.6306943,41.9417835]},"n2189112795":{"id":"n2189112795","loc":[-85.6305344,41.9418474]},"n2189112796":{"id":"n2189112796","loc":[-85.6305981,41.9419355]},"n2189123410":{"id":"n2189123410","loc":[-85.6315476,41.9393801]},"n2189123412":{"id":"n2189123412","loc":[-85.6315247,41.9399188]},"n2189123415":{"id":"n2189123415","loc":[-85.6316484,41.9400433]},"n185945138":{"id":"n185945138","loc":[-85.627073,41.93319]},"n185945142":{"id":"n185945142","loc":[-85.6296891,41.9331674]},"n185945401":{"id":"n185945401","loc":[-85.6269,41.930199]},"n185945405":{"id":"n185945405","loc":[-85.6296598,41.9301676]},"n185956891":{"id":"n185956891","loc":[-85.6251617,41.9255049]},"n185959979":{"id":"n185959979","loc":[-85.626333,41.928347]},"n185959983":{"id":"n185959983","loc":[-85.6296419,41.9283288]},"n185961192":{"id":"n185961192","loc":[-85.627053,41.9352031]},"n185961200":{"id":"n185961200","loc":[-85.6297088,41.9351902]},"n185963655":{"id":"n185963655","loc":[-85.6296112,41.9273948]},"n185963665":{"id":"n185963665","loc":[-85.626047,41.92737]},"n185963688":{"id":"n185963688","loc":[-85.6296503,41.9292199]},"n185963689":{"id":"n185963689","loc":[-85.6296694,41.931157]},"n185963690":{"id":"n185963690","loc":[-85.6296791,41.9321485]},"n185963691":{"id":"n185963691","loc":[-85.6296991,41.9341973]},"n185967638":{"id":"n185967638","loc":[-85.627089,41.9361884]},"n185972917":{"id":"n185972917","loc":[-85.6293759,41.9388605]},"n185972919":{"id":"n185972919","loc":[-85.6290337,41.9380234]},"n185972921":{"id":"n185972921","loc":[-85.628424,41.936212]},"n185972923":{"id":"n185972923","loc":[-85.628367,41.936029]},"n185974511":{"id":"n185974511","loc":[-85.627064,41.932169]},"n185977728":{"id":"n185977728","loc":[-85.625605,41.925842]},"n185977729":{"id":"n185977729","loc":[-85.625685,41.926163]},"n185977731":{"id":"n185977731","loc":[-85.6257845,41.9264872]},"n185977733":{"id":"n185977733","loc":[-85.62663,41.929251]},"n185977734":{"id":"n185977734","loc":[-85.627008,41.930642]},"n185977736":{"id":"n185977736","loc":[-85.627029,41.930775]},"n185977738":{"id":"n185977738","loc":[-85.627041,41.930946]},"n185977739":{"id":"n185977739","loc":[-85.6270379,41.9311746]},"n185977742":{"id":"n185977742","loc":[-85.627055,41.934206]},"n185977744":{"id":"n185977744","loc":[-85.627084,41.936804]},"n185977746":{"id":"n185977746","loc":[-85.627104,41.936914]},"n185977748":{"id":"n185977748","loc":[-85.627156,41.937026]},"n185977750":{"id":"n185977750","loc":[-85.6272406,41.9371672]},"n185977752":{"id":"n185977752","loc":[-85.627317,41.93723]},"n185977753":{"id":"n185977753","loc":[-85.627422,41.937312]},"n185977755":{"id":"n185977755","loc":[-85.627754,41.937504]},"n185977757":{"id":"n185977757","loc":[-85.627883,41.937623]},"n185977761":{"id":"n185977761","loc":[-85.627984,41.93773]},"n1475283996":{"id":"n1475283996","loc":[-85.6270514,41.9317122],"tags":{"railway":"level_crossing"}},"n1475284004":{"id":"n1475284004","loc":[-85.6278177,41.9342117],"tags":{"railway":"level_crossing"}},"n1475284014":{"id":"n1475284014","loc":[-85.6251877,41.9255913],"tags":{"railway":"level_crossing"}},"n1475284017":{"id":"n1475284017","loc":[-85.6274992,41.9331816],"tags":{"railway":"level_crossing"}},"n1475284021":{"id":"n1475284021","loc":[-85.6297108,41.9353939],"tags":{"railway":"level_crossing"}},"n1475284027":{"id":"n1475284027","loc":[-85.62811,41.935198],"tags":{"railway":"level_crossing"}},"n1475284035":{"id":"n1475284035","loc":[-85.626888,41.9311757],"tags":{"railway":"level_crossing"}},"n1475293245":{"id":"n1475293245","loc":[-85.6286047,41.9367881]},"n1875654302":{"id":"n1875654302","loc":[-85.6296367,41.927491]},"n2189099388":{"id":"n2189099388","loc":[-85.6312007,41.9389988]},"n2189099389":{"id":"n2189099389","loc":[-85.6311003,41.9389992]},"n2189099390":{"id":"n2189099390","loc":[-85.6310988,41.9387847]},"n2189099391":{"id":"n2189099391","loc":[-85.6312165,41.9387843]},"n2189099392":{"id":"n2189099392","loc":[-85.6312152,41.9385857]},"n2189099393":{"id":"n2189099393","loc":[-85.6310877,41.9385862]},"n2189099394":{"id":"n2189099394","loc":[-85.6310858,41.9383161]},"n2189099395":{"id":"n2189099395","loc":[-85.6302002,41.9383196]},"n2189099396":{"id":"n2189099396","loc":[-85.6302011,41.9384472]},"n2189099397":{"id":"n2189099397","loc":[-85.6301018,41.9384476]},"n2189099398":{"id":"n2189099398","loc":[-85.6301025,41.9385419]},"n2189099399":{"id":"n2189099399","loc":[-85.6299275,41.9385427]},"n2189099400":{"id":"n2189099400","loc":[-85.62993,41.9388653]},"n2189099401":{"id":"n2189099401","loc":[-85.630107,41.9388645]},"n2189099402":{"id":"n2189099402","loc":[-85.6301079,41.9389908]},"n2189099403":{"id":"n2189099403","loc":[-85.6301951,41.9389904]},"n2189123382":{"id":"n2189123382","loc":[-85.6336279,41.9354365]},"n2189123384":{"id":"n2189123384","loc":[-85.6328492,41.9355177]},"n2189123387":{"id":"n2189123387","loc":[-85.6323762,41.9357396]},"n2189123388":{"id":"n2189123388","loc":[-85.6315174,41.9358966]},"n2189123389":{"id":"n2189123389","loc":[-85.6304331,41.936124]},"n2189123390":{"id":"n2189123390","loc":[-85.6302075,41.9364271]},"n2189123391":{"id":"n2189123391","loc":[-85.6303458,41.9367953]},"n2189123392":{"id":"n2189123392","loc":[-85.6299601,41.9369739]},"n2189123393":{"id":"n2189123393","loc":[-85.6299164,41.9374882]},"n2189123394":{"id":"n2189123394","loc":[-85.6299455,41.9378022]},"n2189123395":{"id":"n2189123395","loc":[-85.6299771,41.9379053]},"n2189123396":{"id":"n2189123396","loc":[-85.6301597,41.9379091]},"n2189123397":{"id":"n2189123397","loc":[-85.6308042,41.9377913]},"n2189123398":{"id":"n2189123398","loc":[-85.6316885,41.9378082]},"n2189123399":{"id":"n2189123399","loc":[-85.6316848,41.9380079]},"n2189123400":{"id":"n2189123400","loc":[-85.6318449,41.9381161]},"n2189123401":{"id":"n2189123401","loc":[-85.6320705,41.9381811]},"n2189123402":{"id":"n2189123402","loc":[-85.6321433,41.9383706]},"n2189123404":{"id":"n2189123404","loc":[-85.632056,41.9384355]},"n2189123406":{"id":"n2189123406","loc":[-85.6317867,41.9384572]},"n2189123409":{"id":"n2189123409","loc":[-85.6316572,41.9387281]},"n2189123417":{"id":"n2189123417","loc":[-85.6315946,41.93775]},"n2189123419":{"id":"n2189123419","loc":[-85.6302641,41.9378393]},"w208640158":{"id":"w208640158","tags":{"area":"yes","natural":"wetland"},"nodes":["n2189123379","n2189123382","n2189123384","n2189123387","n2189123388","n2189123389","n2189123390","n2189123391","n2189123392","n2189123393","n2189123394","n2189123395","n2189123396","n2189123419","n2189123397","n2189123417","n2189123398","n2189123399","n2189123400","n2189123401","n2189123402","n2189123404","n2189123406","n2189123409","n2189123410","n2189123412","n2189123415","n1819805722","n1819805861","n1819805887","n1819805760","n1819805641","n1819805632","n2189123379"]},"w134150787":{"id":"w134150787","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972905","n185972907","n1475293223","n185972911","n1475293241","n1475293246","n185972915","n185972917","n185972919","n1475293245","n185972921","n185972923","n1475284027","n1475284004","n1475284017","n1475283996","n1475284035","n1475284014","n185956891"]},"w208639443":{"id":"w208639443","tags":{"area":"yes","building":"yes"},"nodes":["n2189112720","n2189112721","n2189112722","n2189112723","n2189112720"]},"w17966462":{"id":"w17966462","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977728","n185977729","n185977731","n185963665","n185959979","n185977733","n185945401","n185977734","n185977736","n185977738","n185977739","n1475283996","n185974511","n185945138","n185977742","n185961192","n185967638","n185977744","n185977746","n185977748","n185977750","n185977752","n185977753","n185977754","n185977755","n185977757","n185977761","n185958030","n1475293263","n185963698","n185952745","n185947850","n185977762"]},"w203985741":{"id":"w203985741","tags":{"area":"yes","leisure":"park","name":"Conservation Park"},"nodes":["n2139982404","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n2139982404"]},"w17963676":{"id":"w17963676","tags":{"highway":"service"},"nodes":["n1475293258","n2139982428","n2139982427","n2139982426","n2139982425","n2139982424","n2139982423","n2139982422","n2139982430","n2139982421","n2139982420","n2139982429","n1475293231","n1475293258","n1475293251","n1475293223","n185952745"]},"w203985745":{"id":"w203985745","tags":{"highway":"footway"},"nodes":["n2139982430","n2139982431","n2139982432","n2139982433","n2139982434","n2139982435","n2139982436","n2139982437","n2139982438","n2139982439","n2139982440","n2139982441","n1475293231"]},"w208639451":{"id":"w208639451","tags":{"area":"yes","building":"yes"},"nodes":["n2189112754","n2189112755","n2189112756","n2189112757","n2189112758","n2189112759","n2189112754"]},"w208639452":{"id":"w208639452","tags":{"area":"yes","building":"yes"},"nodes":["n2189112760","n2189112761","n2189112766","n2189112762","n2189112763","n2189112760"]},"w206805244":{"id":"w206805244","tags":{"area":"yes","building":"yes"},"nodes":["n2168544766","n2168544767","n2168544768","n2168544769","n2168544770","n2168544771","n2168544772","n2168544773","n2168544774","n2168544775","n2168544776","n2168544777","n2168544778","n2168544779","n2168544766"]},"w208639444":{"id":"w208639444","tags":{"area":"yes","building":"yes"},"nodes":["n2189112724","n2189112725","n2189112726","n2189112727","n2189112724"]},"w208639450":{"id":"w208639450","tags":{"area":"yes","building":"yes"},"nodes":["n2189112750","n2189112751","n2189112752","n2189112753","n2189112750"]},"w208639448":{"id":"w208639448","tags":{"area":"yes","building":"yes"},"nodes":["n2189112740","n2189112741","n2189112742","n2189112743","n2189112740"]},"w208637859":{"id":"w208637859","tags":{"area":"yes","building":"yes"},"nodes":["n2189099387","n2189099388","n2189099389","n2189099390","n2189099391","n2189099392","n2189099393","n2189099394","n2189099395","n2189099396","n2189099397","n2189099398","n2189099399","n2189099400","n2189099401","n2189099402","n2189099403","n2189099404","n2189099405","n2189099406","n2189099407","n2189099408","n2189099409","n2189099410","n2189099387"]},"w208639453":{"id":"w208639453","tags":{"area":"yes","building":"yes"},"nodes":["n2189112764","n2189112765","n2189112766","n2189112761","n2189112764"]},"w208639456":{"id":"w208639456","tags":{"area":"yes","building":"yes"},"nodes":["n2189112774","n2189112778","n2189112779","n2189112775","n2189112776","n2189112777","n2189112774"]},"w208639445":{"id":"w208639445","tags":{"area":"yes","building":"yes"},"nodes":["n2189112728","n2189112729","n2189112730","n2189112731","n2189112728"]},"w17967776":{"id":"w17967776","tags":{"highway":"residential","name":"5th St"},"nodes":["n185958032","n185988963"]},"w208639461":{"id":"w208639461","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112794","n2189112795","n2189112796","n2189112793","n2189112792"]},"w206805241":{"id":"w206805241","tags":{"area":"yes","building":"yes"},"nodes":["n2168544754","n2168544755","n2168544756","n2168544757","n2168544754"]},"w208639449":{"id":"w208639449","tags":{"area":"yes","building":"yes"},"nodes":["n2189112744","n2189112745","n2189112746","n2189112747","n2189112748","n2189112749","n2189112744"]},"w208639455":{"id":"w208639455","tags":{"area":"yes","building":"yes"},"nodes":["n2189112771","n2189112772","n2189112773","n2189112768","n2189112771"]},"w208639457":{"id":"w208639457","tags":{"area":"yes","building":"yes"},"nodes":["n2189112780","n2189112781","n2189112782","n2189112783","n2189112780"]},"w208639446":{"id":"w208639446","tags":{"area":"yes","building":"yes"},"nodes":["n2189112732","n2189112733","n2189112734","n2189112735","n2189112732"]},"w208639454":{"id":"w208639454","tags":{"area":"yes","building":"yes"},"nodes":["n2189112767","n2189112768","n2189112773","n2189112769","n2189112770","n2189112767"]},"w203985743":{"id":"w203985743","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139982411","n2139982412","n2139982413","n2139982414","n2139982415","n2139982416","n2139982417","n2139982419","n2139982418","n2139982411"]},"w17965023":{"id":"w17965023","tags":{"highway":"residential","name":"4th St"},"nodes":["n185963655","n1875654302","n185959983","n185963688","n185945405","n185963689","n185963690","n185945142","n185963691","n185961200","n1475284021","n1475293246","n1875654132","n1475293263"]},"w206805242":{"id":"w206805242","tags":{"area":"yes","building":"yes"},"nodes":["n2168544758","n2168544759","n2168544760","n2168544761","n2168544758"]},"w208639460":{"id":"w208639460","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112793","n2189112789","n2189112790","n2189112792"]},"w208639447":{"id":"w208639447","tags":{"area":"yes","building":"yes"},"nodes":["n2189112736","n2189112737","n2189112738","n2189112739","n2189112736"]},"w208639458":{"id":"w208639458","tags":{"area":"yes","building":"yes"},"nodes":["n2189112784","n2189112785","n2189112786","n2189112787","n2189112784"]},"w203985744":{"id":"w203985744","tags":{"highway":"service"},"nodes":["n2139982425","n2139982400"]},"w208639459":{"id":"w208639459","tags":{"area":"yes","building":"yes"},"nodes":["n2189112788","n2189112789","n2189112790","n2189112791","n2189112788"]},"w203985742":{"id":"w203985742","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139982407","n2139982408","n2139982409","n2139982410","n2139982407"]},"w206805243":{"id":"w206805243","tags":{"area":"yes","building":"yes"},"nodes":["n2168544762","n2168544763","n2168544764","n2168544765","n2168544762"]},"n185959081":{"id":"n185959081","loc":[-85.628469,41.948674]},"n185967427":{"id":"n185967427","loc":[-85.632054,41.951174]},"n185967424":{"id":"n185967424","loc":[-85.6320391,41.9499109]},"n185968101":{"id":"n185968101","loc":[-85.6308395,41.9511969]},"n185960792":{"id":"n185960792","loc":[-85.632074,41.953707]},"n185961389":{"id":"n185961389","loc":[-85.630935,41.959037]},"n185961391":{"id":"n185961391","loc":[-85.632169,41.959025]},"n185965395":{"id":"n185965395","loc":[-85.63216,41.959859]},"n185966953":{"id":"n185966953","loc":[-85.630894,41.957428]},"n185966955":{"id":"n185966955","loc":[-85.632122,41.957427]},"n185967430":{"id":"n185967430","loc":[-85.632077,41.952453]},"n185967432":{"id":"n185967432","loc":[-85.632095,41.954685]},"n185967434":{"id":"n185967434","loc":[-85.632121,41.955914]},"n185967436":{"id":"n185967436","loc":[-85.632128,41.9583]},"n185967438":{"id":"n185967438","loc":[-85.632187,41.960681]},"n185967440":{"id":"n185967440","loc":[-85.632182,41.961493]},"n185968102":{"id":"n185968102","loc":[-85.630855,41.952452]},"n185968104":{"id":"n185968104","loc":[-85.630887,41.953714]},"n185968106":{"id":"n185968106","loc":[-85.630883,41.954692]},"n185968108":{"id":"n185968108","loc":[-85.630904,41.955913]},"n185968110":{"id":"n185968110","loc":[-85.630904,41.958058]},"n185968112":{"id":"n185968112","loc":[-85.630952,41.960667]},"n185968114":{"id":"n185968114","loc":[-85.630972,41.961495]},"n185968116":{"id":"n185968116","loc":[-85.630962,41.961967]},"n185978969":{"id":"n185978969","loc":[-85.633214,41.948618]},"n185985812":{"id":"n185985812","loc":[-85.633274,41.951159]},"n185986155":{"id":"n185986155","loc":[-85.633258,41.949893]},"n2208608826":{"id":"n2208608826","loc":[-85.6339222,41.9486225]},"w17964531":{"id":"w17964531","tags":{"highway":"residential","name":"Willow Dr"},"nodes":["n185959079","n185959081"]},"w17967386":{"id":"w17967386","tags":{"highway":"residential","name":"East Armitage Street"},"nodes":["n185982195","n185968101","n185967427","n185985812","n185974583"]},"w17965502":{"id":"w17965502","tags":{"highway":"residential","name":"Elm Street"},"nodes":["n185968100","n185968101","n185968102","n185968104","n185968106","n185968108","n185966953","n185968110","n185961389","n185968112","n185968114","n185968116"]},"w17967844":{"id":"w17967844","tags":{"highway":"residential","name":"East Bennett Street"},"nodes":["n185982193","n185967424","n185986155","n185978390"]},"w17966581":{"id":"w17966581","tags":{"highway":"residential","name":"E Kelsey St"},"nodes":["n185978967","n185978969","n2208608826","n185971578"]},"w17965402":{"id":"w17965402","tags":{"highway":"residential","name":"Walnut Street"},"nodes":["n185967422","n185967424","n185967427","n185967430","n185960792","n185967432","n185967434","n185966955","n185967436","n185961391","n185965395","n185967438","n185967440"]},"n2199093506":{"id":"n2199093506","loc":[-85.6251879,41.9478322]},"n2199093505":{"id":"n2199093505","loc":[-85.6252076,41.9477749]},"n2199093504":{"id":"n2199093504","loc":[-85.6252289,41.9477602]},"n2199093503":{"id":"n2199093503","loc":[-85.625201,41.9477492]},"n2199093502":{"id":"n2199093502","loc":[-85.6251682,41.9477066]},"n2199093501":{"id":"n2199093501","loc":[-85.6251715,41.947609]},"n2199093500":{"id":"n2199093500","loc":[-85.6252125,41.9475639]},"n2199093499":{"id":"n2199093499","loc":[-85.6252896,41.9475602]},"n2199093498":{"id":"n2199093498","loc":[-85.6253027,41.9475334]},"n2199093497":{"id":"n2199093497","loc":[-85.6253437,41.9474822]},"n2199093496":{"id":"n2199093496","loc":[-85.6254421,41.9474675]},"n2199093495":{"id":"n2199093495","loc":[-85.6256503,41.9474944]},"n2199093494":{"id":"n2199093494","loc":[-85.6257257,41.9476127]},"n2199093493":{"id":"n2199093493","loc":[-85.6257028,41.9477285]},"n2199093492":{"id":"n2199093492","loc":[-85.6255339,41.9478102]},"n2199093491":{"id":"n2199093491","loc":[-85.6253912,41.9478224]},"n2199093490":{"id":"n2199093490","loc":[-85.6253043,41.947859]},"n2199093489":{"id":"n2199093489","loc":[-85.6252027,41.9478846]},"n2199093458":{"id":"n2199093458","loc":[-85.6246876,41.9486617]},"n2199093457":{"id":"n2199093457","loc":[-85.6243127,41.9486583]},"n2199093456":{"id":"n2199093456","loc":[-85.624306,41.9490569]},"n2199093455":{"id":"n2199093455","loc":[-85.624681,41.9490603]},"n2199093514":{"id":"n2199093514","loc":[-85.6236228,41.9496059]},"n2199093513":{"id":"n2199093513","loc":[-85.6236231,41.9496997]},"n2199093512":{"id":"n2199093512","loc":[-85.623357,41.9497002]},"n2199093511":{"id":"n2199093511","loc":[-85.6233567,41.9496136]},"n2199093508":{"id":"n2199093508","loc":[-85.6239735,41.9494287]},"n2199093507":{"id":"n2199093507","loc":[-85.6239741,41.9496052]},"n2199093488":{"id":"n2199093488","loc":[-85.624497,41.9512286]},"n2199093487":{"id":"n2199093487","loc":[-85.6244966,41.9511259]},"n2199093486":{"id":"n2199093486","loc":[-85.6243151,41.9511263]},"n2199093485":{"id":"n2199093485","loc":[-85.6243154,41.951229]},"n2199093484":{"id":"n2199093484","loc":[-85.6241205,41.9508665]},"n2199093483":{"id":"n2199093483","loc":[-85.624115,41.9505249]},"n2199093482":{"id":"n2199093482","loc":[-85.6243149,41.9505231]},"n2199093481":{"id":"n2199093481","loc":[-85.6243203,41.9508648]},"n2199093480":{"id":"n2199093480","loc":[-85.624393,41.9508668]},"n2199093479":{"id":"n2199093479","loc":[-85.6243904,41.9505956]},"n2199093478":{"id":"n2199093478","loc":[-85.6246727,41.950594]},"n2199093477":{"id":"n2199093477","loc":[-85.624675,41.9508203]},"n2199093476":{"id":"n2199093476","loc":[-85.6245097,41.9508212]},"n2199093475":{"id":"n2199093475","loc":[-85.6245101,41.9508662]},"n2199093474":{"id":"n2199093474","loc":[-85.6241008,41.9493459]},"n2199093473":{"id":"n2199093473","loc":[-85.6242442,41.9493459]},"n2199093472":{"id":"n2199093472","loc":[-85.6242442,41.9493681]},"n2199093471":{"id":"n2199093471","loc":[-85.6243397,41.9493681]},"n2199093470":{"id":"n2199093470","loc":[-85.6243417,41.9493511]},"n2199093469":{"id":"n2199093469","loc":[-85.6247251,41.9493485]},"n2199093468":{"id":"n2199093468","loc":[-85.6247548,41.9504949]},"n2199093467":{"id":"n2199093467","loc":[-85.6241214,41.9505017]},"n2199093466":{"id":"n2199093466","loc":[-85.6254398,41.950174]},"n2199093465":{"id":"n2199093465","loc":[-85.6254412,41.9499872]},"n2199093464":{"id":"n2199093464","loc":[-85.6255363,41.9499876]},"n2199093463":{"id":"n2199093463","loc":[-85.6255374,41.9498439]},"n2199093462":{"id":"n2199093462","loc":[-85.6255638,41.949844]},"n2199093461":{"id":"n2199093461","loc":[-85.6255652,41.9496672]},"n2199093460":{"id":"n2199093460","loc":[-85.6251823,41.9496656]},"n2199093459":{"id":"n2199093459","loc":[-85.6251785,41.9501729]},"n2199093510":{"id":"n2199093510","loc":[-85.6229922,41.9496143]},"n2199093509":{"id":"n2199093509","loc":[-85.6229915,41.9494306]},"n185948903":{"id":"n185948903","loc":[-85.616514,41.947449]},"n185955120":{"id":"n185955120","loc":[-85.620103,41.951]},"n185955143":{"id":"n185955143","loc":[-85.619784,41.94746]},"n185960124":{"id":"n185960124","loc":[-85.615238,41.947468]},"n185961362":{"id":"n185961362","loc":[-85.617437,41.947451]},"n185961364":{"id":"n185961364","loc":[-85.61861,41.947456]},"n185961367":{"id":"n185961367","loc":[-85.620088,41.947458]},"n185965105":{"id":"n185965105","loc":[-85.620087,41.94924]},"n185970220":{"id":"n185970220","loc":[-85.62156,41.948333]},"n185974697":{"id":"n185974697","loc":[-85.6201059,41.950132]},"n2138420778":{"id":"n2138420778","loc":[-85.616948,41.9474499]},"w17967535":{"id":"w17967535","tags":{"highway":"residential","name":"10th Ave"},"nodes":["n185955120","n185986812","n185983141"]},"w209716130":{"id":"w209716130","tags":{"area":"yes","building":"yes"},"nodes":["n2199093485","n2199093486","n2199093487","n2199093488","n2199093485"]},"w17964788":{"id":"w17964788","tags":{"highway":"residential","name":"6th Ave"},"nodes":["n185960124","n185948903","n2138420778","n185961362","n185961364","n185955143","n185961367","n185961369","n185961371"]},"w17965159":{"id":"w17965159","tags":{"highway":"residential","name":"8th Ave"},"nodes":["n185965105","n185965108","n185965110"]},"w209716125":{"id":"w209716125","tags":{"area":"yes","building":"yes"},"nodes":["n2199093459","n2199093460","n2199093461","n2199093462","n2199093463","n2199093464","n2199093465","n2199093466","n2199093459"]},"w17965699":{"id":"w17965699","tags":{"highway":"residential","name":"7th Ave"},"nodes":["n185970220","n185970222","n185970224"]},"w209716132":{"id":"w209716132","tags":{"area":"yes","building":"yes"},"nodes":["n2199093507","n2199093508","n2199093509","n2199093510","n2199093511","n2199093512","n2199093513","n2199093514","n2199093507"]},"w17966129":{"id":"w17966129","tags":{"highway":"residential","name":"9th Ave"},"nodes":["n185974697","n185974699"]},"w209716127":{"id":"w209716127","tags":{"area":"yes","building":"yes"},"nodes":["n2199093475","n2199093476","n2199093477","n2199093478","n2199093479","n2199093480","n2199093475"]},"w209716131":{"id":"w209716131","tags":{"area":"yes","natural":"water","water":"pond"},"nodes":["n2199093489","n2199093490","n2199093491","n2199093492","n2199093493","n2199093494","n2199093495","n2199093496","n2199093497","n2199093498","n2199093499","n2199093500","n2199093501","n2199093502","n2199093503","n2199093504","n2199093505","n2199093506","n2199093489"]},"w209716126":{"id":"w209716126","tags":{"area":"yes","building":"yes"},"nodes":["n2199093467","n2199093468","n2199093469","n2199093470","n2199093471","n2199093472","n2199093473","n2199093474","n2199093467"]},"w209716124":{"id":"w209716124","tags":{"area":"yes","building":"yes"},"nodes":["n2199093455","n2199093456","n2199093457","n2199093458","n2199093455"]},"w209716128":{"id":"w209716128","tags":{"area":"yes","building":"yes"},"nodes":["n2199093481","n2199093482","n2199093483","n2199093484","n2199093481"]},"n185949872":{"id":"n185949872","loc":[-85.643009,41.949264]},"n185949875":{"id":"n185949875","loc":[-85.642598,41.94929]},"n185949877":{"id":"n185949877","loc":[-85.642127,41.949382]},"n185949881":{"id":"n185949881","loc":[-85.64169,41.949936]},"n185988165":{"id":"n185988165","loc":[-85.642167,41.947657]},"n185988167":{"id":"n185988167","loc":[-85.642347,41.947662]},"n185988169":{"id":"n185988169","loc":[-85.642621,41.947659]},"n185965019":{"id":"n185965019","loc":[-85.6385084,41.951127]},"n1475293248":{"id":"n1475293248","loc":[-85.6386095,41.9512214]},"n185962639":{"id":"n185962639","loc":[-85.649669,41.949161]},"n185962810":{"id":"n185962810","loc":[-85.649907,41.949157]},"n185964355":{"id":"n185964355","loc":[-85.637412,41.9511359]},"n185965021":{"id":"n185965021","loc":[-85.638661,41.952386]},"n185965023":{"id":"n185965023","loc":[-85.638654,41.953665]},"n185965025":{"id":"n185965025","loc":[-85.638694,41.954649]},"n185965027":{"id":"n185965027","loc":[-85.638724,41.955913]},"n185971415":{"id":"n185971415","loc":[-85.644466,41.949246]},"n185971417":{"id":"n185971417","loc":[-85.647021,41.949193]},"n185971420":{"id":"n185971420","loc":[-85.648476,41.949169]},"n185979975":{"id":"n185979975","loc":[-85.644429,41.947633]},"n185988171":{"id":"n185988171","loc":[-85.645377,41.947622]},"w17963211":{"id":"w17963211","tags":{"highway":"residential"},"nodes":["n185949870","n185949872","n185949875","n185949877","n185949881"]},"w17965839":{"id":"w17965839","tags":{"highway":"residential","name":"Arnold St"},"nodes":["n185949870","n185971415","n185971417","n185971420","n185962639","n185962810"]},"w17967618":{"id":"w17967618","tags":{"highway":"residential","name":"Pierson St"},"nodes":["n185967777","n185988165","n185988167","n185988169","n185985824","n185979975","n185988171"]},"w17965149":{"id":"w17965149","tags":{"highway":"residential","name":"Oak St"},"nodes":["n185965019","n1475293248","n185965021","n185965023","n185965025","n185965027"]},"w17966118":{"id":"w17966118","tags":{"highway":"residential","name":"West Armitage Street"},"nodes":["n185974583","n185974585","n185964355","n185965019"]},"n2208608800":{"id":"n2208608800","loc":[-85.6354294,41.9486201]},"n2199109806":{"id":"n2199109806","loc":[-85.6350474,41.9477884]},"n2199109804":{"id":"n2199109804","loc":[-85.6350476,41.9477962]},"n2199109802":{"id":"n2199109802","loc":[-85.635002,41.9477969]},"n2199109799":{"id":"n2199109799","loc":[-85.6350018,41.9477883]},"n2199109797":{"id":"n2199109797","loc":[-85.6349141,41.9477897]},"n2199109795":{"id":"n2199109795","loc":[-85.6349131,41.9477535]},"n2199109793":{"id":"n2199109793","loc":[-85.6349395,41.9477531]},"n2199109791":{"id":"n2199109791","loc":[-85.6349382,41.9477077]},"n2199109789":{"id":"n2199109789","loc":[-85.6351236,41.9477049]},"n2199109787":{"id":"n2199109787","loc":[-85.6351259,41.9477872]},"n2199109785":{"id":"n2199109785","loc":[-85.634972,41.9475992]},"n2199109783":{"id":"n2199109783","loc":[-85.6349206,41.9475997]},"n2199109770":{"id":"n2199109770","loc":[-85.6348499,41.9475461]},"n2199109768":{"id":"n2199109768","loc":[-85.6348499,41.9475084]},"n2199109765":{"id":"n2199109765","loc":[-85.6349241,41.9474569]},"n2199109763":{"id":"n2199109763","loc":[-85.634967,41.9474564]},"n2199109762":{"id":"n2199109762","loc":[-85.6350405,41.9475121]},"n2199109761":{"id":"n2199109761","loc":[-85.6350405,41.9475419]},"n2199109753":{"id":"n2199109753","loc":[-85.6342443,41.9478391]},"n2199109751":{"id":"n2199109751","loc":[-85.6342427,41.9477927]},"n2199109745":{"id":"n2199109745","loc":[-85.6342439,41.9476859]},"n2199109743":{"id":"n2199109743","loc":[-85.6342429,41.9476575]},"n2199109741":{"id":"n2199109741","loc":[-85.6344615,41.9476533]},"n2199109739":{"id":"n2199109739","loc":[-85.6344678,41.9478348]},"n2199109737":{"id":"n2199109737","loc":[-85.634416,41.9480059]},"n2199109735":{"id":"n2199109735","loc":[-85.6344145,41.9478983]},"n2199109733":{"id":"n2199109733","loc":[-85.6342749,41.9478993]},"n2199109731":{"id":"n2199109731","loc":[-85.6342753,41.9479272]},"n2199109729":{"id":"n2199109729","loc":[-85.6342498,41.9479274]},"n2199109727":{"id":"n2199109727","loc":[-85.6342505,41.9479762]},"n2199109725":{"id":"n2199109725","loc":[-85.6342743,41.947976]},"n2199109723":{"id":"n2199109723","loc":[-85.6342747,41.948007]},"n2199109721":{"id":"n2199109721","loc":[-85.6343415,41.9476355]},"n2199109719":{"id":"n2199109719","loc":[-85.6343391,41.9474973]},"n2199109717":{"id":"n2199109717","loc":[-85.6343133,41.9474798]},"n2199109715":{"id":"n2199109715","loc":[-85.6342874,41.9474737]},"n2199109709":{"id":"n2199109709","loc":[-85.6349804,41.94815]},"n2199109707":{"id":"n2199109707","loc":[-85.6348915,41.9481505]},"n2199109705":{"id":"n2199109705","loc":[-85.6348917,41.9481692]},"n2199109702":{"id":"n2199109702","loc":[-85.6348522,41.9481694]},"n2199109700":{"id":"n2199109700","loc":[-85.6348532,41.9482679]},"n2199109698":{"id":"n2199109698","loc":[-85.6348315,41.948268]},"n2199109696":{"id":"n2199109696","loc":[-85.6348318,41.9482955]},"n2199109694":{"id":"n2199109694","loc":[-85.6349653,41.9482946]},"n2199109692":{"id":"n2199109692","loc":[-85.6349656,41.9483211]},"n2199109690":{"id":"n2199109690","loc":[-85.634999,41.9483209]},"n2199109688":{"id":"n2199109688","loc":[-85.6349987,41.9482947]},"n2199109686":{"id":"n2199109686","loc":[-85.6351753,41.9482935]},"n2199109684":{"id":"n2199109684","loc":[-85.6351749,41.9482617]},"n2199109682":{"id":"n2199109682","loc":[-85.6351588,41.9482618]},"n2199109680":{"id":"n2199109680","loc":[-85.6351575,41.9481518]},"n2199109678":{"id":"n2199109678","loc":[-85.6350671,41.9481524]},"n2199109676":{"id":"n2199109676","loc":[-85.6350649,41.9479659]},"n2199109674":{"id":"n2199109674","loc":[-85.6349785,41.9479665]},"n2199109671":{"id":"n2199109671","loc":[-85.6343069,41.9483263]},"n2199109669":{"id":"n2199109669","loc":[-85.6343052,41.9482981]},"n2199109658":{"id":"n2199109658","loc":[-85.6343314,41.9480549]},"n2199109656":{"id":"n2199109656","loc":[-85.6343305,41.9480461]},"n2199109654":{"id":"n2199109654","loc":[-85.634435,41.9480468]},"n2199109652":{"id":"n2199109652","loc":[-85.6344342,41.9483746]},"n2199109650":{"id":"n2199109650","loc":[-85.6344629,41.9483727]},"n2199109648":{"id":"n2199109648","loc":[-85.6344637,41.9484561]},"n2199109645":{"id":"n2199109645","loc":[-85.63443,41.9484567]},"n2199109642":{"id":"n2199109642","loc":[-85.6344317,41.948505]},"n185964352":{"id":"n185964352","loc":[-85.6373958,41.9489943]},"n185964351":{"id":"n185964351","loc":[-85.637113,41.9486]},"n2208608825":{"id":"n2208608825","loc":[-85.6354483,41.9494241]},"n2208608823":{"id":"n2208608823","loc":[-85.6360418,41.949416]},"n2208608821":{"id":"n2208608821","loc":[-85.6360458,41.9495802]},"n2208608811":{"id":"n2208608811","loc":[-85.6357458,41.9495843]},"n2208608808":{"id":"n2208608808","loc":[-85.6357508,41.9497835]},"n2208608806":{"id":"n2208608806","loc":[-85.6354573,41.9497875]},"n2208608795":{"id":"n2208608795","loc":[-85.6354595,41.9498778]},"n2199109638":{"id":"n2199109638","loc":[-85.6349605,41.949749]},"n2199109636":{"id":"n2199109636","loc":[-85.6349605,41.9497639]},"n2199109634":{"id":"n2199109634","loc":[-85.6349061,41.94971]},"n2199109632":{"id":"n2199109632","loc":[-85.6349048,41.9496569]},"n2199109630":{"id":"n2199109630","loc":[-85.6348835,41.9496571]},"n2199109628":{"id":"n2199109628","loc":[-85.6348829,41.9497103]},"n2199109626":{"id":"n2199109626","loc":[-85.635227,41.9497738]},"n2199109624":{"id":"n2199109624","loc":[-85.6352184,41.9497787]},"n2199109622":{"id":"n2199109622","loc":[-85.6351181,41.9497806]},"n2199109620":{"id":"n2199109620","loc":[-85.6351181,41.9497456]},"n2199109618":{"id":"n2199109618","loc":[-85.6348842,41.9497651]},"n2199109616":{"id":"n2199109616","loc":[-85.6348827,41.9496238]},"n2199109615":{"id":"n2199109615","loc":[-85.6351268,41.9496206]},"n2199109614":{"id":"n2199109614","loc":[-85.6351261,41.9495891]},"n2199109613":{"id":"n2199109613","loc":[-85.6351957,41.9495881]},"n2199109612":{"id":"n2199109612","loc":[-85.6351924,41.9494515]},"n2199109611":{"id":"n2199109611","loc":[-85.6353997,41.9494488]},"n2199109610":{"id":"n2199109610","loc":[-85.6354074,41.9497715]},"n2189015681":{"id":"n2189015681","loc":[-85.6344229,41.9509639]},"n2189015677":{"id":"n2189015677","loc":[-85.634424,41.9507396]},"n2138493843":{"id":"n2138493843","loc":[-85.6343935,41.9502836]},"n2138493840":{"id":"n2138493840","loc":[-85.634398,41.9506264]},"n354002838":{"id":"n354002838","loc":[-85.6345197,41.9510631]},"n2114807590":{"id":"n2114807590","loc":[-85.634511,41.9499767]},"n185964353":{"id":"n185964353","loc":[-85.6374092,41.9498755]},"n1819849180":{"id":"n1819849180","loc":[-85.6348236,41.94996]},"n1819849115":{"id":"n1819849115","loc":[-85.6354372,41.9499538]},"n1819848921":{"id":"n1819848921","loc":[-85.6348439,41.951064]},"n1819848885":{"id":"n1819848885","loc":[-85.6354575,41.9510578]},"n185984281":{"id":"n185984281","loc":[-85.638075,41.949872]},"n2208608827":{"id":"n2208608827","loc":[-85.6339169,41.9473191]},"n2199109749":{"id":"n2199109749","loc":[-85.6342082,41.9477934]},"n2199109747":{"id":"n2199109747","loc":[-85.6342045,41.9476867]},"n2199109713":{"id":"n2199109713","loc":[-85.6342404,41.9474746]},"n2199109711":{"id":"n2199109711","loc":[-85.6342404,41.9476355]},"n2199109673":{"id":"n2199109673","loc":[-85.6340886,41.9483282]},"n2199109667":{"id":"n2199109667","loc":[-85.6342403,41.9482988]},"n2199109665":{"id":"n2199109665","loc":[-85.6342386,41.9482116]},"n2199109662":{"id":"n2199109662","loc":[-85.6340861,41.9482135]},"n2199109660":{"id":"n2199109660","loc":[-85.6340802,41.9480562]},"n2199109640":{"id":"n2199109640","loc":[-85.6340928,41.9485063]},"n354031366":{"id":"n354031366","loc":[-85.6341667,41.9477778],"tags":{"amenity":"place_of_worship","name":"Faith Tabernacle Church","religion":"christian"}},"n2189015686":{"id":"n2189015686","loc":[-85.6337798,41.95099]},"n2189015684":{"id":"n2189015684","loc":[-85.6337794,41.9509674]},"n2189015673":{"id":"n2189015673","loc":[-85.6337501,41.9507457]},"n2189015669":{"id":"n2189015669","loc":[-85.6337501,41.9506974]},"n2189015665":{"id":"n2189015665","loc":[-85.6339034,41.9506959]},"n2189015662":{"id":"n2189015662","loc":[-85.6339015,41.950436]},"n2189015658":{"id":"n2189015658","loc":[-85.6334916,41.9504376]},"n2189015655":{"id":"n2189015655","loc":[-85.6334939,41.9507558]},"n2189015650":{"id":"n2189015650","loc":[-85.6334543,41.950756]},"n2189015649":{"id":"n2189015649","loc":[-85.633456,41.9509915]},"n2138493842":{"id":"n2138493842","loc":[-85.6339937,41.9502836]},"n2138493841":{"id":"n2138493841","loc":[-85.6339983,41.9506281]},"n2114807579":{"id":"n2114807579","loc":[-85.6333644,41.9510682]},"n2114807573":{"id":"n2114807573","loc":[-85.6333557,41.9499819]},"n354031330":{"id":"n354031330","loc":[-85.6341667,41.9497222],"tags":{"amenity":"place_of_worship","name":"Trinity Episcopal Church","religion":"christian"}},"n185960794":{"id":"n185960794","loc":[-85.633307,41.9537]},"n185964357":{"id":"n185964357","loc":[-85.637432,41.952399]},"n185964358":{"id":"n185964358","loc":[-85.637452,41.953665]},"n185964359":{"id":"n185964359","loc":[-85.63746,41.954658]},"n185964360":{"id":"n185964360","loc":[-85.637473,41.95592]},"n185964361":{"id":"n185964361","loc":[-85.637468,41.956906]},"n185964362":{"id":"n185964362","loc":[-85.637483,41.958313]},"n185966957":{"id":"n185966957","loc":[-85.633361,41.957422]},"n185975351":{"id":"n185975351","loc":[-85.63334,41.9559]},"n185978784":{"id":"n185978784","loc":[-85.633311,41.954679]},"n185986157":{"id":"n185986157","loc":[-85.633287,41.952426]},"n185986158":{"id":"n185986158","loc":[-85.6333607,41.9582301],"tags":{"highway":"turning_circle"}},"w17965182":{"id":"w17965182","tags":{"highway":"residential","name":"W Prutzman St"},"nodes":["n185965289","n2189153241","n185965291"]},"w208627205":{"id":"w208627205","tags":{"area":"yes","building":"yes"},"nodes":["n2189015649","n2189015650","n2189015655","n2189015658","n2189015662","n2189015665","n2189015669","n2189015673","n2189015677","n2189015681","n2189015684","n2189015686","n2189015649"]},"w209717042":{"id":"w209717042","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"presbyterian","name":"First Presbyterian Church","religion":"christian"},"nodes":["n2199109610","n2199109611","n2199109612","n2199109613","n2199109614","n2199109615","n2199109616","n2199109630","n2199109632","n2199109634","n2199109628","n2199109618","n2199109636","n2199109638","n2199109620","n2199109622","n2199109624","n2199109626","n2199109610"]},"w209717045":{"id":"w209717045","tags":{"area":"yes","building":"yes"},"nodes":["n2199109711","n2199109713","n2199109715","n2199109717","n2199109719","n2199109721","n2199109711"]},"w209717047":{"id":"w209717047","tags":{"area":"yes","building":"yes"},"nodes":["n2199109739","n2199109741","n2199109743","n2199109745","n2199109747","n2199109749","n2199109751","n2199109753","n2199109739"]},"w209717044":{"id":"w209717044","tags":{"area":"yes","building":"yes"},"nodes":["n2199109674","n2199109676","n2199109678","n2199109680","n2199109682","n2199109684","n2199109686","n2199109688","n2199109690","n2199109692","n2199109694","n2199109696","n2199109698","n2199109700","n2199109702","n2199109705","n2199109707","n2199109709","n2199109674"]},"w210822776":{"id":"w210822776","tags":{"highway":"service","service":"alley","surface":"unpaved"},"nodes":["n2208608795","n2208608806","n2208608825","n2208608800","n2189153241"]},"w210822778":{"id":"w210822778","tags":{"highway":"service","service":"alley"},"nodes":["n2208608826","n2208608827"]},"w209717050":{"id":"w209717050","tags":{"area":"yes","building":"yes"},"nodes":["n2199109787","n2199109789","n2199109791","n2199109793","n2199109795","n2199109797","n2199109799","n2199109802","n2199109804","n2199109806","n2199109787"]},"w17965097":{"id":"w17965097","tags":{"highway":"residential","name":"Maple Street"},"nodes":["n185964351","n185964352","n185964353","n185964355","n185964357","n185964358","n185964359","n185964360","n185964361","n185964362"]},"w17965856":{"id":"w17965856","tags":{"highway":"residential","name":"W Kelsey St"},"nodes":["n185971578","n2208608800","n185971580","n185964351"]},"w17967444":{"id":"w17967444","tags":{"highway":"residential","name":"East Street"},"nodes":["n185966937","n185978969","n185986155","n185985812","n185986157","n185960794","n185978784","n185975351","n185966957","n185986158"]},"w17967764":{"id":"w17967764","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n185984017","n185964351"]},"w170848329":{"id":"w170848329","tags":{"leisure":"park","name":"LaFayette Park"},"nodes":["n1819849180","n1819849115","n1819848885","n1819848921","n1819849180"]},"w17967208":{"id":"w17967208","tags":{"highway":"residential","name":"West Bennett Street"},"nodes":["n185978390","n2208608795","n185984020","n185964353","n185984281"]},"w17965349":{"id":"w17965349","tags":{"highway":"residential","name":"E Prutzman St"},"nodes":["n185966937","n2208608827","n185965289"]},"w209717049":{"id":"w209717049","tags":{"area":"yes","building":"yes"},"nodes":["n2199109761","n2199109762","n2199109763","n2199109765","n2199109768","n2199109770","n2199109783","n2199109785","n2199109761"]},"w203841840":{"id":"w203841840","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493840","n2138493841","n2138493842","n2138493843","n2138493840"]},"w209717043":{"id":"w209717043","tags":{"amenity":"place_of_worship","area":"yes","building":"church","denomination":"methodist","name":"First United Methodist Church","religion":"christian"},"nodes":["n2199109640","n2199109642","n2199109645","n2199109648","n2199109650","n2199109652","n2199109654","n2199109656","n2199109658","n2199109660","n2199109662","n2199109665","n2199109667","n2199109669","n2199109671","n2199109673","n2199109640"]},"w201484341":{"id":"w201484341","tags":{"amenity":"school","name":"Hoppin School"},"nodes":["n354002838","n2114807579","n2114807573","n2114807590","n354002838"]},"w209717046":{"id":"w209717046","tags":{"area":"yes","building":"yes"},"nodes":["n2199109723","n2199109725","n2199109727","n2199109729","n2199109731","n2199109733","n2199109735","n2199109737","n2199109723"]},"w210822777":{"id":"w210822777","tags":{"amenity":"parking","area":"yes"},"nodes":["n2208608806","n2208608808","n2208608811","n2208608821","n2208608823","n2208608825","n2208608806"]},"n185954965":{"id":"n185954965","loc":[-85.6191189,41.9441922]},"n185954968":{"id":"n185954968","loc":[-85.6194384,41.9442405]},"n185954970":{"id":"n185954970","loc":[-85.6196543,41.9443252]},"n185954972":{"id":"n185954972","loc":[-85.6197862,41.9444539]},"n354002931":{"id":"n354002931","loc":[-85.6198991,41.9455269]},"n354030853":{"id":"n354030853","loc":[-85.6219444,41.9455556],"tags":{"amenity":"place_of_worship","name":"Grant Chapel","religion":"christian"}},"n367815963":{"id":"n367815963","loc":[-85.6202778,41.9461111],"tags":{"building":"yes","name":"George Washington Carver Community Center"}},"n185947331":{"id":"n185947331","loc":[-85.618779,41.943269]},"n185947333":{"id":"n185947333","loc":[-85.618795,41.943511]},"n185947336":{"id":"n185947336","loc":[-85.618711,41.94413]},"n185947338":{"id":"n185947338","loc":[-85.618704,41.944189]},"n185947339":{"id":"n185947339","loc":[-85.618597,41.944337]},"n185947340":{"id":"n185947340","loc":[-85.618485,41.944528]},"n185947343":{"id":"n185947343","loc":[-85.618442,41.944716]},"n185947345":{"id":"n185947345","loc":[-85.618457,41.945107]},"n185947347":{"id":"n185947347","loc":[-85.618296,41.945338]},"n185947374":{"id":"n185947374","loc":[-85.616748,41.944453]},"n185947375":{"id":"n185947375","loc":[-85.616813,41.944646]},"n185947376":{"id":"n185947376","loc":[-85.616859,41.945196]},"n185947377":{"id":"n185947377","loc":[-85.616941,41.945352]},"n185947406":{"id":"n185947406","loc":[-85.618184,41.944227]},"n185947409":{"id":"n185947409","loc":[-85.617911,41.943875]},"n185947410":{"id":"n185947410","loc":[-85.617579,41.943682]},"n185947411":{"id":"n185947411","loc":[-85.61713,41.943589]},"n185947412":{"id":"n185947412","loc":[-85.616549,41.943559]},"n185947414":{"id":"n185947414","loc":[-85.616482,41.943556]},"n185947464":{"id":"n185947464","loc":[-85.616526,41.943788]},"n185947466":{"id":"n185947466","loc":[-85.616504,41.944002]},"n185948863":{"id":"n185948863","loc":[-85.619017,41.943391]},"n185948865":{"id":"n185948865","loc":[-85.619059,41.943368]},"n185955022":{"id":"n185955022","loc":[-85.620088,41.945571]},"n185955025":{"id":"n185955025","loc":[-85.620051,41.945505]},"n185955028":{"id":"n185955028","loc":[-85.62001,41.94541]},"n185980371":{"id":"n185980371","loc":[-85.620982,41.944742]},"n185980398":{"id":"n185980398","loc":[-85.621305,41.944782]},"n185980401":{"id":"n185980401","loc":[-85.621174,41.944819]},"n185980403":{"id":"n185980403","loc":[-85.621029,41.944871]},"n185980405":{"id":"n185980405","loc":[-85.620741,41.945011]},"n185980407":{"id":"n185980407","loc":[-85.620616,41.945085]},"n185980409":{"id":"n185980409","loc":[-85.620506,41.945172]},"n185980411":{"id":"n185980411","loc":[-85.620394,41.945273]},"n185980413":{"id":"n185980413","loc":[-85.620316,41.94536]},"n185980415":{"id":"n185980415","loc":[-85.620257,41.945452]},"n185980417":{"id":"n185980417","loc":[-85.620212,41.945535]},"n185985910":{"id":"n185985910","loc":[-85.620101,41.945811]},"n185985912":{"id":"n185985912","loc":[-85.620081,41.945937]},"n1475283972":{"id":"n1475283972","loc":[-85.6198991,41.9437179]},"n1475283982":{"id":"n1475283982","loc":[-85.6195022,41.9433463]},"n1475284007":{"id":"n1475284007","loc":[-85.6193037,41.9433383]},"n1475284040":{"id":"n1475284040","loc":[-85.6197329,41.9434121]},"n1475284044":{"id":"n1475284044","loc":[-85.6198756,41.9435363]},"n1475284050":{"id":"n1475284050","loc":[-85.6199689,41.9432106]},"n1475284053":{"id":"n1475284053","loc":[-85.6198943,41.9432921]},"n185954974":{"id":"n185954974","loc":[-85.6198296,41.94473]},"n185954977":{"id":"n185954977","loc":[-85.6200474,41.9447384]},"n2196831365":{"id":"n2196831365","loc":[-85.6202259,41.9460883]},"n2196831366":{"id":"n2196831366","loc":[-85.6202245,41.9458642]},"n2196831367":{"id":"n2196831367","loc":[-85.6205184,41.9458631]},"n2196831368":{"id":"n2196831368","loc":[-85.6205189,41.9459437]},"n2196831369":{"id":"n2196831369","loc":[-85.6203879,41.9459441]},"n2196831370":{"id":"n2196831370","loc":[-85.6203888,41.9460878]},"n2196831371":{"id":"n2196831371","loc":[-85.6184046,41.9465663]},"n2196831372":{"id":"n2196831372","loc":[-85.6191563,41.9465618]},"n2196831373":{"id":"n2196831373","loc":[-85.6191536,41.946319]},"n2196831374":{"id":"n2196831374","loc":[-85.6187356,41.9463216]},"n2196831375":{"id":"n2196831375","loc":[-85.6187334,41.9461197]},"n2196831376":{"id":"n2196831376","loc":[-85.6193167,41.9461162]},"n2196831377":{"id":"n2196831377","loc":[-85.6193156,41.9460229]},"n2196831378":{"id":"n2196831378","loc":[-85.619622,41.946021]},"n2196831379":{"id":"n2196831379","loc":[-85.6196237,41.9461712]},"n2196831380":{"id":"n2196831380","loc":[-85.6197702,41.9461703]},"n2196831381":{"id":"n2196831381","loc":[-85.6197685,41.9460202]},"n2196831382":{"id":"n2196831382","loc":[-85.6197323,41.9460204]},"n2196831383":{"id":"n2196831383","loc":[-85.6197305,41.9458563]},"n2196831384":{"id":"n2196831384","loc":[-85.6196165,41.945857]},"n2196831385":{"id":"n2196831385","loc":[-85.6196156,41.9457764]},"n2196831386":{"id":"n2196831386","loc":[-85.6194472,41.9457775]},"n2196831387":{"id":"n2196831387","loc":[-85.6194151,41.9457777]},"n2196831388":{"id":"n2196831388","loc":[-85.6183779,41.9457883]},"n2196831389":{"id":"n2196831389","loc":[-85.6183842,41.9461317]},"n2196831390":{"id":"n2196831390","loc":[-85.6185026,41.9461304]},"n2196831391":{"id":"n2196831391","loc":[-85.6185061,41.9463194]},"n2196831392":{"id":"n2196831392","loc":[-85.6184001,41.9463205]},"n2196831393":{"id":"n2196831393","loc":[-85.6182482,41.9464163]},"n2196831394":{"id":"n2196831394","loc":[-85.6182467,41.9463193]},"n2196831395":{"id":"n2196831395","loc":[-85.6180389,41.946321]},"n2196831397":{"id":"n2196831397","loc":[-85.6180404,41.946418]},"n185947303":{"id":"n185947303","loc":[-85.611074,41.943389]},"n185947304":{"id":"n185947304","loc":[-85.611332,41.943267]},"n185947305":{"id":"n185947305","loc":[-85.611635,41.943218]},"n185947306":{"id":"n185947306","loc":[-85.612762,41.943311]},"n185947308":{"id":"n185947308","loc":[-85.613027,41.943327]},"n185947310":{"id":"n185947310","loc":[-85.615377,41.942996]},"n185947312":{"id":"n185947312","loc":[-85.615701,41.943007]},"n185947314":{"id":"n185947314","loc":[-85.61604,41.943067]},"n185947315":{"id":"n185947315","loc":[-85.61626,41.943083]},"n185947316":{"id":"n185947316","loc":[-85.616507,41.943048]},"n185947319":{"id":"n185947319","loc":[-85.616702,41.94299]},"n185947321":{"id":"n185947321","loc":[-85.617078,41.942918]},"n185947322":{"id":"n185947322","loc":[-85.617366,41.942973]},"n185947323":{"id":"n185947323","loc":[-85.617601,41.943033]},"n185947325":{"id":"n185947325","loc":[-85.617799,41.943027]},"n185947327":{"id":"n185947327","loc":[-85.618264,41.942961]},"n185947328":{"id":"n185947328","loc":[-85.618508,41.942972]},"n185947329":{"id":"n185947329","loc":[-85.618707,41.943076]},"n185947361":{"id":"n185947361","loc":[-85.615356,41.944922]},"n185947363":{"id":"n185947363","loc":[-85.61536,41.944893]},"n185947365":{"id":"n185947365","loc":[-85.615406,41.944547]},"n185947367":{"id":"n185947367","loc":[-85.61548,41.944351]},"n185947369":{"id":"n185947369","loc":[-85.615805,41.94419]},"n185947371":{"id":"n185947371","loc":[-85.616166,41.944156]},"n185947373":{"id":"n185947373","loc":[-85.616411,41.944197]},"n185947416":{"id":"n185947416","loc":[-85.616335,41.94343]},"n185947417":{"id":"n185947417","loc":[-85.616069,41.943293]},"n185947419":{"id":"n185947419","loc":[-85.615803,41.943249]},"n185947420":{"id":"n185947420","loc":[-85.615524,41.943342]},"n185947421":{"id":"n185947421","loc":[-85.615311,41.94353]},"n185947422":{"id":"n185947422","loc":[-85.614338,41.943558]},"n185947423":{"id":"n185947423","loc":[-85.61422,41.94369]},"n185947425":{"id":"n185947425","loc":[-85.614221,41.944224]},"n185947427":{"id":"n185947427","loc":[-85.614198,41.944888]},"n185947429":{"id":"n185947429","loc":[-85.614221,41.945439]},"n185947468":{"id":"n185947468","loc":[-85.615908,41.944756]},"n185947470":{"id":"n185947470","loc":[-85.615871,41.944888]},"n185947472":{"id":"n185947472","loc":[-85.615878,41.94507]},"n185955153":{"id":"n185955153","loc":[-85.620087,41.947701]},"n185960690":{"id":"n185960690","loc":[-85.620141,41.951901]},"n185978817":{"id":"n185978817","loc":[-85.617193,41.954706]},"n185985916":{"id":"n185985916","loc":[-85.620088,41.94758]},"n185985918":{"id":"n185985918","loc":[-85.620133,41.951538]},"n185985919":{"id":"n185985919","loc":[-85.62013,41.952104]},"n185985920":{"id":"n185985920","loc":[-85.620104,41.952305]},"n185985921":{"id":"n185985921","loc":[-85.620062,41.952499]},"n185985922":{"id":"n185985922","loc":[-85.619993,41.952702]},"n185985925":{"id":"n185985925","loc":[-85.619879,41.952986]},"n185985927":{"id":"n185985927","loc":[-85.619689,41.95329]},"n185985928":{"id":"n185985928","loc":[-85.619508,41.953521]},"n185985929":{"id":"n185985929","loc":[-85.619286,41.953728]},"n185985930":{"id":"n185985930","loc":[-85.618925,41.954007]},"n185985931":{"id":"n185985931","loc":[-85.618638,41.954189]},"n185985932":{"id":"n185985932","loc":[-85.61831,41.954358]},"n185985934":{"id":"n185985934","loc":[-85.618015,41.954485]},"n185985936":{"id":"n185985936","loc":[-85.617606,41.954611]},"n1475283975":{"id":"n1475283975","loc":[-85.6150935,41.9434118]},"n1475283979":{"id":"n1475283979","loc":[-85.6193367,41.9430252]},"n1475283989":{"id":"n1475283989","loc":[-85.6104771,41.9455269]},"n1475283990":{"id":"n1475283990","loc":[-85.6104771,41.9437179]},"n1475283994":{"id":"n1475283994","loc":[-85.6198042,41.9429763]},"n1475283998":{"id":"n1475283998","loc":[-85.6192101,41.9426716]},"n1475284000":{"id":"n1475284000","loc":[-85.6198622,41.942836]},"n1475284002":{"id":"n1475284002","loc":[-85.6163262,41.9427688]},"n1475284006":{"id":"n1475284006","loc":[-85.6179527,41.9429168]},"n1475284029":{"id":"n1475284029","loc":[-85.6197195,41.9427278]},"n1475284038":{"id":"n1475284038","loc":[-85.6194405,41.9427837]},"n1475284052":{"id":"n1475284052","loc":[-85.6153225,41.942841]},"n1475284055":{"id":"n1475284055","loc":[-85.6129233,41.9437179]},"n2139966627":{"id":"n2139966627","loc":[-85.61958,41.9427558]},"w17966773":{"id":"w17966773","tags":{"highway":"secondary","name":"E Michigan Ave","ref":"M 60"},"nodes":["n185980372","n185980398","n185980401","n185980403","n185980405","n185980407","n185980409","n185980411","n185980413","n185980415","n185980417","n185955019"]},"w17964043":{"id":"w17964043","tags":{"highway":"residential"},"nodes":["n185955019","n185955022","n185955025","n185955028","n185954977","n185971477","n1475284050","n1475284000","n1475284029","n2139966627","n1475284038"]},"w17962834":{"id":"w17962834","tags":{"highway":"service"},"nodes":["n185947316","n185947414","n185947464","n185947466","n185947373","n185947468","n185947470","n185947472","n185947474"]},"w209470310":{"id":"w209470310","tags":{"area":"yes","building":"yes"},"nodes":["n2196831393","n2196831394","n2196831395","n2196831397","n2196831393"]},"w17963058":{"id":"w17963058","tags":{"highway":"service"},"nodes":["n185947333","n185948863","n185948865","n1475284007","n1475283982","n1475284040","n1475284044"]},"w17962823":{"id":"w17962823","tags":{"highway":"service"},"nodes":["n185947359","n185947361","n185947363","n185947365","n185947367","n185947369","n185947371","n185947373","n185947374","n185947375","n185947376","n185947377","n185947378"]},"w17962821":{"id":"w17962821","tags":{"highway":"service"},"nodes":["n185947303","n185947304","n185947305","n185947306","n185947308","n185947310","n185947312","n185947314","n185947315","n185947316","n185947319","n185947321","n185947322","n185947323","n185947325","n185947327","n185947328","n185947329","n185947331","n185947333","n185947336","n185947338","n185947339","n185947340","n185947343","n185947345","n185947347","n185947349"]},"w134150798":{"id":"w134150798","tags":{"amenity":"grave_yard","name":"Riverside Cemetery"},"nodes":["n354002931","n1475283972","n1475284053","n1475283994","n1475283979","n1475283998","n1475284006","n1475284002","n1475284052","n1475283975","n1475284055","n1475283990","n1475283989","n354002931"]},"w17964040":{"id":"w17964040","tags":{"highway":"service"},"nodes":["n185947336","n185954965","n185954968","n185954970","n185954972","n185954974","n185954977"]},"w209470308":{"id":"w209470308","tags":{"area":"yes","building":"yes"},"nodes":["n2196831365","n2196831366","n2196831367","n2196831368","n2196831369","n2196831370","n2196831365"]},"w17962828":{"id":"w17962828","tags":{"highway":"service"},"nodes":["n185947340","n185947406","n185947409","n185947410","n185947411","n185947412","n185947414","n185947416","n185947417","n185947419","n185947420","n185947421","n185947422","n185947423","n185947425","n185947427","n185947429"]},"w209470309":{"id":"w209470309","tags":{"area":"yes","building":"yes"},"nodes":["n2196831371","n2196831372","n2196831373","n2196831374","n2196831375","n2196831376","n2196831377","n2196831378","n2196831379","n2196831380","n2196831381","n2196831382","n2196831383","n2196831384","n2196831385","n2196831386","n2196831387","n2196831388","n2196831389","n2196831390","n2196831391","n2196831392","n2196831371"]},"w17967415":{"id":"w17967415","tags":{"highway":"secondary","name":"Jefferson St","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185955019","n185985910","n185985912","n185985914","n185961367","n185985916","n185955153","n185965105","n185974697","n185955120","n185985918","n185960690","n185985919","n185985920","n185985921","n185985922","n185985925","n185985927","n185985928","n185985929","n185985930","n185985931","n185985932","n185985934","n185985936","n185978817"]},"w17966772":{"id":"w17966772","tags":{"highway":"unclassified","name":"E Michigan Ave","name_1":"State Highway 60"},"nodes":["n185954977","n185980371","n185980372"]},"n185958500":{"id":"n185958500","loc":[-85.621591,41.941075]},"n185963110":{"id":"n185963110","loc":[-85.6204416,41.9408882]},"n2139966628":{"id":"n2139966628","loc":[-85.6196431,41.9426467],"tags":{"leisure":"fishing"}},"n2139966630":{"id":"n2139966630","loc":[-85.6199354,41.9429616]},"n2199127051":{"id":"n2199127051","loc":[-85.6170556,41.939696]},"n2199127052":{"id":"n2199127052","loc":[-85.6170536,41.9392909]},"n2199127053":{"id":"n2199127053","loc":[-85.6172067,41.9392905]},"n2199127054":{"id":"n2199127054","loc":[-85.6172061,41.9391853]},"n2199127055":{"id":"n2199127055","loc":[-85.6171481,41.9391854]},"n2199127060":{"id":"n2199127060","loc":[-85.6167389,41.9392896]},"n2199127061":{"id":"n2199127061","loc":[-85.6168728,41.9392892]},"n2199127062":{"id":"n2199127062","loc":[-85.6168747,41.9396965]},"n2199127071":{"id":"n2199127071","loc":[-85.620196,41.9399446]},"n2199127072":{"id":"n2199127072","loc":[-85.620193,41.9397316]},"n2199127073":{"id":"n2199127073","loc":[-85.6200381,41.9397328]},"n2199127074":{"id":"n2199127074","loc":[-85.6200412,41.9399458]},"n2199127075":{"id":"n2199127075","loc":[-85.6203606,41.9399939]},"n2199127076":{"id":"n2199127076","loc":[-85.6205527,41.9399922]},"n2199127077":{"id":"n2199127077","loc":[-85.6205482,41.9397115]},"n2199127078":{"id":"n2199127078","loc":[-85.6204132,41.9397124]},"n2199127079":{"id":"n2199127079","loc":[-85.6204144,41.9396341]},"n2199127080":{"id":"n2199127080","loc":[-85.6205699,41.9396324]},"n2199127081":{"id":"n2199127081","loc":[-85.6205722,41.939498]},"n2199127082":{"id":"n2199127082","loc":[-85.6204064,41.9394997]},"n2199127083":{"id":"n2199127083","loc":[-85.6204087,41.939561]},"n2199127084":{"id":"n2199127084","loc":[-85.6203103,41.9395618]},"n2199127085":{"id":"n2199127085","loc":[-85.620308,41.9396069]},"n2199127086":{"id":"n2199127086","loc":[-85.6200347,41.9396086]},"n2199127087":{"id":"n2199127087","loc":[-85.6200382,41.9397141]},"n2199127088":{"id":"n2199127088","loc":[-85.6202257,41.9397149]},"n2199127089":{"id":"n2199127089","loc":[-85.6202269,41.9399182]},"n2199127090":{"id":"n2199127090","loc":[-85.6203595,41.9399199]},"n2199127091":{"id":"n2199127091","loc":[-85.6212335,41.939688]},"n2199127092":{"id":"n2199127092","loc":[-85.6212328,41.939595]},"n2199127093":{"id":"n2199127093","loc":[-85.6208807,41.9395966]},"n2199127094":{"id":"n2199127094","loc":[-85.6208815,41.9396896]},"n2199127095":{"id":"n2199127095","loc":[-85.6208676,41.9396872]},"n2199127096":{"id":"n2199127096","loc":[-85.6208583,41.9393539]},"n2199127097":{"id":"n2199127097","loc":[-85.6207006,41.9393563]},"n2199127098":{"id":"n2199127098","loc":[-85.6207099,41.9396896]},"n185967054":{"id":"n185967054","loc":[-85.6173384,41.9356126]},"n185967063":{"id":"n185967063","loc":[-85.617371,41.936243]},"n185967065":{"id":"n185967065","loc":[-85.617337,41.936299]},"n185967068":{"id":"n185967068","loc":[-85.617321,41.936373]},"n185967070":{"id":"n185967070","loc":[-85.6173562,41.9366969]},"n185967074":{"id":"n185967074","loc":[-85.6173635,41.9377414]},"n185967075":{"id":"n185967075","loc":[-85.6173696,41.9381886]},"n185967076":{"id":"n185967076","loc":[-85.617372,41.938535]},"n2199127056":{"id":"n2199127056","loc":[-85.617147,41.9389616]},"n2199127057":{"id":"n2199127057","loc":[-85.6172136,41.9389614]},"n2199127058":{"id":"n2199127058","loc":[-85.6172123,41.9386909]},"n2199127059":{"id":"n2199127059","loc":[-85.616736,41.9386922]},"n2203921041":{"id":"n2203921041","loc":[-85.6173018,41.9346369]},"w203983952":{"id":"w203983952","tags":{"highway":"service"},"nodes":["n2139966627","n1819800319"]},"w209718301":{"id":"w209718301","tags":{"area":"yes","building":"yes"},"nodes":["n2199127051","n2199127052","n2199127053","n2199127054","n2199127055","n2199127056","n2199127057","n2199127058","n2199127059","n2199127060","n2199127061","n2199127062","n2199127051"]},"w209718304":{"id":"w209718304","tags":{"area":"yes","building":"yes"},"nodes":["n2199127071","n2199127072","n2199127073","n2199127074","n2199127071"]},"w17964961":{"id":"w17964961","tags":{"highway":"residential","name":"Whipple St"},"nodes":["n185963099","n185963110"]},"w17964489":{"id":"w17964489","tags":{"highway":"residential","name":"Jackson St"},"nodes":["n185958498","n185958500"]},"w203983953":{"id":"w203983953","tags":{"area":"yes","leisure":"park","name":"Marina Park"},"nodes":["n1475283994","n1475283979","n1475283998","n2139966629","n2139966625","n1819800319","n2139966623","n2139966622","n2139966621","n2139966630","n1475283994"]},"w17965366":{"id":"w17965366","tags":{"highway":"residential","name":"14th St"},"nodes":["n2203921041","n185967054","n185967063","n185967065","n185967068","n185967070","n185967074","n185967075","n185967076","n185967077"]},"w209718306":{"id":"w209718306","tags":{"area":"yes","building":"yes"},"nodes":["n2199127091","n2199127092","n2199127093","n2199127094","n2199127091"]},"w209718307":{"id":"w209718307","tags":{"area":"yes","building":"yes"},"nodes":["n2199127095","n2199127096","n2199127097","n2199127098","n2199127095"]},"w209718305":{"id":"w209718305","tags":{"area":"yes","building":"yes"},"nodes":["n2199127075","n2199127076","n2199127077","n2199127078","n2199127079","n2199127080","n2199127081","n2199127082","n2199127083","n2199127084","n2199127085","n2199127086","n2199127087","n2199127088","n2199127089","n2199127090","n2199127075"]},"n185960199":{"id":"n185960199","loc":[-85.62965,41.95469]},"n185980737":{"id":"n185980737","loc":[-85.629083,41.953725]},"n2114807561":{"id":"n2114807561","loc":[-85.6297681,41.9524688]},"n2114807597":{"id":"n2114807597","loc":[-85.6296517,41.952563]},"n185960197":{"id":"n185960197","loc":[-85.629676,41.9537314]},"n185978791":{"id":"n185978791","loc":[-85.6244542,41.9547066]},"w17967573":{"id":"w17967573","tags":{"highway":"residential","name":"E Wheeler St"},"nodes":["n185960195","n2114807561","n185968102","n185967430","n185986157","n185978392"]},"w17966553":{"id":"w17966553","tags":{"highway":"residential","name":"East Hoffman Street"},"nodes":["n185971631","n185978784","n185967432","n185968106","n185960199","n185978787","n185978790","n185978791"]},"w17966787":{"id":"w17966787","tags":{"highway":"residential","name":"East Cushman Street"},"nodes":["n185980735","n185980737","n185960197","n185968104","n185960792"]},"w17964723":{"id":"w17964723","tags":{"highway":"residential","name":"Cushman Street"},"nodes":["n185960792","n185960794","n185960796"]},"w17964654":{"id":"w17964654","tags":{"highway":"residential","name":"Pine Street"},"nodes":["n185960195","n2114807597","n185960197","n185960199"]},"n1819848862":{"id":"n1819848862","loc":[-85.6346087,41.9545845]},"n1819848935":{"id":"n1819848935","loc":[-85.6345948,41.9537717]},"n1819848973":{"id":"n1819848973","loc":[-85.6334247,41.9537827]},"n1819848997":{"id":"n1819848997","loc":[-85.6334386,41.9545956]},"n2189015861":{"id":"n2189015861","loc":[-85.6375906,41.954836]},"n2189015865":{"id":"n2189015865","loc":[-85.6383307,41.9548291]},"n2189015867":{"id":"n2189015867","loc":[-85.6383337,41.9550072]},"n2189015868":{"id":"n2189015868","loc":[-85.6380986,41.9550094]},"n2189015869":{"id":"n2189015869","loc":[-85.6381005,41.9551226]},"n2199109808":{"id":"n2199109808","loc":[-85.6372702,41.9522894]},"n2199109810":{"id":"n2199109810","loc":[-85.6372677,41.9521583]},"n2199109812":{"id":"n2199109812","loc":[-85.6369505,41.9521617]},"n2199109814":{"id":"n2199109814","loc":[-85.636953,41.9522927]},"n185952156":{"id":"n185952156","loc":[-85.640983,41.9546557]},"n185953423":{"id":"n185953423","loc":[-85.641871,41.954652]},"n185971637":{"id":"n185971637","loc":[-85.641583,41.95465]},"n185971639":{"id":"n185971639","loc":[-85.6421344,41.9546444]},"n185971642":{"id":"n185971642","loc":[-85.6428264,41.9545612]},"n185971648":{"id":"n185971648","loc":[-85.6436023,41.9544262]},"n185975066":{"id":"n185975066","loc":[-85.640532,41.953638]},"n185975067":{"id":"n185975067","loc":[-85.64079,41.953638]},"n185982166":{"id":"n185982166","loc":[-85.6399012,41.9523817]},"n2189015858":{"id":"n2189015858","loc":[-85.6376104,41.9560138]},"n2189015870":{"id":"n2189015870","loc":[-85.6386794,41.9551172]},"n2189015871":{"id":"n2189015871","loc":[-85.6386817,41.955256]},"n2189015873":{"id":"n2189015873","loc":[-85.6385437,41.9552573]},"n2189015876":{"id":"n2189015876","loc":[-85.638555,41.9559278]},"n2189015879":{"id":"n2189015879","loc":[-85.6384954,41.9559283]},"n2189015882":{"id":"n2189015882","loc":[-85.6384965,41.9559935]},"n2189015885":{"id":"n2189015885","loc":[-85.6383533,41.9559949]},"n2189015888":{"id":"n2189015888","loc":[-85.638351,41.9558607]},"n2189015891":{"id":"n2189015891","loc":[-85.6382178,41.9558619]},"n2189015894":{"id":"n2189015894","loc":[-85.6382203,41.956008]},"w208627223":{"id":"w208627223","tags":{"area":"yes","building":"yes"},"nodes":["n2189015858","n2189015861","n2189015865","n2189015867","n2189015868","n2189015869","n2189015870","n2189015871","n2189015873","n2189015876","n2189015879","n2189015882","n2189015885","n2189015888","n2189015891","n2189015894","n2189015858"]},"w170848328":{"id":"w170848328","tags":{"leisure":"park","name":"Bowman Park"},"nodes":["n1819848935","n1819848973","n1819848997","n1819848862","n1819848935"]},"w17965866":{"id":"w17965866","tags":{"highway":"residential","name":"West Hoffman Street"},"nodes":["n185971631","n185971632","n185964359","n185965025","n1475293264","n185952156","n185971637","n185953423","n185971639","n185971642","n185971648"]},"w209717051":{"id":"w209717051","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"baptist","name":"Calvary Missionary Baptist Church","religion":"christian"},"nodes":["n2199109808","n2199109810","n2199109812","n2199109814","n2199109808"]},"w17966172":{"id":"w17966172","tags":{"highway":"residential","name":"West Cushman Street"},"nodes":["n185960796","n185975064","n185964358","n185965023","n1475293222","n185975066","n185975067"]},"w17966975":{"id":"w17966975","tags":{"highway":"residential","name":"W Wheeler St"},"nodes":["n185978392","n185982163","n185964357","n185965021","n1475293261","n185982166"]},"n185960684":{"id":"n185960684","loc":[-85.622687,41.951885]},"n185960686":{"id":"n185960686","loc":[-85.622492,41.951901]},"n185978795":{"id":"n185978795","loc":[-85.6240991,41.954708]},"n185978803":{"id":"n185978803","loc":[-85.623348,41.954547]},"n185978806":{"id":"n185978806","loc":[-85.623123,41.954502]},"n185978808":{"id":"n185978808","loc":[-85.622923,41.954469]},"n185978810":{"id":"n185978810","loc":[-85.622787,41.954457]},"n185978811":{"id":"n185978811","loc":[-85.622612,41.954458]},"n185978813":{"id":"n185978813","loc":[-85.622368,41.954472]},"n1819790545":{"id":"n1819790545","loc":[-85.6240295,41.9548949]},"n1819790621":{"id":"n1819790621","loc":[-85.6235789,41.954855]},"n1819790664":{"id":"n1819790664","loc":[-85.6238363,41.9549507]},"n1819790683":{"id":"n1819790683","loc":[-85.6224727,41.9545921]},"n1819790730":{"id":"n1819790730","loc":[-85.6227527,41.9545795]},"n1819790740":{"id":"n1819790740","loc":[-85.6240402,41.9550784]},"n1819790831":{"id":"n1819790831","loc":[-85.624126,41.9549986]},"n1819790861":{"id":"n1819790861","loc":[-85.6231712,41.9546872]},"n1819790887":{"id":"n1819790887","loc":[-85.6242762,41.955206]},"n2168544739":{"id":"n2168544739","loc":[-85.6249102,41.952801]},"n2168544740":{"id":"n2168544740","loc":[-85.6251859,41.9527564]},"n2168544741":{"id":"n2168544741","loc":[-85.6255515,41.9527921]},"n2168544742":{"id":"n2168544742","loc":[-85.626001,41.9529481]},"n2168544743":{"id":"n2168544743","loc":[-85.6265284,41.9529838]},"n2168544744":{"id":"n2168544744","loc":[-85.626942,41.9528857]},"n2168544745":{"id":"n2168544745","loc":[-85.6270918,41.9526851]},"n2168544746":{"id":"n2168544746","loc":[-85.6272117,41.95244]},"n2168544747":{"id":"n2168544747","loc":[-85.6271578,41.952226]},"n2168544748":{"id":"n2168544748","loc":[-85.6270019,41.9519719]},"n2168544749":{"id":"n2168544749","loc":[-85.6268221,41.9518382]},"n2168544750":{"id":"n2168544750","loc":[-85.6265284,41.951807]},"n2168544751":{"id":"n2168544751","loc":[-85.6256534,41.9518516]},"n2168544752":{"id":"n2168544752","loc":[-85.6253477,41.9518338]},"n2168544753":{"id":"n2168544753","loc":[-85.6251139,41.9517669]},"n185955747":{"id":"n185955747","loc":[-85.620674,41.954709]},"n185960688":{"id":"n185960688","loc":[-85.621032,41.951913]},"n185972054":{"id":"n185972054","loc":[-85.6186728,41.9547335]},"n185978814":{"id":"n185978814","loc":[-85.6201708,41.9547403]},"n1819790532":{"id":"n1819790532","loc":[-85.6244908,41.9555731]},"n1819790536":{"id":"n1819790536","loc":[-85.6217925,41.9583135]},"n1819790538":{"id":"n1819790538","loc":[-85.6233954,41.9600014]},"n1819790539":{"id":"n1819790539","loc":[-85.6204611,41.9562117]},"n1819790546":{"id":"n1819790546","loc":[-85.6210898,41.9567657]},"n1819790548":{"id":"n1819790548","loc":[-85.6202465,41.9562237]},"n1819790550":{"id":"n1819790550","loc":[-85.6250165,41.9560677]},"n1819790551":{"id":"n1819790551","loc":[-85.6227946,41.9597023]},"n1819790553":{"id":"n1819790553","loc":[-85.6215726,41.9584571]},"n1819790556":{"id":"n1819790556","loc":[-85.6196306,41.9573002]},"n1819790557":{"id":"n1819790557","loc":[-85.6209503,41.9563109]},"n1819790558":{"id":"n1819790558","loc":[-85.6196939,41.9574085]},"n1819790561":{"id":"n1819790561","loc":[-85.621079,41.957751]},"n1819790562":{"id":"n1819790562","loc":[-85.6224255,41.9611417]},"n1819790565":{"id":"n1819790565","loc":[-85.6232506,41.9604841]},"n1819790566":{"id":"n1819790566","loc":[-85.6190835,41.9562909]},"n1819790567":{"id":"n1819790567","loc":[-85.622227,41.9593028]},"n1819790569":{"id":"n1819790569","loc":[-85.620976,41.9591039]},"n1819790571":{"id":"n1819790571","loc":[-85.6212078,41.9565303]},"n1819790572":{"id":"n1819790572","loc":[-85.6235306,41.9595102]},"n1819790581":{"id":"n1819790581","loc":[-85.6235563,41.9579351]},"n1819790584":{"id":"n1819790584","loc":[-85.6230371,41.9574598]},"n1819790586":{"id":"n1819790586","loc":[-85.6211748,41.9564272]},"n1819790588":{"id":"n1819790588","loc":[-85.6226508,41.9601086]},"n1819790591":{"id":"n1819790591","loc":[-85.6218032,41.9607468]},"n1819790593":{"id":"n1819790593","loc":[-85.6207915,41.9618735]},"n1819790596":{"id":"n1819790596","loc":[-85.6252955,41.9567858]},"n1819790598":{"id":"n1819790598","loc":[-85.6196618,41.9568939]},"n1819790600":{"id":"n1819790600","loc":[-85.6224416,41.9587084]},"n1819790602":{"id":"n1819790602","loc":[-85.6217442,41.9558641]},"n1819790603":{"id":"n1819790603","loc":[-85.6213355,41.9592116]},"n1819790604":{"id":"n1819790604","loc":[-85.622801,41.9573042]},"n1819790608":{"id":"n1819790608","loc":[-85.6199729,41.9574325]},"n1819790610":{"id":"n1819790610","loc":[-85.6195555,41.9557165]},"n1819790611":{"id":"n1819790611","loc":[-85.622978,41.9586007]},"n1819790613":{"id":"n1819790613","loc":[-85.6253963,41.9562636]},"n1819790614":{"id":"n1819790614","loc":[-85.6235252,41.9580342]},"n1819790616":{"id":"n1819790616","loc":[-85.6232988,41.9596305]},"n1819790617":{"id":"n1819790617","loc":[-85.6226776,41.9598732]},"n1819790619":{"id":"n1819790619","loc":[-85.625553,41.9561794]},"n1819790620":{"id":"n1819790620","loc":[-85.6235574,41.959231]},"n1819790624":{"id":"n1819790624","loc":[-85.6228429,41.9573726]},"n1819790626":{"id":"n1819790626","loc":[-85.6193785,41.9556766]},"n1819790628":{"id":"n1819790628","loc":[-85.620092,41.9554253]},"n1819790630":{"id":"n1819790630","loc":[-85.6226658,41.9604402]},"n1819790638":{"id":"n1819790638","loc":[-85.6219964,41.9602561]},"n1819790640":{"id":"n1819790640","loc":[-85.6232731,41.9599969]},"n1819790643":{"id":"n1819790643","loc":[-85.6247698,41.9568895]},"n1819790650":{"id":"n1819790650","loc":[-85.6216412,41.9550149]},"n1819790652":{"id":"n1819790652","loc":[-85.6224952,41.9603918]},"n1819790656":{"id":"n1819790656","loc":[-85.61918,41.9555649]},"n1819790661":{"id":"n1819790661","loc":[-85.6200169,41.955505]},"n1819790662":{"id":"n1819790662","loc":[-85.6217389,41.9563149]},"n1819790666":{"id":"n1819790666","loc":[-85.6229566,41.9598373]},"n1819790667":{"id":"n1819790667","loc":[-85.6209117,41.9609189]},"n1819790669":{"id":"n1819790669","loc":[-85.6252311,41.9562353]},"n1819790670":{"id":"n1819790670","loc":[-85.6209758,41.961868]},"n1819790672":{"id":"n1819790672","loc":[-85.6209557,41.9589078]},"n1819790673":{"id":"n1819790673","loc":[-85.6190352,41.9561393]},"n1819790675":{"id":"n1819790675","loc":[-85.6236432,41.9586685]},"n1819790676":{"id":"n1819790676","loc":[-85.6194901,41.9565389]},"n1819790678":{"id":"n1819790678","loc":[-85.6219266,41.9582417]},"n1819790680":{"id":"n1819790680","loc":[-85.6208258,41.9557211]},"n1819790681":{"id":"n1819790681","loc":[-85.6212024,41.9613212]},"n1819790682":{"id":"n1819790682","loc":[-85.624877,41.9559401]},"n1819790684":{"id":"n1819790684","loc":[-85.6206499,41.9583693]},"n1819790699":{"id":"n1819790699","loc":[-85.6215243,41.956279]},"n1819790701":{"id":"n1819790701","loc":[-85.6246625,41.9559321]},"n1819790703":{"id":"n1819790703","loc":[-85.6230478,41.9585089]},"n1819790708":{"id":"n1819790708","loc":[-85.6211102,41.9575402]},"n1819790710":{"id":"n1819790710","loc":[-85.6215082,41.9548468]},"n1819790711":{"id":"n1819790711","loc":[-85.6206552,41.9586007]},"n1819790713":{"id":"n1819790713","loc":[-85.6215404,41.9549705]},"n1819790715":{"id":"n1819790715","loc":[-85.6216906,41.955521]},"n1819790717":{"id":"n1819790717","loc":[-85.6215404,41.9547391]},"n1819790722":{"id":"n1819790722","loc":[-85.6219964,41.9599131]},"n1819790723":{"id":"n1819790723","loc":[-85.622286,41.9606989]},"n1819790725":{"id":"n1819790725","loc":[-85.6228439,41.9572005]},"n1819790727":{"id":"n1819790727","loc":[-85.6202518,41.9554458]},"n1819790728":{"id":"n1819790728","loc":[-85.623434,41.9575276]},"n1819790729":{"id":"n1819790729","loc":[-85.6234287,41.9568576]},"n1819790732":{"id":"n1819790732","loc":[-85.6229566,41.9571369]},"n1819790733":{"id":"n1819790733","loc":[-85.6225543,41.9590275]},"n1819790734":{"id":"n1819790734","loc":[-85.6232892,41.9583135]},"n1819790736":{"id":"n1819790736","loc":[-85.622977,41.9608551]},"n1819790737":{"id":"n1819790737","loc":[-85.624008,41.9569533]},"n1819790741":{"id":"n1819790741","loc":[-85.6212775,41.9608545]},"n1819790742":{"id":"n1819790742","loc":[-85.6231282,41.9569932]},"n1819790743":{"id":"n1819790743","loc":[-85.6224523,41.9591831]},"n1819790744":{"id":"n1819790744","loc":[-85.6210951,41.9610819]},"n1819790745":{"id":"n1819790745","loc":[-85.6220114,41.960544]},"n1819790755":{"id":"n1819790755","loc":[-85.6216369,41.9553854]},"n1819790757":{"id":"n1819790757","loc":[-85.6209986,41.9592709]},"n1819790758":{"id":"n1819790758","loc":[-85.6200437,41.9563468]},"n1819790764":{"id":"n1819790764","loc":[-85.6219363,41.9596823]},"n1819790765":{"id":"n1819790765","loc":[-85.6237612,41.9568496]},"n1819790769":{"id":"n1819790769","loc":[-85.6212389,41.9593433]},"n1819790771":{"id":"n1819790771","loc":[-85.6210726,41.9560123]},"n1819790772":{"id":"n1819790772","loc":[-85.6212711,41.9561838]},"n1819790776":{"id":"n1819790776","loc":[-85.6234437,41.9577795]},"n1819790777":{"id":"n1819790777","loc":[-85.6212502,41.9618599]},"n1819790783":{"id":"n1819790783","loc":[-85.6216895,41.9610585]},"n1819790784":{"id":"n1819790784","loc":[-85.6200115,41.9556367]},"n1819790785":{"id":"n1819790785","loc":[-85.6210576,41.9573002]},"n1819790786":{"id":"n1819790786","loc":[-85.621138,41.9576632]},"n1819790788":{"id":"n1819790788","loc":[-85.6207733,41.9578946]},"n1819790789":{"id":"n1819790789","loc":[-85.6200705,41.9571566]},"n1819790790":{"id":"n1819790790","loc":[-85.6245337,41.9558443]},"n1819790792":{"id":"n1819790792","loc":[-85.621932,41.9608066]},"n1819790793":{"id":"n1819790793","loc":[-85.6233578,41.9581385]},"n1819790794":{"id":"n1819790794","loc":[-85.6204557,41.9555136]},"n1819790797":{"id":"n1819790797","loc":[-85.6235038,41.9576074]},"n1819790800":{"id":"n1819790800","loc":[-85.6214438,41.9607508]},"n1819790801":{"id":"n1819790801","loc":[-85.623492,41.9602129]},"n1819790802":{"id":"n1819790802","loc":[-85.6216691,41.9546553]},"n1819790803":{"id":"n1819790803","loc":[-85.6231057,41.9586851]},"n1819790804":{"id":"n1819790804","loc":[-85.6209224,41.9578673]},"n1819790813":{"id":"n1819790813","loc":[-85.620092,41.9572962]},"n1819790814":{"id":"n1819790814","loc":[-85.6216691,41.9552218]},"n1819790816":{"id":"n1819790816","loc":[-85.6216144,41.9609668]},"n1819790818":{"id":"n1819790818","loc":[-85.6216906,41.9557324]},"n1819790820":{"id":"n1819790820","loc":[-85.6192069,41.9564186]},"n1819790823":{"id":"n1819790823","loc":[-85.6211155,41.9566027]},"n1819790825":{"id":"n1819790825","loc":[-85.6233106,41.9569294]},"n1819790839":{"id":"n1819790839","loc":[-85.625671,41.9564986]},"n1819790842":{"id":"n1819790842","loc":[-85.6235252,41.9567379]},"n1819790844":{"id":"n1819790844","loc":[-85.6253813,41.9566342]},"n1819790847":{"id":"n1819790847","loc":[-85.6200963,41.9567702]},"n1819790849":{"id":"n1819790849","loc":[-85.6238031,41.9587449]},"n1819790851":{"id":"n1819790851","loc":[-85.6234984,41.9584571]},"n1819790856":{"id":"n1819790856","loc":[-85.6242226,41.9570092]},"n1819790865":{"id":"n1819790865","loc":[-85.6200265,41.9569458]},"n1819790869":{"id":"n1819790869","loc":[-85.6230049,41.9601245]},"n1819790871":{"id":"n1819790871","loc":[-85.6190727,41.9558322]},"n1819790873":{"id":"n1819790873","loc":[-85.6217442,41.9550104]},"n1819790875":{"id":"n1819790875","loc":[-85.6208044,41.9587808]},"n1819790879":{"id":"n1819790879","loc":[-85.6198444,41.9574484]},"n1819790883":{"id":"n1819790883","loc":[-85.623713,41.9588719]},"n1819790885":{"id":"n1819790885","loc":[-85.6223289,41.9605075]},"n1819790889":{"id":"n1819790889","loc":[-85.6208044,41.9562437]},"n1819790893":{"id":"n1819790893","loc":[-85.6218183,41.9559684]},"n1819790906":{"id":"n1819790906","loc":[-85.6214052,41.958697]},"n1819790913":{"id":"n1819790913","loc":[-85.6209981,41.9609957]},"n1819790917":{"id":"n1819790917","loc":[-85.6216208,41.9604436]},"n1819790919":{"id":"n1819790919","loc":[-85.6209406,41.9616373]},"n1819790920":{"id":"n1819790920","loc":[-85.6221948,41.9583334]},"n1819790922":{"id":"n1819790922","loc":[-85.6216681,41.9615292]},"n1819790924":{"id":"n1819790924","loc":[-85.6210147,41.9570489]},"n1819790929":{"id":"n1819790929","loc":[-85.6193678,41.955521]},"w17964707":{"id":"w17964707","tags":{"highway":"residential","name":"11th Ave"},"nodes":["n185960682","n185960684","n185960686","n185960688","n185960690"]},"w201484345":{"id":"w201484345","tags":{"bridge":"yes","highway":"residential","name":"E Hoffman St"},"nodes":["n185978791","n185978795"]},"w201484348":{"id":"w201484348","tags":{"highway":"residential","name":"E Hoffman St"},"nodes":["n185978795","n185978800","n185978803","n185978806","n185978808","n185978810","n185978811","n185978813","n185955747","n185978814","n185972054","n185978817"]},"w170843845":{"id":"w170843845","tags":{"landuse":"reservoir","name":"Hoffman Pond","natural":"water"},"nodes":["n1819790732","n1819790742","n1819790825","n1819790729","n1819790842","n1819790765","n1819790737","n1819790856","n1819790643","n1819790596","n1819790844","n1819790839","n1819849190","n1819790619","n1819790613","n1819790669","n1819790550","n1819790682","n1819790701","n1819790790","n1819790532","n1819790887","n1819790740","n1819790831","n1819790545","n1819790664","n1819790621","n1819790861","n1819790730","n1819790683","n1819790802","n1819790717","n1819790710","n1819790713","n1819790650","n1819790873","n1819790814","n1819790755","n1819790715","n1819790818","n1819790602","n1819790893","n1819790662","n1819790699","n1819790772","n1819790771","n1819790680","n1819790794","n1819790727","n1819790628","n1819790661","n1819790784","n1819790610","n1819790626","n1819790929","n1819790656","n1819790871","n1819790673","n1819790566","n1819790820","n1819790676","n1819790598","n1819790556","n1819790558","n1819790879","n1819790608","n1819790813","n1819790789","n1819790865","n1819790847","n1819790758","n1819790548","n1819790539","n1819790889","n1819790557","n1819790586","n1819790571","n1819790823","n1819790546","n1819790924","n1819790785","n1819790708","n1819790786","n1819790561","n1819790804","n1819790788","n1819790684","n1819790711","n1819790875","n1819790672","n1819790569","n1819790757","n1819790769","n1819790603","n1819790906","n1819790553","n1819790536","n1819790678","n1819790920","n1819790600","n1819790733","n1819790743","n1819790567","n1819790764","n1819790722","n1819790638","n1819790917","n1819790800","n1819790741","n1819790667","n1819790913","n1819790744","n1819790816","n1819790591","n1819790745","n1819790885","n1819790652","n1819790588","n1819790617","n1819790551","n1819790666","n1819790869","n1819790630","n1819790723","n1819790792","n1819790783","n1819790681","n1819790919","n1819790593","n1819790670","n1819790777","n1819790922","n1819790562","n1819790736","n1819790565","n1819790801","n1819790538","n1819790640","n1819790616","n1819790572","n1819790620","n1819790883","n1819790849","n1819790675","n1819790851","n1819790803","n1819790611","n1819790703","n1819790734","n1819790793","n1819790614","n1819790581","n1819790776","n1819790797","n1819790728","n1819790584","n1819790624","n1819790604","n1819790725","n1819790732"]},"w206805240":{"id":"w206805240","tags":{"waterway":"river"},"nodes":["n2168544738","n2168544739","n2168544740","n2168544741","n2168544742","n2168544743","n2168544744","n2168544745","n2168544746","n2168544747","n2168544748","n2168544749","n2168544750","n2168544751","n2168544752","n2168544753","n1819848944"]},"n394490429":{"id":"n394490429","loc":[-85.643883,41.954365]},"n185953421":{"id":"n185953421","loc":[-85.641876,41.954946]},"n185953417":{"id":"n185953417","loc":[-85.6418306,41.9551597]},"n185977233":{"id":"n185977233","loc":[-85.642987,41.95486]},"n185977232":{"id":"n185977232","loc":[-85.642894,41.9547842]},"n1475293244":{"id":"n1475293244","loc":[-85.63974,41.9521543]},"n1819848890":{"id":"n1819848890","loc":[-85.6410004,41.9552822]},"n1819848965":{"id":"n1819848965","loc":[-85.6409795,41.9553892]},"n2189015846":{"id":"n2189015846","loc":[-85.6420457,41.9549528]},"n2189015849":{"id":"n2189015849","loc":[-85.6425867,41.9551392]},"n2189015852":{"id":"n2189015852","loc":[-85.6426877,41.9549771]},"n2199109816":{"id":"n2199109816","loc":[-85.6399215,41.9540925]},"n2199109818":{"id":"n2199109818","loc":[-85.6399182,41.9538236]},"n2199109820":{"id":"n2199109820","loc":[-85.6402201,41.9538216]},"n2199109822":{"id":"n2199109822","loc":[-85.640222,41.9539771]},"n2199109825":{"id":"n2199109825","loc":[-85.6402904,41.9539766]},"n2199109827":{"id":"n2199109827","loc":[-85.6402918,41.95409]},"n2199109829":{"id":"n2199109829","loc":[-85.6395845,41.9544626]},"n2199109831":{"id":"n2199109831","loc":[-85.6395792,41.9540671]},"n2199109833":{"id":"n2199109833","loc":[-85.6397173,41.9540661]},"n2199109835":{"id":"n2199109835","loc":[-85.6397226,41.9544616]},"n2199109837":{"id":"n2199109837","loc":[-85.6399641,41.9545058]},"n2199109839":{"id":"n2199109839","loc":[-85.6399637,41.9541859]},"n2199109841":{"id":"n2199109841","loc":[-85.6401098,41.9541858]},"n2199109843":{"id":"n2199109843","loc":[-85.64011,41.9543272]},"n2199109845":{"id":"n2199109845","loc":[-85.6400783,41.9543273]},"n2199109847":{"id":"n2199109847","loc":[-85.6400785,41.9545058]},"n2199109853":{"id":"n2199109853","loc":[-85.6396184,41.9554049]},"n2199109855":{"id":"n2199109855","loc":[-85.6396825,41.9553713]},"n185949745":{"id":"n185949745","loc":[-85.6442727,41.9553112]},"n185949748":{"id":"n185949748","loc":[-85.6448804,41.9555238]},"n185949755":{"id":"n185949755","loc":[-85.6420011,41.9603536]},"n185949763":{"id":"n185949763","loc":[-85.6408843,41.9555822]},"n185949765":{"id":"n185949765","loc":[-85.6414548,41.9557751]},"n185952158":{"id":"n185952158","loc":[-85.640066,41.956854]},"n185952160":{"id":"n185952160","loc":[-85.639848,41.957229]},"n185952161":{"id":"n185952161","loc":[-85.6396089,41.9576192]},"n185952163":{"id":"n185952163","loc":[-85.63892,41.957957]},"n185953413":{"id":"n185953413","loc":[-85.64162,41.955475]},"n185971651":{"id":"n185971651","loc":[-85.6440766,41.9543462]},"n185977234":{"id":"n185977234","loc":[-85.645044,41.955581]},"n394490395":{"id":"n394490395","loc":[-85.657336,41.936762]},"n394490396":{"id":"n394490396","loc":[-85.653896,41.936978]},"n394490397":{"id":"n394490397","loc":[-85.653732,41.937386]},"n394490398":{"id":"n394490398","loc":[-85.65182,41.937378]},"n394490399":{"id":"n394490399","loc":[-85.651843,41.938445]},"n394490400":{"id":"n394490400","loc":[-85.652536,41.938447]},"n394490401":{"id":"n394490401","loc":[-85.652533,41.938901]},"n394490402":{"id":"n394490402","loc":[-85.652084,41.9389]},"n394490403":{"id":"n394490403","loc":[-85.6521,41.939627]},"n394490404":{"id":"n394490404","loc":[-85.652301,41.939628]},"n394490405":{"id":"n394490405","loc":[-85.652302,41.939755]},"n394490406":{"id":"n394490406","loc":[-85.652783,41.939747]},"n394490407":{"id":"n394490407","loc":[-85.652835,41.94112]},"n394490408":{"id":"n394490408","loc":[-85.651968,41.941123]},"n394490409":{"id":"n394490409","loc":[-85.651983,41.941969]},"n394490410":{"id":"n394490410","loc":[-85.652908,41.941961]},"n394490411":{"id":"n394490411","loc":[-85.65292,41.94278]},"n394490412":{"id":"n394490412","loc":[-85.651698,41.942816]},"n394490413":{"id":"n394490413","loc":[-85.651509,41.942823]},"n394490414":{"id":"n394490414","loc":[-85.651272,41.942837]},"n394490415":{"id":"n394490415","loc":[-85.651272,41.943325]},"n394490416":{"id":"n394490416","loc":[-85.65122,41.944053]},"n394490417":{"id":"n394490417","loc":[-85.651193,41.944449]},"n394490418":{"id":"n394490418","loc":[-85.651088,41.944969]},"n394490419":{"id":"n394490419","loc":[-85.650949,41.945554]},"n394490420":{"id":"n394490420","loc":[-85.650907,41.945719]},"n394490421":{"id":"n394490421","loc":[-85.650808,41.946016]},"n394490422":{"id":"n394490422","loc":[-85.650712,41.946516]},"n394490423":{"id":"n394490423","loc":[-85.650493,41.947166]},"n394490424":{"id":"n394490424","loc":[-85.650626,41.947213]},"n394490425":{"id":"n394490425","loc":[-85.650201,41.948109]},"n394490426":{"id":"n394490426","loc":[-85.649868,41.948797]},"n394490427":{"id":"n394490427","loc":[-85.649669,41.949161]},"n394490428":{"id":"n394490428","loc":[-85.64659,41.954067]},"n394490430":{"id":"n394490430","loc":[-85.644034,41.95444]},"n394490431":{"id":"n394490431","loc":[-85.644248,41.954507]},"n394490432":{"id":"n394490432","loc":[-85.64491,41.954481]},"n394490433":{"id":"n394490433","loc":[-85.645213,41.954433]},"n394490434":{"id":"n394490434","loc":[-85.645426,41.954477]},"n394490435":{"id":"n394490435","loc":[-85.6458,41.954704]},"n394490436":{"id":"n394490436","loc":[-85.64605,41.954804]},"n394490437":{"id":"n394490437","loc":[-85.646125,41.954817]},"n394490438":{"id":"n394490438","loc":[-85.646002,41.954997]},"n394490439":{"id":"n394490439","loc":[-85.645764,41.955366]},"n394490440":{"id":"n394490440","loc":[-85.645525,41.955734]},"n394490441":{"id":"n394490441","loc":[-85.64443,41.957424]},"n394490442":{"id":"n394490442","loc":[-85.641712,41.961723]},"n394490443":{"id":"n394490443","loc":[-85.640747,41.963246]},"n394490444":{"id":"n394490444","loc":[-85.637803,41.967894]},"n394490445":{"id":"n394490445","loc":[-85.637673,41.967861]},"n394490446":{"id":"n394490446","loc":[-85.636637,41.969275]},"n394490447":{"id":"n394490447","loc":[-85.634923,41.969269]},"n394490448":{"id":"n394490448","loc":[-85.634893,41.968537]},"n394490449":{"id":"n394490449","loc":[-85.634544,41.96927]},"n394490450":{"id":"n394490450","loc":[-85.630835,41.969274]},"n394490451":{"id":"n394490451","loc":[-85.630834,41.968348]},"n394490452":{"id":"n394490452","loc":[-85.630857,41.968179]},"n394490453":{"id":"n394490453","loc":[-85.630924,41.968044]},"n394490454":{"id":"n394490454","loc":[-85.631004,41.967925]},"n394490455":{"id":"n394490455","loc":[-85.631143,41.967811]},"n394490456":{"id":"n394490456","loc":[-85.631311,41.967736]},"n394490457":{"id":"n394490457","loc":[-85.631595,41.967693]},"n394490458":{"id":"n394490458","loc":[-85.63325,41.967702]},"n394490459":{"id":"n394490459","loc":[-85.633247,41.967021]},"n394490460":{"id":"n394490460","loc":[-85.634858,41.967021]},"n394490461":{"id":"n394490461","loc":[-85.634865,41.967711]},"n394490462":{"id":"n394490462","loc":[-85.634884,41.968231]},"n394490463":{"id":"n394490463","loc":[-85.636559,41.963867]},"n394490464":{"id":"n394490464","loc":[-85.634832,41.963866]},"n394490465":{"id":"n394490465","loc":[-85.63481,41.961899]},"n394490466":{"id":"n394490466","loc":[-85.637219,41.961842]},"n394490467":{"id":"n394490467","loc":[-85.637837,41.960019]},"n394490468":{"id":"n394490468","loc":[-85.637459,41.960022]},"n394490469":{"id":"n394490469","loc":[-85.635295,41.959987]},"n394490470":{"id":"n394490470","loc":[-85.634783,41.959979]},"n394490471":{"id":"n394490471","loc":[-85.634776,41.959834]},"n394490472":{"id":"n394490472","loc":[-85.634767,41.959009]},"n394490473":{"id":"n394490473","loc":[-85.634763,41.958292]},"n394490474":{"id":"n394490474","loc":[-85.633346,41.958287]},"n394490475":{"id":"n394490475","loc":[-85.632128,41.9583]},"n394490476":{"id":"n394490476","loc":[-85.631414,41.958318]},"n394490477":{"id":"n394490477","loc":[-85.63137,41.959033]},"n394490478":{"id":"n394490478","loc":[-85.631325,41.959753]},"n394490479":{"id":"n394490479","loc":[-85.631494,41.95977]},"n394490480":{"id":"n394490480","loc":[-85.631456,41.960673]},"n394490481":{"id":"n394490481","loc":[-85.631421,41.961494]},"n394490482":{"id":"n394490482","loc":[-85.631404,41.961887]},"n394490483":{"id":"n394490483","loc":[-85.631401,41.961968]},"n394490484":{"id":"n394490484","loc":[-85.630962,41.961967]},"n394490485":{"id":"n394490485","loc":[-85.6299,41.961973]},"n394490486":{"id":"n394490486","loc":[-85.624929,41.962002]},"n394490487":{"id":"n394490487","loc":[-85.623333,41.961987]},"n394490488":{"id":"n394490488","loc":[-85.621894,41.963956]},"n394490489":{"id":"n394490489","loc":[-85.62131,41.963727]},"n394490490":{"id":"n394490490","loc":[-85.621216,41.963868]},"n394490491":{"id":"n394490491","loc":[-85.620356,41.965119]},"n394490492":{"id":"n394490492","loc":[-85.620848,41.965341]},"n394490493":{"id":"n394490493","loc":[-85.620684,41.965558]},"n394490494":{"id":"n394490494","loc":[-85.620621,41.965658]},"n394490495":{"id":"n394490495","loc":[-85.618165,41.965759]},"n394490496":{"id":"n394490496","loc":[-85.618071,41.965759]},"n394490497":{"id":"n394490497","loc":[-85.617986,41.965759]},"n394490498":{"id":"n394490498","loc":[-85.605673,41.965764]},"n394490499":{"id":"n394490499","loc":[-85.605668,41.963548]},"n394490500":{"id":"n394490500","loc":[-85.605664,41.962094]},"n394490501":{"id":"n394490501","loc":[-85.595828,41.962159]},"n394490502":{"id":"n394490502","loc":[-85.587869,41.962169]},"n394490503":{"id":"n394490503","loc":[-85.586289,41.962179]},"n394490504":{"id":"n394490504","loc":[-85.583774,41.962178]},"n394490505":{"id":"n394490505","loc":[-85.583774,41.961789]},"n394490506":{"id":"n394490506","loc":[-85.581303,41.961783]},"n394490507":{"id":"n394490507","loc":[-85.581304,41.961616]},"n394490508":{"id":"n394490508","loc":[-85.581292,41.961616]},"n394490509":{"id":"n394490509","loc":[-85.581247,41.959244]},"n394490510":{"id":"n394490510","loc":[-85.581245,41.958394]},"n394490511":{"id":"n394490511","loc":[-85.581276,41.958372]},"n394490512":{"id":"n394490512","loc":[-85.581302,41.958353]},"n394490513":{"id":"n394490513","loc":[-85.581376,41.9583]},"n394490514":{"id":"n394490514","loc":[-85.582256,41.957663]},"n394490515":{"id":"n394490515","loc":[-85.585299,41.955483]},"n394490516":{"id":"n394490516","loc":[-85.585588,41.955331]},"n394490517":{"id":"n394490517","loc":[-85.586053,41.955163]},"n394490518":{"id":"n394490518","loc":[-85.58632,41.955076]},"n394490519":{"id":"n394490519","loc":[-85.586478,41.955025]},"n394490520":{"id":"n394490520","loc":[-85.58692,41.954947]},"n394490521":{"id":"n394490521","loc":[-85.587327,41.954914]},"n394490522":{"id":"n394490522","loc":[-85.587345,41.954913]},"n394490523":{"id":"n394490523","loc":[-85.587358,41.954913]},"n394490524":{"id":"n394490524","loc":[-85.58963,41.954877]},"n394490525":{"id":"n394490525","loc":[-85.591077,41.954865]},"n394490526":{"id":"n394490526","loc":[-85.594824,41.954843]},"n394490527":{"id":"n394490527","loc":[-85.594804,41.95331]},"n394490528":{"id":"n394490528","loc":[-85.599336,41.95331]},"n394490529":{"id":"n394490529","loc":[-85.599336,41.954825]},"n394490530":{"id":"n394490530","loc":[-85.597828,41.954839]},"n394490531":{"id":"n394490531","loc":[-85.597833,41.95614]},"n394490532":{"id":"n394490532","loc":[-85.596586,41.956151]},"n394490533":{"id":"n394490533","loc":[-85.596586,41.956394]},"n394490534":{"id":"n394490534","loc":[-85.595933,41.956394]},"n394490535":{"id":"n394490535","loc":[-85.595933,41.958176]},"n394490536":{"id":"n394490536","loc":[-85.597635,41.958179]},"n394490537":{"id":"n394490537","loc":[-85.597717,41.958177]},"n394490538":{"id":"n394490538","loc":[-85.601671,41.958194]},"n394490539":{"id":"n394490539","loc":[-85.605619,41.958194]},"n394490540":{"id":"n394490540","loc":[-85.608054,41.958187]},"n394490542":{"id":"n394490542","loc":[-85.6080762,41.9547864]},"n394490545":{"id":"n394490545","loc":[-85.6104354,41.9548263]},"n394490546":{"id":"n394490546","loc":[-85.610274,41.951106]},"n394490547":{"id":"n394490547","loc":[-85.610278,41.950829]},"n394490548":{"id":"n394490548","loc":[-85.610309,41.948377]},"n394490549":{"id":"n394490549","loc":[-85.610314,41.947986]},"n394490550":{"id":"n394490550","loc":[-85.610464,41.947985]},"n394490551":{"id":"n394490551","loc":[-85.610447,41.947468]},"n394490552":{"id":"n394490552","loc":[-85.612469,41.947471]},"n394490553":{"id":"n394490553","loc":[-85.612494,41.945576]},"n394490554":{"id":"n394490554","loc":[-85.610292,41.94558]},"n394490555":{"id":"n394490555","loc":[-85.608412,41.945625]},"n394490556":{"id":"n394490556","loc":[-85.608412,41.943036]},"n394490557":{"id":"n394490557","loc":[-85.608702,41.943087]},"n394490558":{"id":"n394490558","loc":[-85.609196,41.943224]},"n394490559":{"id":"n394490559","loc":[-85.609571,41.943263]},"n394490560":{"id":"n394490560","loc":[-85.610116,41.943295]},"n394490561":{"id":"n394490561","loc":[-85.610273,41.943275]},"n394490562":{"id":"n394490562","loc":[-85.611339,41.943075]},"n394490563":{"id":"n394490563","loc":[-85.611575,41.942997]},"n394490564":{"id":"n394490564","loc":[-85.611847,41.942849]},"n394490565":{"id":"n394490565","loc":[-85.612164,41.942568]},"n394490566":{"id":"n394490566","loc":[-85.612341,41.942529]},"n394490567":{"id":"n394490567","loc":[-85.612562,41.942524]},"n394490568":{"id":"n394490568","loc":[-85.612768,41.942546]},"n394490569":{"id":"n394490569","loc":[-85.612938,41.942633]},"n394490570":{"id":"n394490570","loc":[-85.6131,41.942782]},"n394490571":{"id":"n394490571","loc":[-85.613299,41.942919]},"n394490572":{"id":"n394490572","loc":[-85.613498,41.942996]},"n394490573":{"id":"n394490573","loc":[-85.614698,41.942842]},"n394490574":{"id":"n394490574","loc":[-85.615288,41.942698]},"n394490575":{"id":"n394490575","loc":[-85.616054,41.942693]},"n394490576":{"id":"n394490576","loc":[-85.61603,41.942175]},"n394490577":{"id":"n394490577","loc":[-85.616004,41.941741]},"n394490578":{"id":"n394490578","loc":[-85.615994,41.940156]},"n394490579":{"id":"n394490579","loc":[-85.615144,41.940159]},"n394490580":{"id":"n394490580","loc":[-85.614915,41.940161]},"n394490582":{"id":"n394490582","loc":[-85.614875,41.938532]},"n394490583":{"id":"n394490583","loc":[-85.616167,41.938787]},"n394490585":{"id":"n394490585","loc":[-85.616176,41.938589]},"n394490586":{"id":"n394490586","loc":[-85.614537,41.938282]},"n394490588":{"id":"n394490588","loc":[-85.610141,41.937459]},"n394490589":{"id":"n394490589","loc":[-85.610172,41.937298]},"n394490590":{"id":"n394490590","loc":[-85.609918,41.935495]},"n394490592":{"id":"n394490592","loc":[-85.610092,41.935451]},"n394490594":{"id":"n394490594","loc":[-85.610681,41.935247]},"n394490595":{"id":"n394490595","loc":[-85.611446,41.934955]},"n394490596":{"id":"n394490596","loc":[-85.612057,41.934696]},"n394490598":{"id":"n394490598","loc":[-85.613256,41.934084]},"n394490599":{"id":"n394490599","loc":[-85.613948,41.933682]},"n394490601":{"id":"n394490601","loc":[-85.61436,41.933417]},"n394490602":{"id":"n394490602","loc":[-85.614638,41.933212]},"n394490604":{"id":"n394490604","loc":[-85.615249,41.9332]},"n394490605":{"id":"n394490605","loc":[-85.618218,41.933223]},"n394490607":{"id":"n394490607","loc":[-85.618241,41.933479]},"n394490608":{"id":"n394490608","loc":[-85.618257,41.93365]},"n394490609":{"id":"n394490609","loc":[-85.618298,41.935067]},"n394490611":{"id":"n394490611","loc":[-85.619791,41.935067]},"n394490612":{"id":"n394490612","loc":[-85.619794,41.933301]},"n394490613":{"id":"n394490613","loc":[-85.619795,41.932692]},"n394490614":{"id":"n394490614","loc":[-85.619729,41.929517]},"n394490615":{"id":"n394490615","loc":[-85.619801,41.929305]},"n394490616":{"id":"n394490616","loc":[-85.619809,41.927391]},"n394490617":{"id":"n394490617","loc":[-85.620883,41.927378]},"n394490618":{"id":"n394490618","loc":[-85.620988,41.927368]},"n394490619":{"id":"n394490619","loc":[-85.621076,41.927368]},"n394490620":{"id":"n394490620","loc":[-85.621156,41.927376]},"n394490621":{"id":"n394490621","loc":[-85.621685,41.92737]},"n394490622":{"id":"n394490622","loc":[-85.624716,41.927359]},"n394490623":{"id":"n394490623","loc":[-85.625308,41.92737]},"n394490624":{"id":"n394490624","loc":[-85.625655,41.927377]},"n394490625":{"id":"n394490625","loc":[-85.625093,41.925591]},"n394490626":{"id":"n394490626","loc":[-85.625174,41.92559]},"n394490627":{"id":"n394490627","loc":[-85.625249,41.925597]},"n394490628":{"id":"n394490628","loc":[-85.625532,41.925604]},"n394490629":{"id":"n394490629","loc":[-85.625761,41.925597]},"n394490630":{"id":"n394490630","loc":[-85.625955,41.926153]},"n394490631":{"id":"n394490631","loc":[-85.626209,41.926155]},"n394490632":{"id":"n394490632","loc":[-85.627757,41.926151]},"n394490633":{"id":"n394490633","loc":[-85.627825,41.926298]},"n394490634":{"id":"n394490634","loc":[-85.627994,41.926315]},"n394490635":{"id":"n394490635","loc":[-85.628049,41.927196]},"n394490636":{"id":"n394490636","loc":[-85.62949,41.927221]},"n394490637":{"id":"n394490637","loc":[-85.629602,41.927277]},"n394490638":{"id":"n394490638","loc":[-85.6297102,41.9273279]},"n394490639":{"id":"n394490639","loc":[-85.630958,41.927398]},"n394490699":{"id":"n394490699","loc":[-85.632741,41.927388]},"n394490700":{"id":"n394490700","loc":[-85.632997,41.927391]},"n394490701":{"id":"n394490701","loc":[-85.633149,41.927393]},"n394490702":{"id":"n394490702","loc":[-85.633334,41.927393]},"n394490703":{"id":"n394490703","loc":[-85.633468,41.927561]},"n394490704":{"id":"n394490704","loc":[-85.633563,41.927755]},"n394490705":{"id":"n394490705","loc":[-85.633662,41.928192]},"n394490706":{"id":"n394490706","loc":[-85.633679,41.928807]},"n394490707":{"id":"n394490707","loc":[-85.633687,41.929107]},"n394490708":{"id":"n394490708","loc":[-85.633927,41.929109]},"n394490709":{"id":"n394490709","loc":[-85.634126,41.929111]},"n394490710":{"id":"n394490710","loc":[-85.634207,41.92911]},"n394490711":{"id":"n394490711","loc":[-85.634323,41.929111]},"n394490712":{"id":"n394490712","loc":[-85.636712,41.929128]},"n394490713":{"id":"n394490713","loc":[-85.63808,41.9291]},"n394490714":{"id":"n394490714","loc":[-85.639213,41.929088]},"n394490715":{"id":"n394490715","loc":[-85.639189,41.92852]},"n394490716":{"id":"n394490716","loc":[-85.639204,41.925488]},"n394490717":{"id":"n394490717","loc":[-85.644204,41.925452]},"n394490718":{"id":"n394490718","loc":[-85.651425,41.925406]},"n394490719":{"id":"n394490719","loc":[-85.651449,41.926321]},"n394490720":{"id":"n394490720","loc":[-85.651451,41.926969]},"n394490721":{"id":"n394490721","loc":[-85.651458,41.928052]},"n394490722":{"id":"n394490722","loc":[-85.651446,41.928892]},"n394490723":{"id":"n394490723","loc":[-85.651456,41.929447]},"n394490724":{"id":"n394490724","loc":[-85.651707,41.929454]},"n394490725":{"id":"n394490725","loc":[-85.652369,41.929473]},"n394490726":{"id":"n394490726","loc":[-85.6525,41.929452]},"n394490727":{"id":"n394490727","loc":[-85.654066,41.92946]},"n394490728":{"id":"n394490728","loc":[-85.654816,41.92946]},"n394490729":{"id":"n394490729","loc":[-85.654816,41.930337]},"n394490730":{"id":"n394490730","loc":[-85.654587,41.930337]},"n394490731":{"id":"n394490731","loc":[-85.654548,41.931072]},"n394490732":{"id":"n394490732","loc":[-85.654538,41.931701]},"n394490733":{"id":"n394490733","loc":[-85.654898,41.931689]},"n394490734":{"id":"n394490734","loc":[-85.654898,41.932505]},"n394490735":{"id":"n394490735","loc":[-85.654854,41.932514]},"n394490736":{"id":"n394490736","loc":[-85.655497,41.932499]},"n394490737":{"id":"n394490737","loc":[-85.656405,41.932493]},"n394490738":{"id":"n394490738","loc":[-85.656422,41.933416]},"n394490739":{"id":"n394490739","loc":[-85.657322,41.933438]},"n1475293233":{"id":"n1475293233","loc":[-85.6385522,41.9585167]},"n1475293242":{"id":"n1475293242","loc":[-85.64609,41.9540815]},"n1475293249":{"id":"n1475293249","loc":[-85.6358079,41.9692721]},"n1475293256":{"id":"n1475293256","loc":[-85.6387369,41.9581583]},"n1475293259":{"id":"n1475293259","loc":[-85.6455882,41.9541138]},"n1475293266":{"id":"n1475293266","loc":[-85.6451008,41.9541821]},"n1819800253":{"id":"n1819800253","loc":[-85.6134286,41.9429692]},"n2114807558":{"id":"n2114807558","loc":[-85.6365609,41.963866],"tags":{"railway":"level_crossing"}},"n2189015728":{"id":"n2189015728","loc":[-85.6383956,41.9590576]},"n2189015838":{"id":"n2189015838","loc":[-85.6435144,41.9563705]},"n2189015842":{"id":"n2189015842","loc":[-85.6415782,41.9557035]},"n2189015855":{"id":"n2189015855","loc":[-85.6440829,41.9554577]},"n2199109849":{"id":"n2199109849","loc":[-85.6393434,41.9565591]},"n2199109851":{"id":"n2199109851","loc":[-85.6393208,41.9565002]},"n2199109857":{"id":"n2199109857","loc":[-85.6401986,41.955545]},"n2199109859":{"id":"n2199109859","loc":[-85.6402362,41.955587]},"n2199109861":{"id":"n2199109861","loc":[-85.6395958,41.9565675]},"n2199109863":{"id":"n2199109863","loc":[-85.639528,41.9566011]},"w209717053":{"id":"w209717053","tags":{"area":"yes","building":"yes"},"nodes":["n2199109829","n2199109831","n2199109833","n2199109835","n2199109829"]},"w17966415":{"id":"w17966415","tags":{"access":"private","highway":"service","name":"Manufacturing Way"},"nodes":["n185971642","n185977232","n185977233","n185949745","n185949748","n185977234"]},"w209717054":{"id":"w209717054","tags":{"area":"yes","building":"yes"},"nodes":["n2199109837","n2199109839","n2199109841","n2199109843","n2199109845","n2199109847","n2199109837"]},"w208627214":{"id":"w208627214","tags":{"highway":"service"},"nodes":["n185949755","n2189015728","n1475293233","n1475293256","n185952163","n185952161","n185952160","n185952158","n185949763","n1819848965","n1819848890","n185952156"]},"w17963817":{"id":"w17963817","tags":{"access":"private","highway":"service"},"nodes":["n185949765","n185953413","n185953417","n185953421","n185953423"]},"w34369809":{"id":"w34369809","tags":{"admin_level":"8","boundary":"administrative","landuse":"residential"},"nodes":["n394490395","n394490396","n394490397","n394490398","n394490399","n394490400","n394490401","n394490402","n394490403","n394490404","n394490405","n394490406","n394490407","n394490408","n394490409","n394490410","n394490411","n394490412","n394490413","n394490414","n394490415","n394490416","n394490417","n394490418","n394490419","n394490420","n394490421","n394490422","n394490423","n394490424","n394490425","n394490426","n394490427","n394490428","n1475293242","n1475293259","n1475293266","n394490429","n394490430","n394490431","n394490432","n394490433","n394490434","n394490435","n394490436","n394490437","n394490438","n394490439","n394490440","n394490441","n394490442","n394490443","n394490444","n394490445","n394490446","n1475293249","n394490447","n394490448","n394490449","n394490450","n394490451","n394490452","n394490453","n394490454","n394490455","n394490456","n394490457","n394490458","n394490459","n394490460","n394490461","n394490462","n2114807558","n394490463","n1475293226","n394490464","n394490465","n394490466","n394490467","n394490468","n394490469","n394490470","n394490471","n394490472","n394490473","n394490474","n394490475","n394490476","n394490477","n394490478","n394490479","n394490480","n394490481","n394490482","n394490483","n394490484","n394490485","n394490486","n394490487","n394490488","n394490489","n394490490","n394490491","n394490492","n394490493","n394490494","n394490495","n394490496","n394490497","n394490498","n394490499","n394490500","n394490501","n394490502","n394490503","n394490504","n394490505","n394490506","n394490507","n394490508","n394490509","n394490510","n394490511","n394490512","n394490513","n394490514","n394490515","n394490516","n394490517","n394490518","n394490519","n394490520","n394490521","n394490522","n394490523","n394490524","n394490525","n394490526","n394490527","n394490528","n394490529","n394490530","n394490531","n394490532","n394490533","n394490534","n394490535","n394490536","n394490537","n394490538","n394490539","n394490540","n394490542","n394490545","n394490546","n394490547","n394490548","n394490549","n394490550","n394490551","n394490552","n394490553","n394490554","n394490555","n394490556","n394490557","n394490558","n394490559","n394490560","n394490561","n394490562","n394490563","n394490564","n394490565","n394490566","n394490567","n394490568","n394490569","n394490570","n394490571","n1819800253","n394490572","n394490573","n394490574","n394490575","n394490576","n394490577","n394490578","n394490579","n394490580","n394490582","n394490583","n394490585","n394490586","n394490588","n394490589","n394490590","n394490592","n394490594","n394490595","n394490596","n394490598","n394490599","n394490601","n394490602","n394490604","n394490605","n394490607","n394490608","n394490609","n394490611","n394490612","n394490613","n394490614","n394490615","n394490616","n394490617","n394490618","n394490619","n394490620","n394490621","n394490622","n394490623","n394490624","n394490625","n394490626","n394490627","n394490628","n394490629","n394490630","n394490631","n394490632","n394490633","n394490634","n394490635","n394490636","n394490637","n394490638","n394490639","n394490699","n394490700","n394490701","n394490702","n394490703","n394490704","n394490705","n394490706","n394490707","n394490708","n394490709","n394490710","n394490711","n394490712","n394490713","n394490714","n394490715","n394490716","n394490717","n394490718","n394490719","n394490720","n394490721","n394490722","n394490723","n394490724","n394490725","n394490726","n394490727","n394490728","n394490729","n394490730","n394490731","n394490732","n394490733","n394490734","n394490735","n394490736","n394490737","n394490738","n394490739","n394490395"]},"w208627221":{"id":"w208627221","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015838","n2189015842","n2189015846","n2189015849","n2189015852","n2189015855","n2189015838"]},"w209717052":{"id":"w209717052","tags":{"area":"yes","building":"yes"},"nodes":["n2199109816","n2199109818","n2199109820","n2199109822","n2199109825","n2199109827","n2199109816"]},"w134151784":{"id":"w134151784","tags":{"bridge":"yes","highway":"residential","name":"W Hoffman St"},"nodes":["n185971648","n185971651"]},"w209717055":{"id":"w209717055","tags":{"area":"yes","landuse":"basin"},"nodes":["n2199109849","n2199109851","n2199109853","n2199109855","n2199109857","n2199109859","n2199109861","n2199109863","n2199109849"]},"w17967763":{"id":"w17967763","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n1475293244","n185982166","n185975067","n185971637"]},"r134949":{"id":"r134949","tags":{"admin_level":"8","border_type":"city","boundary":"administrative","name":"Three Rivers","place":"city","type":"boundary"},"members":[{"id":"w34369809","type":"way","role":"outer"},{"id":"w34369821","type":"way","role":"outer"},{"id":"w34369822","type":"way","role":"outer"},{"id":"w34369823","type":"way","role":"outer"},{"id":"w34369824","type":"way","role":"outer"},{"id":"w34369825","type":"way","role":"outer"},{"id":"w34369826","type":"way","role":"outer"},{"id":"w34369810","type":"way","role":"inner"},{"id":"w34369811","type":"way","role":"inner"},{"id":"w34369812","type":"way","role":"inner"},{"id":"w34367079","type":"way","role":"inner"},{"id":"w34369814","type":"way","role":"inner"},{"id":"w34367080","type":"way","role":"inner"},{"id":"w34369815","type":"way","role":"inner"},{"id":"w34369820","type":"way","role":"inner"}]},"n1819848881":{"id":"n1819848881","loc":[-85.638562,41.9569965]},"n1819848947":{"id":"n1819848947","loc":[-85.6384348,41.9576565]},"n1819849044":{"id":"n1819849044","loc":[-85.6385749,41.9573345]},"n2114807547":{"id":"n2114807547","loc":[-85.6384626,41.9583756]},"n2114807564":{"id":"n2114807564","loc":[-85.638535,41.9581283]},"n2189015691":{"id":"n2189015691","loc":[-85.6435584,41.9565243]},"n2189015696":{"id":"n2189015696","loc":[-85.6435805,41.9566049]},"n2189015722":{"id":"n2189015722","loc":[-85.6435035,41.9567438]},"n2189015744":{"id":"n2189015744","loc":[-85.6437991,41.9569582]},"n2189015747":{"id":"n2189015747","loc":[-85.6433042,41.9567742]},"n2189015750":{"id":"n2189015750","loc":[-85.6433827,41.9566844]},"n2189015753":{"id":"n2189015753","loc":[-85.6430447,41.9565588]},"n2189015756":{"id":"n2189015756","loc":[-85.6431111,41.956451]},"n2189015759":{"id":"n2189015759","loc":[-85.6420247,41.956083]},"n2189015760":{"id":"n2189015760","loc":[-85.6419945,41.9561369]},"n2189015764":{"id":"n2189015764","loc":[-85.6413729,41.9558945]},"n2189015766":{"id":"n2189015766","loc":[-85.6412884,41.9560606]},"n2189015770":{"id":"n2189015770","loc":[-85.6411798,41.9560112]},"n2189015771":{"id":"n2189015771","loc":[-85.6410651,41.9562132]},"n2189015774":{"id":"n2189015774","loc":[-85.6409504,41.9561728]},"n2189015778":{"id":"n2189015778","loc":[-85.6407996,41.9564241]},"n2189015781":{"id":"n2189015781","loc":[-85.6406889,41.9563892]},"n2189015785":{"id":"n2189015785","loc":[-85.6404857,41.9567024]},"n2189015789":{"id":"n2189015789","loc":[-85.6406909,41.9567877]},"n2189015793":{"id":"n2189015793","loc":[-85.6405642,41.9570165]},"n2189015796":{"id":"n2189015796","loc":[-85.6415359,41.9573711]},"n2189015800":{"id":"n2189015800","loc":[-85.6411738,41.9579501]},"n2189015804":{"id":"n2189015804","loc":[-85.6411119,41.957921]},"n2189015808":{"id":"n2189015808","loc":[-85.6403186,41.9591751]},"n2189015909":{"id":"n2189015909","loc":[-85.6389293,41.9564636]},"n2189015926":{"id":"n2189015926","loc":[-85.6385431,41.9564617]},"n2189015929":{"id":"n2189015929","loc":[-85.6385457,41.9561823]},"n2189015932":{"id":"n2189015932","loc":[-85.6389319,41.9561843]},"n2199109865":{"id":"n2199109865","loc":[-85.6400768,41.956776]},"n2199109867":{"id":"n2199109867","loc":[-85.639902,41.9567153]},"n2199109869":{"id":"n2199109869","loc":[-85.640004,41.956553]},"n2199109871":{"id":"n2199109871","loc":[-85.6401788,41.9566137]},"n2199109873":{"id":"n2199109873","loc":[-85.6399316,41.9564506],"tags":{"man_made":"water_tower"}},"n2199109876":{"id":"n2199109876","loc":[-85.6397689,41.9572354]},"n2199109878":{"id":"n2199109878","loc":[-85.6399229,41.9569826]},"n2199109880":{"id":"n2199109880","loc":[-85.639706,41.9569095]},"n2199109882":{"id":"n2199109882","loc":[-85.639552,41.9571623]},"n2199109884":{"id":"n2199109884","loc":[-85.6391028,41.9569517]},"n2199109886":{"id":"n2199109886","loc":[-85.6392876,41.956646]},"n2199109888":{"id":"n2199109888","loc":[-85.639484,41.9567117]},"n2199109889":{"id":"n2199109889","loc":[-85.6394322,41.9567973]},"n2199109890":{"id":"n2199109890","loc":[-85.6393718,41.9567771]},"n2199109891":{"id":"n2199109891","loc":[-85.6392387,41.9569972]},"n1819848900":{"id":"n1819848900","loc":[-85.638281,41.9576578]},"n1819848978":{"id":"n1819848978","loc":[-85.6377186,41.9580867]},"n1819849039":{"id":"n1819849039","loc":[-85.6384217,41.9573405]},"n1819849050":{"id":"n1819849050","loc":[-85.6377011,41.9570042]},"n1819849088":{"id":"n1819849088","loc":[-85.6382879,41.9580817]},"n2114807549":{"id":"n2114807549","loc":[-85.6362551,41.96473]},"n2114807587":{"id":"n2114807587","loc":[-85.6368694,41.9629829]},"n2189015725":{"id":"n2189015725","loc":[-85.644156,41.9569753]},"n2189015741":{"id":"n2189015741","loc":[-85.6419825,41.9597632]},"w208627217":{"id":"w208627217","tags":{"area":"yes","building":"yes"},"nodes":["n2189015741","n2189015744","n2189015747","n2189015750","n2189015753","n2189015756","n2189015759","n2189015760","n2189015764","n2189015766","n2189015770","n2189015771","n2189015774","n2189015778","n2189015781","n2189015785","n2189015789","n2189015793","n2189015796","n2189015800","n2189015804","n2189015808","n2189015741"]},"w208627212":{"id":"w208627212","tags":{"highway":"service"},"nodes":["n2189015691","n2189015696","n2189015722","n2189015725"]},"w209717057":{"id":"w209717057","tags":{"area":"yes","building":"yes"},"nodes":["n2199109876","n2199109878","n2199109880","n2199109882","n2199109876"]},"w209717056":{"id":"w209717056","tags":{"area":"yes","building":"yes"},"nodes":["n2199109865","n2199109867","n2199109869","n2199109871","n2199109865"]},"w208627231":{"id":"w208627231","tags":{"area":"yes","building":"yes"},"nodes":["n2189015909","n2189015926","n2189015929","n2189015932","n2189015909"]},"w170848326":{"id":"w170848326","tags":{"building":"yes"},"nodes":["n1819848881","n1819849050","n1819848978","n1819849088","n1819848900","n1819848947","n1819849039","n1819849044","n1819848881"]},"w17963182":{"id":"w17963182","tags":{"highway":"service"},"nodes":["n185949763","n185949765","n2189015691","n185949745"]},"w201484340":{"id":"w201484340","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807565","n2114807564","n2114807547","n2114807587","n2114807558","n2114807549","n2114807593"]},"w209717058":{"id":"w209717058","tags":{"area":"yes","building":"yes"},"nodes":["n2199109884","n2199109886","n2199109888","n2199109889","n2199109890","n2199109891","n2199109884"]},"n185954650":{"id":"n185954650","loc":[-85.627331,41.957439]},"n185966949":{"id":"n185966949","loc":[-85.626868,41.957314]},"n185989335":{"id":"n185989335","loc":[-85.62529,41.958568]},"n185989337":{"id":"n185989337","loc":[-85.624962,41.958453]},"n185989339":{"id":"n185989339","loc":[-85.624832,41.958399]},"n185989340":{"id":"n185989340","loc":[-85.624707,41.958325]},"n185989342":{"id":"n185989342","loc":[-85.624636,41.958251]},"n185989345":{"id":"n185989345","loc":[-85.624578,41.95818]},"n185989347":{"id":"n185989347","loc":[-85.624533,41.958099]},"n185989349":{"id":"n185989349","loc":[-85.624507,41.957985]},"n185989351":{"id":"n185989351","loc":[-85.624495,41.957807]},"n185989353":{"id":"n185989353","loc":[-85.624514,41.957663]},"n185989354":{"id":"n185989354","loc":[-85.624577,41.957593]},"n185989356":{"id":"n185989356","loc":[-85.624685,41.95754]},"n185989357":{"id":"n185989357","loc":[-85.624802,41.957523]},"n185989359":{"id":"n185989359","loc":[-85.624996,41.957524]},"n185989361":{"id":"n185989361","loc":[-85.625409,41.957515]},"n185989364":{"id":"n185989364","loc":[-85.625634,41.957496]},"n185989367":{"id":"n185989367","loc":[-85.625832,41.957453]},"n185989368":{"id":"n185989368","loc":[-85.626044,41.957394]},"n354031352":{"id":"n354031352","loc":[-85.6252778,41.9586111],"tags":{"amenity":"place_of_worship","denomination":"baptist","name":"First Baptist Church","religion":"christian"}},"n2199109892":{"id":"n2199109892","loc":[-85.6261578,41.9589963]},"n2199109893":{"id":"n2199109893","loc":[-85.6263191,41.9586865]},"n2199109894":{"id":"n2199109894","loc":[-85.6261186,41.9586288]},"n2199109895":{"id":"n2199109895","loc":[-85.6260644,41.9587329]},"n2199109896":{"id":"n2199109896","loc":[-85.6261547,41.9587589]},"n2199109898":{"id":"n2199109898","loc":[-85.6260476,41.9589646]},"n185966951":{"id":"n185966951","loc":[-85.628404,41.957438]},"w17965351":{"id":"w17965351","tags":{"highway":"residential","name":"Flower Street"},"nodes":["n185966948","n185966949","n185954650","n185966951","n185966953","n185966955","n185966957"]},"w17967809":{"id":"w17967809","tags":{"highway":"residential","name":"Azaleamum Drive"},"nodes":["n185982197","n185989335","n185989337","n185989339","n185989340","n185989342","n185989345","n185989347","n185989349","n185989351","n185989353","n185989354","n185989356","n185989357","n185989359","n185989361","n185989364","n185989367","n185989368","n185982196"]},"w209717059":{"id":"w209717059","tags":{"area":"yes","building":"yes"},"nodes":["n2199109892","n2199109893","n2199109894","n2199109895","n2199109896","n2199109898","n2199109892"]},"n185961390":{"id":"n185961390","loc":[-85.63137,41.959033]},"n185961393":{"id":"n185961393","loc":[-85.634315,41.959017]},"w17966214":{"id":"w17966214","tags":{"highway":"residential","name":"East Adams Street"},"nodes":["n185975351","n185967434","n185968108"]},"w17964793":{"id":"w17964793","tags":{"highway":"residential","name":"Morris Ave"},"nodes":["n185961389","n185961390","n185961391","n185961393","n185961396"]},"n185952166":{"id":"n185952166","loc":[-85.638174,41.95831]},"n2114807552":{"id":"n2114807552","loc":[-85.6383526,41.9593788]},"n2114807591":{"id":"n2114807591","loc":[-85.6383741,41.9593968]},"n2189015731":{"id":"n2189015731","loc":[-85.6368404,41.9592785]},"n2189015734":{"id":"n2189015734","loc":[-85.6368404,41.9585918]},"n2189015737":{"id":"n2189015737","loc":[-85.6376009,41.9585918]},"n2189015738":{"id":"n2189015738","loc":[-85.6376009,41.9592785]},"n2189015897":{"id":"n2189015897","loc":[-85.6376839,41.9566137]},"n2189015900":{"id":"n2189015900","loc":[-85.6376831,41.9564865]},"n2189015903":{"id":"n2189015903","loc":[-85.6381161,41.9564851]},"n2189015906":{"id":"n2189015906","loc":[-85.6381168,41.9566122]},"n2189015937":{"id":"n2189015937","loc":[-85.6364789,41.9590634]},"n2189015940":{"id":"n2189015940","loc":[-85.6361137,41.9590672]},"n2189015943":{"id":"n2189015943","loc":[-85.6361169,41.9594033]},"n2189015945":{"id":"n2189015945","loc":[-85.6363456,41.9594021]},"n2189015952":{"id":"n2189015952","loc":[-85.636112,41.958892]},"n2189015955":{"id":"n2189015955","loc":[-85.6364757,41.9588894]},"n2189015957":{"id":"n2189015957","loc":[-85.6364729,41.9586747]},"n2189015958":{"id":"n2189015958","loc":[-85.6361103,41.9586765]},"n2189015959":{"id":"n2189015959","loc":[-85.6364719,41.9585562]},"n2189015960":{"id":"n2189015960","loc":[-85.6361093,41.958558]},"n2189015961":{"id":"n2189015961","loc":[-85.6355494,41.9586403]},"n2189015962":{"id":"n2189015962","loc":[-85.635549,41.9584711]},"n2189015963":{"id":"n2189015963","loc":[-85.6351831,41.9584715]},"n2189015964":{"id":"n2189015964","loc":[-85.6351834,41.9586408]},"n2189015966":{"id":"n2189015966","loc":[-85.6359579,41.9586359]},"n2189015968":{"id":"n2189015968","loc":[-85.6359561,41.9585465]},"n2189015971":{"id":"n2189015971","loc":[-85.6355476,41.9585509]},"n2189015974":{"id":"n2189015974","loc":[-85.6359516,41.9592934]},"n2189015977":{"id":"n2189015977","loc":[-85.635949,41.9586697]},"n2189015980":{"id":"n2189015980","loc":[-85.6351329,41.9586716]},"n2189015983":{"id":"n2189015983","loc":[-85.6351318,41.9583949]},"n2189015986":{"id":"n2189015986","loc":[-85.6349148,41.9583954]},"n2189015989":{"id":"n2189015989","loc":[-85.6349186,41.9592958]},"n2189015995":{"id":"n2189015995","loc":[-85.6360173,41.9593286]},"n2189015998":{"id":"n2189015998","loc":[-85.6360278,41.9583079]},"n2114807550":{"id":"n2114807550","loc":[-85.6383392,41.9595404]},"n2114807551":{"id":"n2114807551","loc":[-85.6375855,41.9616107]},"n2114807559":{"id":"n2114807559","loc":[-85.6373978,41.9621273]},"n2114807562":{"id":"n2114807562","loc":[-85.6373361,41.9622609]},"n2114807563":{"id":"n2114807563","loc":[-85.6376472,41.9613953]},"n2114807574":{"id":"n2114807574","loc":[-85.636974,41.9627695]},"n2114807589":{"id":"n2114807589","loc":[-85.6383017,41.9595005]},"n2114807592":{"id":"n2114807592","loc":[-85.6377169,41.9613494]},"n2114807595":{"id":"n2114807595","loc":[-85.6371081,41.962574]},"n2189015934":{"id":"n2189015934","loc":[-85.6364855,41.9595098]},"n2189015949":{"id":"n2189015949","loc":[-85.6363466,41.9595105]},"w208627244":{"id":"w208627244","tags":{"highway":"service"},"nodes":["n2189015992","n2189015995","n2189015998"]},"w208627240":{"id":"w208627240","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015971","n2189015962","n2189015963","n2189015964","n2189015961"]},"w17967437":{"id":"w17967437","tags":{"highway":"residential","name":"Lyman St"},"nodes":["n185964361","n185984024"]},"w208627237":{"id":"w208627237","tags":{"area":"yes","building":"yes"},"nodes":["n2189015955","n2189015957","n2189015958","n2189015952","n2189015955"]},"w17967465":{"id":"w17967465","tags":{"highway":"residential","name":"W Adams St"},"nodes":["n185978394","n185984022","n185964360"]},"w208627228":{"id":"w208627228","tags":{"area":"yes","building":"yes"},"nodes":["n2189015897","n2189015900","n2189015903","n2189015906","n2189015897"]},"w201484351":{"id":"w201484351","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807587","n2114807574","n2114807595","n2114807562","n2114807559","n2114807551","n2114807563","n2114807589","n2114807552"]},"w208627239":{"id":"w208627239","tags":{"area":"yes","building":"yes"},"nodes":["n2189015957","n2189015959","n2189015960","n2189015958","n2189015957"]},"w208627233":{"id":"w208627233","tags":{"area":"yes","building":"yes"},"nodes":["n2189015934","n2189015937","n2189015940","n2189015943","n2189015945","n2189015949","n2189015934"]},"w208627241":{"id":"w208627241","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015966","n2189015968","n2189015971","n2189015961"]},"w17967970":{"id":"w17967970","tags":{"highway":"residential","name":"Adams St"},"nodes":["n185975351","n185978394"]},"w208627235":{"id":"w208627235","tags":{"area":"yes","building":"yes"},"nodes":["n2189015940","n2189015952","n2189015955","n2189015937","n2189015940"]},"w17965468":{"id":"w17965468","tags":{"highway":"residential","name":"Armstrong Blvd"},"nodes":["n185967917","n2189015998","n185967918","n185964362","n185952166"]},"w201484346":{"id":"w201484346","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807551","n2114807592","n2114807550","n2114807591"]},"w208627242":{"id":"w208627242","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015974","n2189015977","n2189015980","n2189015983","n2189015986","n2189015989","n2189015974"]},"w208627216":{"id":"w208627216","tags":{"area":"yes","building":"yes"},"nodes":["n2189015731","n2189015734","n2189015737","n2189015738","n2189015731"]},"n185984309":{"id":"n185984309","loc":[-85.631421,41.961494]},"n185987987":{"id":"n185987987","loc":[-85.631456,41.960673]},"n185965397":{"id":"n185965397","loc":[-85.634603,41.959838]},"w17965196":{"id":"w17965196","tags":{"highway":"residential","name":"Burke Ave"},"nodes":["n185965395","n185965397","n185965399"]},"w17967215":{"id":"w17967215","tags":{"highway":"residential","name":"Kellogg Ave"},"nodes":["n185968114","n185984309","n185967440","n185978402"]},"w17967597":{"id":"w17967597","tags":{"highway":"residential","name":"Barnard Ave"},"nodes":["n185968112","n185987987","n185967438","n185978399"]},"n394490857":{"id":"n394490857","loc":[-85.633952,41.960664]},"n394490858":{"id":"n394490858","loc":[-85.633938,41.960227]},"n394490859":{"id":"n394490859","loc":[-85.634794,41.960212]},"n394490860":{"id":"n394490860","loc":[-85.634815,41.960662]},"n394490861":{"id":"n394490861","loc":[-85.634103,41.961268]},"n394490862":{"id":"n394490862","loc":[-85.634103,41.961001]},"n394490863":{"id":"n394490863","loc":[-85.634504,41.961003]},"n394490864":{"id":"n394490864","loc":[-85.634561,41.961269]},"n1057629869":{"id":"n1057629869","loc":[-85.6382599,41.9612134]},"n1057629937":{"id":"n1057629937","loc":[-85.6380035,41.9616137]},"n2189016014":{"id":"n2189016014","loc":[-85.6360365,41.9626496]},"n2189016017":{"id":"n2189016017","loc":[-85.6360374,41.9623228]},"n2189016020":{"id":"n2189016020","loc":[-85.6367557,41.9623239]},"n2189016022":{"id":"n2189016022","loc":[-85.6367566,41.9619919]},"n2189016025":{"id":"n2189016025","loc":[-85.6351794,41.9619893]},"n2189016028":{"id":"n2189016028","loc":[-85.6351788,41.9622011]},"n2189016031":{"id":"n2189016031","loc":[-85.6350855,41.9622009]},"n2189016034":{"id":"n2189016034","loc":[-85.6350845,41.962527]},"n2189016037":{"id":"n2189016037","loc":[-85.6352732,41.9625273]},"n2189016039":{"id":"n2189016039","loc":[-85.6352738,41.9623178]},"n2189016042":{"id":"n2189016042","loc":[-85.6357712,41.9623186]},"n2189016044":{"id":"n2189016044","loc":[-85.6357702,41.9626492]},"n1057629880":{"id":"n1057629880","loc":[-85.638817,41.9619017]},"n1057629923":{"id":"n1057629923","loc":[-85.6390733,41.9615014]},"w91092312":{"id":"w91092312","tags":{"power":"station"},"nodes":["n1057629923","n1057629869","n1057629937","n1057629880","n1057629923"]},"w34369826":{"id":"w34369826","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490861","n394490862","n394490863","n394490864","n394490861"]},"w34369825":{"id":"w34369825","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490857","n394490858","n394490859","n394490860","n394490857"]},"w208627248":{"id":"w208627248","tags":{"area":"yes","building":"yes"},"nodes":["n2189016014","n2189016017","n2189016020","n2189016022","n2189016025","n2189016028","n2189016031","n2189016034","n2189016037","n2189016039","n2189016042","n2189016044","n2189016014"]},"n394490766":{"id":"n394490766","loc":[-85.616777,41.955642]},"n394490768":{"id":"n394490768","loc":[-85.617239,41.955644]},"n394490792":{"id":"n394490792","loc":[-85.619034,41.95543]},"n185972055":{"id":"n185972055","loc":[-85.6185905,41.9568211]},"n185972057":{"id":"n185972057","loc":[-85.6186688,41.9570086]},"n185972059":{"id":"n185972059","loc":[-85.6186924,41.9581453]},"n185972060":{"id":"n185972060","loc":[-85.6187082,41.9588211],"tags":{"highway":"turning_circle"}},"n1819790724":{"id":"n1819790724","loc":[-85.6182155,41.9555703]},"n1819790735":{"id":"n1819790735","loc":[-85.6184059,41.9566188]},"n1819790799":{"id":"n1819790799","loc":[-85.6182372,41.9563771]},"n1819790896":{"id":"n1819790896","loc":[-85.6181431,41.9557227]},"n185971405":{"id":"n185971405","loc":[-85.6186766,41.9577468]},"n185971565":{"id":"n185971565","loc":[-85.6181613,41.9560879]},"n185967985":{"id":"n185967985","loc":[-85.6186798,41.9585791]},"n185955753":{"id":"n185955753","loc":[-85.620773,41.9555854]},"n185955755":{"id":"n185955755","loc":[-85.6212652,41.9559891]},"n185955748":{"id":"n185955748","loc":[-85.620722,41.954858]},"n185955751":{"id":"n185955751","loc":[-85.6206912,41.955367]},"n185967987":{"id":"n185967987","loc":[-85.6159351,41.9585809]},"n185971407":{"id":"n185971407","loc":[-85.6159142,41.9577578]},"n185971570":{"id":"n185971570","loc":[-85.6162248,41.95603]},"n185971572":{"id":"n185971572","loc":[-85.6160402,41.9560749]},"n185971574":{"id":"n185971574","loc":[-85.61593,41.956201]},"n185981301":{"id":"n185981301","loc":[-85.6158973,41.9581601]},"n394490762":{"id":"n394490762","loc":[-85.617193,41.954706]},"n394490764":{"id":"n394490764","loc":[-85.616773,41.954737]},"n394490787":{"id":"n394490787","loc":[-85.618972,41.954737]},"n394490790":{"id":"n394490790","loc":[-85.619046,41.954929]},"n394490794":{"id":"n394490794","loc":[-85.619922,41.955296]},"n394490796":{"id":"n394490796","loc":[-85.61991,41.95501]},"n394490798":{"id":"n394490798","loc":[-85.619974,41.954751]},"n1819790677":{"id":"n1819790677","loc":[-85.6187031,41.9550522]},"n1819790787":{"id":"n1819790787","loc":[-85.6186436,41.9552022]},"n1819790828":{"id":"n1819790828","loc":[-85.6185127,41.9553393]},"w17966857":{"id":"w17966857","tags":{"access":"private","highway":"service","name":"Sable River Rd"},"nodes":["n185972059","n185981301"]},"w34369814":{"id":"w34369814","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490787","n394490790","n394490792","n394490794","n394490796","n394490798","n394490787"]},"w17964176":{"id":"w17964176","tags":{"highway":"residential"},"nodes":["n185955747","n185955748","n185955751","n185955753","n185955755"]},"w17965838":{"id":"w17965838","tags":{"access":"private","highway":"service","name":"Pine River Rd"},"nodes":["n185971405","n185971407"]},"w17965476":{"id":"w17965476","tags":{"access":"private","highway":"service","name":"Raisin River Rd"},"nodes":["n185967985","n185967987"]},"w17965913":{"id":"w17965913","tags":{"access":"private","highway":"service","name":"Shiawassee River Rd"},"nodes":["n185972054","n1819790677","n1819790787","n1819790828","n1819790724","n1819790896","n185971565","n1819790799","n1819790735","n185972055","n185972057","n185971405","n185972059","n185967985","n185972060"]},"w34369811":{"id":"w34369811","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490762","n394490764","n394490766","n394490768","n394490762"]},"w17965854":{"id":"w17965854","tags":{"access":"private","highway":"service","name":"Sturgeon River Rd"},"nodes":["n185971565","n185971570","n185971572","n185971574"]},"n2139795769":{"id":"n2139795769","loc":[-85.6250804,41.9608796]},"n2139795770":{"id":"n2139795770","loc":[-85.6250315,41.9613684]},"n2139795771":{"id":"n2139795771","loc":[-85.6249671,41.9614362]},"n2139795772":{"id":"n2139795772","loc":[-85.6249698,41.961522]},"n2139795773":{"id":"n2139795773","loc":[-85.6250798,41.9615838]},"n2139795774":{"id":"n2139795774","loc":[-85.6252273,41.9615639]},"n2139795775":{"id":"n2139795775","loc":[-85.6252863,41.9614622]},"n2139795776":{"id":"n2139795776","loc":[-85.6252273,41.9613764]},"n2139795777":{"id":"n2139795777","loc":[-85.6251227,41.9613525]},"n2139795778":{"id":"n2139795778","loc":[-85.6249564,41.9612527]},"n2139795779":{"id":"n2139795779","loc":[-85.6249846,41.9610254]},"n2139795780":{"id":"n2139795780","loc":[-85.6266725,41.9599647]},"n2139795781":{"id":"n2139795781","loc":[-85.6259162,41.9599711]},"n2139795782":{"id":"n2139795782","loc":[-85.6257185,41.960019]},"n2139795783":{"id":"n2139795783","loc":[-85.6255509,41.9601213]},"n185963539":{"id":"n185963539","loc":[-85.615718,41.983893]},"n185964418":{"id":"n185964418","loc":[-85.616626,42.049512]},"n185966614":{"id":"n185966614","loc":[-85.615514,41.976603]},"n185966635":{"id":"n185966635","loc":[-85.616118,42.013017]},"n185969040":{"id":"n185969040","loc":[-85.615632,41.972357]},"n185969070":{"id":"n185969070","loc":[-85.619145,41.967648]},"n185972156":{"id":"n185972156","loc":[-85.621894,41.963956]},"n185972157":{"id":"n185972157","loc":[-85.621806,41.964077]},"n185972158":{"id":"n185972158","loc":[-85.620848,41.965341]},"n185972159":{"id":"n185972159","loc":[-85.620684,41.965558]},"n185972160":{"id":"n185972160","loc":[-85.620621,41.965658]},"n185972161":{"id":"n185972161","loc":[-85.617844,41.969359]},"n185972162":{"id":"n185972162","loc":[-85.616843,41.97068]},"n185972164":{"id":"n185972164","loc":[-85.616714,41.970839]},"n185972166":{"id":"n185972166","loc":[-85.615879,41.971969]},"n185972168":{"id":"n185972168","loc":[-85.615748,41.972159]},"n185972170":{"id":"n185972170","loc":[-85.615589,41.972502]},"n185972172":{"id":"n185972172","loc":[-85.615542,41.972733]},"n185972175":{"id":"n185972175","loc":[-85.615524,41.972947]},"n185972177":{"id":"n185972177","loc":[-85.615512,41.973715]},"n185972179":{"id":"n185972179","loc":[-85.615513,41.976496]},"n185972180":{"id":"n185972180","loc":[-85.615538,41.977246]},"n185972181":{"id":"n185972181","loc":[-85.61558,41.982139]},"n185972184":{"id":"n185972184","loc":[-85.61557,41.983317]},"n185972186":{"id":"n185972186","loc":[-85.615591,41.983463]},"n185972188":{"id":"n185972188","loc":[-85.615763,41.984146]},"n185972190":{"id":"n185972190","loc":[-85.615814,41.98435]},"n185972192":{"id":"n185972192","loc":[-85.615965,41.998453]},"n185972194":{"id":"n185972194","loc":[-85.615982,42.001237]},"n185972195":{"id":"n185972195","loc":[-85.616055,42.00555]},"n185972197":{"id":"n185972197","loc":[-85.616134,42.014887]},"n185972199":{"id":"n185972199","loc":[-85.616177,42.018465]},"n185972201":{"id":"n185972201","loc":[-85.616298,42.027627]},"n185972203":{"id":"n185972203","loc":[-85.616513,42.042212]},"w203968015":{"id":"w203968015","tags":{"highway":"residential"},"nodes":["n2139795768","n2139795769"]},"w17965932":{"id":"w17965932","tags":{"highway":"residential","name":"Buckhorn Road","name_1":"County Highway 122"},"nodes":["n185972155","n185972156","n185972157","n185972158","n185972159","n185972160","n185969070","n185972161","n185972162","n185972164","n185972166","n185972168","n185969040","n185972170","n185972172","n185972175","n185972177","n185972179","n185966614","n185972180","n185972181","n185972184","n185972186","n185963539","n185972188","n185972190","n185972192","n185972194","n185972195","n185966635","n185972197","n185972199","n185972201","n185972203","n185964418"]},"w203968016":{"id":"w203968016","tags":{"highway":"residential","name":"New Jersey Court"},"nodes":["n2139795770","n2139795771","n2139795772","n2139795773","n2139795774","n2139795775","n2139795776","n2139795777","n2139795770","n2139795778","n2139795779","n2139795769"]},"w203968017":{"id":"w203968017","tags":{"highway":"residential","name":"Oklahoma Drive"},"nodes":["n2139795780","n2139795781","n2139795782","n2139795783","n2139795769"]},"n1819790528":{"id":"n1819790528","loc":[-85.6184827,41.960025]},"n1819790530":{"id":"n1819790530","loc":[-85.6168626,41.9605834]},"n1819790534":{"id":"n1819790534","loc":[-85.6197379,41.9617163]},"n1819790541":{"id":"n1819790541","loc":[-85.6198881,41.9620833]},"n1819790543":{"id":"n1819790543","loc":[-85.619695,41.9619397]},"n1819790547":{"id":"n1819790547","loc":[-85.6190298,41.9609504]},"n1819790555":{"id":"n1819790555","loc":[-85.6180471,41.9609788]},"n1819790559":{"id":"n1819790559","loc":[-85.6203817,41.9605436]},"n1819790583":{"id":"n1819790583","loc":[-85.6201564,41.9603282]},"n1819790590":{"id":"n1819790590","loc":[-85.617045,41.9598894]},"n1819790609":{"id":"n1819790609","loc":[-85.6177638,41.9598495]},"n1819790618":{"id":"n1819790618","loc":[-85.6195234,41.9610143]},"n1819790642":{"id":"n1819790642","loc":[-85.6181179,41.9627933]},"n1819790659":{"id":"n1819790659","loc":[-85.6174634,41.962897]},"n1819790665":{"id":"n1819790665","loc":[-85.6170343,41.9630885]},"n1819790674":{"id":"n1819790674","loc":[-85.6194697,41.9601925]},"n1819790685":{"id":"n1819790685","loc":[-85.6207722,41.9610665]},"n1819790687":{"id":"n1819790687","loc":[-85.6202315,41.9622109]},"n1819790697":{"id":"n1819790697","loc":[-85.6184505,41.9624662]},"n1819790726":{"id":"n1819790726","loc":[-85.6178926,41.9628492]},"n1819790738":{"id":"n1819790738","loc":[-85.6173347,41.9598016]},"n1819790762":{"id":"n1819790762","loc":[-85.6186221,41.9609105]},"n1819790774":{"id":"n1819790774","loc":[-85.6175922,41.9608308]},"n1819790781":{"id":"n1819790781","loc":[-85.6167768,41.9633198]},"n1819790796":{"id":"n1819790796","loc":[-85.619856,41.961461]},"n1819790811":{"id":"n1819790811","loc":[-85.6208215,41.9620195]},"n1819790833":{"id":"n1819790833","loc":[-85.618311,41.9612536]},"n1819790854":{"id":"n1819790854","loc":[-85.6183646,41.9626417]},"n1819790863":{"id":"n1819790863","loc":[-85.6204997,41.9608547]},"n1819790867":{"id":"n1819790867","loc":[-85.6184934,41.9621391]},"n1819790877":{"id":"n1819790877","loc":[-85.6206928,41.9621152]},"n1819790881":{"id":"n1819790881","loc":[-85.6170879,41.960735]},"n1819790891":{"id":"n1819790891","loc":[-85.6168304,41.9601207]},"n1819790898":{"id":"n1819790898","loc":[-85.619813,41.9612297]},"n1819790909":{"id":"n1819790909","loc":[-85.6167982,41.960376]},"n1819790912":{"id":"n1819790912","loc":[-85.6205855,41.9610462]},"n1819790544":{"id":"n1819790544","loc":[-85.612968,41.9707781]},"n1819790549":{"id":"n1819790549","loc":[-85.614395,41.9697172]},"n1819790552":{"id":"n1819790552","loc":[-85.6180535,41.9655536]},"n1819790554":{"id":"n1819790554","loc":[-85.6111227,41.9703713]},"n1819790560":{"id":"n1819790560","loc":[-85.6112729,41.9701958]},"n1819790563":{"id":"n1819790563","loc":[-85.6137512,41.9689917]},"n1819790564":{"id":"n1819790564","loc":[-85.6181072,41.9659205]},"n1819790595":{"id":"n1819790595","loc":[-85.6170021,41.9666863]},"n1819790605":{"id":"n1819790605","loc":[-85.6168948,41.9644527]},"n1819790606":{"id":"n1819790606","loc":[-85.6128071,41.9701081]},"n1819790607":{"id":"n1819790607","loc":[-85.6129251,41.9704032]},"n1819790612":{"id":"n1819790612","loc":[-85.6177638,41.9663912]},"n1819790615":{"id":"n1819790615","loc":[-85.6152533,41.9670373]},"n1819790622":{"id":"n1819790622","loc":[-85.6146739,41.9673804]},"n1819790623":{"id":"n1819790623","loc":[-85.6180428,41.9661838]},"n1819790625":{"id":"n1819790625","loc":[-85.6172918,41.9646202]},"n1819790645":{"id":"n1819790645","loc":[-85.6178067,41.965043]},"n1819790647":{"id":"n1819790647","loc":[-85.6143306,41.9712488]},"n1819790649":{"id":"n1819790649","loc":[-85.6147383,41.9707702]},"n1819790654":{"id":"n1819790654","loc":[-85.6157361,41.9668459]},"n1819790657":{"id":"n1819790657","loc":[-85.6145666,41.9710733]},"n1819790668":{"id":"n1819790668","loc":[-85.6166909,41.9642692]},"n1819790671":{"id":"n1819790671","loc":[-85.6141482,41.9696538]},"n1819790679":{"id":"n1819790679","loc":[-85.6148349,41.9705388]},"n1819790686":{"id":"n1819790686","loc":[-85.6139551,41.9695501]},"n1819790696":{"id":"n1819790696","loc":[-85.6119703,41.9699087]},"n1819790704":{"id":"n1819790704","loc":[-85.6140731,41.9684174]},"n1819790706":{"id":"n1819790706","loc":[-85.6124745,41.9699246]},"n1819790718":{"id":"n1819790718","loc":[-85.6165407,41.9636868]},"n1819790720":{"id":"n1819790720","loc":[-85.61388,41.9687365]},"n1819790731":{"id":"n1819790731","loc":[-85.6165193,41.9639421]},"n1819790739":{"id":"n1819790739","loc":[-85.6146739,41.9699964]},"n1819790753":{"id":"n1819790753","loc":[-85.6173883,41.9665747]},"n1819790760":{"id":"n1819790760","loc":[-85.6133221,41.9712089]},"n1819790767":{"id":"n1819790767","loc":[-85.6116698,41.9699246]},"n1819790779":{"id":"n1819790779","loc":[-85.6130753,41.9710573]},"n1819790791":{"id":"n1819790791","loc":[-85.6137083,41.9692869]},"n1819790795":{"id":"n1819790795","loc":[-85.6141482,41.9679627]},"n1819790798":{"id":"n1819790798","loc":[-85.6137727,41.9694305]},"n1819790836":{"id":"n1819790836","loc":[-85.6143842,41.9676037]},"n1819790915":{"id":"n1819790915","loc":[-85.6148456,41.9702756]},"n1819790926":{"id":"n1819790926","loc":[-85.6138371,41.9713525]},"n1819790927":{"id":"n1819790927","loc":[-85.6141053,41.9713525]},"n1819790931":{"id":"n1819790931","loc":[-85.6162832,41.966814]},"n1821014625":{"id":"n1821014625","loc":[-85.5960611,41.9808498]},"n1821014627":{"id":"n1821014627","loc":[-85.5565843,42.010982]},"n1821014629":{"id":"n1821014629","loc":[-85.5971541,41.9805808]},"n1821014632":{"id":"n1821014632","loc":[-85.6061837,41.9725907]},"n1821014633":{"id":"n1821014633","loc":[-85.5247773,42.025766]},"n1821014635":{"id":"n1821014635","loc":[-85.5908938,41.9902384]},"n1821014636":{"id":"n1821014636","loc":[-85.5917682,41.9860637]},"n1821014637":{"id":"n1821014637","loc":[-85.5456556,42.0166797]},"n1821014638":{"id":"n1821014638","loc":[-85.5795749,42.0032352]},"n1821014639":{"id":"n1821014639","loc":[-85.6103988,41.9723456]},"n1821014642":{"id":"n1821014642","loc":[-85.5818816,42.0022466]},"n1821014643":{"id":"n1821014643","loc":[-85.5570604,42.0091586]},"n1821014644":{"id":"n1821014644","loc":[-85.5952886,41.9803792]},"n1821014645":{"id":"n1821014645","loc":[-85.5780366,42.0040343]},"n1821014646":{"id":"n1821014646","loc":[-85.6050505,41.9751971]},"n1821014647":{"id":"n1821014647","loc":[-85.5854435,41.9946162]},"n1821014648":{"id":"n1821014648","loc":[-85.5452278,42.0168768]},"n1821014649":{"id":"n1821014649","loc":[-85.6023254,41.9780166]},"n1821014651":{"id":"n1821014651","loc":[-85.5761899,42.0046783]},"n1821014653":{"id":"n1821014653","loc":[-85.5897351,41.9876707]},"n1821014657":{"id":"n1821014657","loc":[-85.5963601,41.9808998]},"n1821014658":{"id":"n1821014658","loc":[-85.5892952,41.9951983]},"n1821014660":{"id":"n1821014660","loc":[-85.5778328,42.0037194]},"n1821014661":{"id":"n1821014661","loc":[-85.5541475,42.0125705]},"n1821014663":{"id":"n1821014663","loc":[-85.5914047,41.9856469]},"n1821014664":{"id":"n1821014664","loc":[-85.6101681,41.9727723]},"n1821014665":{"id":"n1821014665","loc":[-85.5910172,41.9854696]},"n1821014666":{"id":"n1821014666","loc":[-85.5398688,42.0187699]},"n1821014667":{"id":"n1821014667","loc":[-85.5218752,42.0282884]},"n1821014668":{"id":"n1821014668","loc":[-85.5159582,42.0329384]},"n1821014669":{"id":"n1821014669","loc":[-85.5898102,41.9847319]},"n1821014670":{"id":"n1821014670","loc":[-85.5734809,42.0066235]},"n1821014671":{"id":"n1821014671","loc":[-85.5922939,41.980852]},"n1821014672":{"id":"n1821014672","loc":[-85.6023629,41.9781163]},"n1821014674":{"id":"n1821014674","loc":[-85.5409953,42.0191724]},"n1821014676":{"id":"n1821014676","loc":[-85.584435,41.9949909]},"n1821014677":{"id":"n1821014677","loc":[-85.5972399,41.9783835]},"n1821014678":{"id":"n1821014678","loc":[-85.5616738,42.0071337]},"n1821014681":{"id":"n1821014681","loc":[-85.5202994,42.0310755]},"n1821014682":{"id":"n1821014682","loc":[-85.5915912,41.9857767]},"n1821014684":{"id":"n1821014684","loc":[-85.6022288,41.977897]},"n1821014687":{"id":"n1821014687","loc":[-85.5933024,41.9846362]},"n1821014688":{"id":"n1821014688","loc":[-85.5846871,41.9956169]},"n1821014689":{"id":"n1821014689","loc":[-85.5898209,41.99037]},"n1821014691":{"id":"n1821014691","loc":[-85.5448939,42.0149261]},"n1821014692":{"id":"n1821014692","loc":[-85.5977763,41.9786348]},"n1821014694":{"id":"n1821014694","loc":[-85.5767706,42.0034523]},"n1821014695":{"id":"n1821014695","loc":[-85.6103559,41.9726766]},"n1821014697":{"id":"n1821014697","loc":[-85.5922134,41.9809876]},"n1821014698":{"id":"n1821014698","loc":[-85.5935277,41.9831728]},"n1821014700":{"id":"n1821014700","loc":[-85.5674674,42.0078273]},"n1821014703":{"id":"n1821014703","loc":[-85.6021,41.9778053]},"n1821014704":{"id":"n1821014704","loc":[-85.5756763,42.0053737]},"n1821014705":{"id":"n1821014705","loc":[-85.5887695,41.9895207]},"n1821014707":{"id":"n1821014707","loc":[-85.6061073,41.9746866]},"n1821014708":{"id":"n1821014708","loc":[-85.6033446,41.9751692]},"n1821014710":{"id":"n1821014710","loc":[-85.5180986,42.0322332]},"n1821014711":{"id":"n1821014711","loc":[-85.543365,42.0163569]},"n1821014712":{"id":"n1821014712","loc":[-85.6030656,41.9753646]},"n1821014713":{"id":"n1821014713","loc":[-85.6104417,41.9704792]},"n1821014714":{"id":"n1821014714","loc":[-85.5205716,42.030998]},"n1821014716":{"id":"n1821014716","loc":[-85.516382,42.032536]},"n1821014717":{"id":"n1821014717","loc":[-85.5932863,41.9820882]},"n1821014718":{"id":"n1821014718","loc":[-85.5361928,42.0194974]},"n1821014720":{"id":"n1821014720","loc":[-85.6011613,41.9773586]},"n1821014721":{"id":"n1821014721","loc":[-85.554287,42.0109124]},"n1821014722":{"id":"n1821014722","loc":[-85.5577524,42.0103425]},"n1821014725":{"id":"n1821014725","loc":[-85.5867256,41.9921004]},"n1821014726":{"id":"n1821014726","loc":[-85.5856045,41.9968807]},"n1821014727":{"id":"n1821014727","loc":[-85.5545445,42.0106454]},"n1821014728":{"id":"n1821014728","loc":[-85.5923797,41.9842534]},"n1821014729":{"id":"n1821014729","loc":[-85.5696346,42.0081462]},"n1821014730":{"id":"n1821014730","loc":[-85.5998322,41.9786884]},"n1821014735":{"id":"n1821014735","loc":[-85.5337426,42.0218266]},"n1821014736":{"id":"n1821014736","loc":[-85.5847944,41.994672]},"n1821014740":{"id":"n1821014740","loc":[-85.5315271,42.0238669]},"n1821014741":{"id":"n1821014741","loc":[-85.5248846,42.027085]},"n1821014742":{"id":"n1821014742","loc":[-85.5853376,41.997018]},"n1821014743":{"id":"n1821014743","loc":[-85.5894883,41.988811]},"n1821014745":{"id":"n1821014745","loc":[-85.6095311,41.9726226]},"n1821014746":{"id":"n1821014746","loc":[-85.5531511,42.0133416]},"n1821014747":{"id":"n1821014747","loc":[-85.5735882,42.007058]},"n1821014749":{"id":"n1821014749","loc":[-85.5428554,42.0164366]},"n1821014751":{"id":"n1821014751","loc":[-85.5395255,42.0186304]},"n1821014752":{"id":"n1821014752","loc":[-85.571378,42.0083176]},"n1821014754":{"id":"n1821014754","loc":[-85.5541918,42.0113925]},"n1821014755":{"id":"n1821014755","loc":[-85.5278029,42.0250806]},"n1821014756":{"id":"n1821014756","loc":[-85.5936725,41.9827102]},"n1821014757":{"id":"n1821014757","loc":[-85.5176266,42.0346677]},"n1821014758":{"id":"n1821014758","loc":[-85.6096692,41.9714245]},"n1821014759":{"id":"n1821014759","loc":[-85.5770321,42.0034266]},"n1821014761":{"id":"n1821014761","loc":[-85.5988921,41.9779369]},"n1821014762":{"id":"n1821014762","loc":[-85.5811788,42.0024499]},"n1821014763":{"id":"n1821014763","loc":[-85.5154003,42.0381101]},"n1821014764":{"id":"n1821014764","loc":[-85.5155827,42.0374089]},"n1821014765":{"id":"n1821014765","loc":[-85.5891249,41.9884978]},"n1821014766":{"id":"n1821014766","loc":[-85.5313863,42.0238293]},"n1821014768":{"id":"n1821014768","loc":[-85.593297,41.9833363]},"n1821014769":{"id":"n1821014769","loc":[-85.5849446,41.9957245]},"n1821014770":{"id":"n1821014770","loc":[-85.5537774,42.0130847]},"n1821014771":{"id":"n1821014771","loc":[-85.6111766,41.9706069]},"n1821014772":{"id":"n1821014772","loc":[-85.5585477,42.008989]},"n1821014774":{"id":"n1821014774","loc":[-85.5928142,41.9852623]},"n1821014777":{"id":"n1821014777","loc":[-85.5891933,41.9882608]},"n1821014778":{"id":"n1821014778","loc":[-85.5926909,41.9817532]},"n1821014779":{"id":"n1821014779","loc":[-85.5260272,42.0252201]},"n1821014781":{"id":"n1821014781","loc":[-85.5894615,41.9950468]},"n1821014782":{"id":"n1821014782","loc":[-85.5461063,42.0143242]},"n1821014783":{"id":"n1821014783","loc":[-85.5711527,42.0085886]},"n1821014784":{"id":"n1821014784","loc":[-85.5329379,42.0218624]},"n1821014786":{"id":"n1821014786","loc":[-85.583047,42.0020252]},"n1821014787":{"id":"n1821014787","loc":[-85.5758962,42.0054095]},"n1821014788":{"id":"n1821014788","loc":[-85.5626354,42.0077733]},"n1821014789":{"id":"n1821014789","loc":[-85.6029852,41.9755999]},"n1821014790":{"id":"n1821014790","loc":[-85.5892362,41.9886755]},"n1821014791":{"id":"n1821014791","loc":[-85.5157597,42.0372017]},"n1821014793":{"id":"n1821014793","loc":[-85.6054582,41.9751094]},"n1821014794":{"id":"n1821014794","loc":[-85.5986936,41.9778412]},"n1821014795":{"id":"n1821014795","loc":[-85.5880775,41.98976]},"n1821014796":{"id":"n1821014796","loc":[-85.5858727,41.9963624]},"n1821014798":{"id":"n1821014798","loc":[-85.5792543,42.0035958]},"n1821014799":{"id":"n1821014799","loc":[-85.5921665,41.9838326]},"n1821014801":{"id":"n1821014801","loc":[-85.599214,41.9782599]},"n1821014802":{"id":"n1821014802","loc":[-85.5571905,42.0090967]},"n1821014803":{"id":"n1821014803","loc":[-85.5426891,42.0173612]},"n1821014804":{"id":"n1821014804","loc":[-85.5889626,41.9896404]},"n1821014805":{"id":"n1821014805","loc":[-85.5491264,42.0141648]},"n1821014806":{"id":"n1821014806","loc":[-85.5618897,42.0072631]},"n1821014808":{"id":"n1821014808","loc":[-85.5573501,42.0109802]},"n1821014809":{"id":"n1821014809","loc":[-85.5983463,41.9778031]},"n1821014810":{"id":"n1821014810","loc":[-85.5885173,41.9895128]},"n1821014811":{"id":"n1821014811","loc":[-85.6084998,41.9721143]},"n1821014812":{"id":"n1821014812","loc":[-85.5737598,42.0056389]},"n1821014814":{"id":"n1821014814","loc":[-85.5542173,42.0118132]},"n1821014816":{"id":"n1821014816","loc":[-85.5277868,42.024451]},"n1821014817":{"id":"n1821014817","loc":[-85.5403999,42.0191724]},"n1821014819":{"id":"n1821014819","loc":[-85.5983879,41.9791452]},"n1821014820":{"id":"n1821014820","loc":[-85.5891302,41.9897578]},"n1821014822":{"id":"n1821014822","loc":[-85.5930731,41.9805108]},"n1821014824":{"id":"n1821014824","loc":[-85.515395,42.0378471]},"n1821014825":{"id":"n1821014825","loc":[-85.5352755,42.0205136]},"n1821014826":{"id":"n1821014826","loc":[-85.5502744,42.0133398]},"n1821014828":{"id":"n1821014828","loc":[-85.5701295,42.0088256]},"n1821014830":{"id":"n1821014830","loc":[-85.5888929,41.9953099]},"n1821014832":{"id":"n1821014832","loc":[-85.5880077,41.9901547]},"n1821014833":{"id":"n1821014833","loc":[-85.5451192,42.0157072]},"n1821014834":{"id":"n1821014834","loc":[-85.6096478,41.9711932]},"n1821014835":{"id":"n1821014835","loc":[-85.5806424,42.0026532]},"n1821014836":{"id":"n1821014836","loc":[-85.5911674,41.9868732]},"n1821014838":{"id":"n1821014838","loc":[-85.5930302,41.9836571]},"n1821014839":{"id":"n1821014839","loc":[-85.588925,41.9938148]},"n1821014840":{"id":"n1821014840","loc":[-85.6111874,41.9705311]},"n1821014841":{"id":"n1821014841","loc":[-85.5680843,42.0075842]},"n1821014842":{"id":"n1821014842","loc":[-85.6012793,41.9775062]},"n1821014843":{"id":"n1821014843","loc":[-85.5855562,41.9989777]},"n1821014844":{"id":"n1821014844","loc":[-85.5506137,42.0131662]},"n1821014845":{"id":"n1821014845","loc":[-85.5270049,42.025457]},"n1821014846":{"id":"n1821014846","loc":[-85.5257054,42.025244]},"n1821014847":{"id":"n1821014847","loc":[-85.6011184,41.9771832]},"n1821014848":{"id":"n1821014848","loc":[-85.515534,42.0389234]},"n1821014850":{"id":"n1821014850","loc":[-85.5847032,42.0010347]},"n1821014853":{"id":"n1821014853","loc":[-85.5361499,42.019063]},"n1821014854":{"id":"n1821014854","loc":[-85.5439176,42.0165721]},"n1821014855":{"id":"n1821014855","loc":[-85.5838825,42.0017284]},"n1821014857":{"id":"n1821014857","loc":[-85.5542173,42.0122317]},"n1821014859":{"id":"n1821014859","loc":[-85.5708201,42.0089195]},"n1821014860":{"id":"n1821014860","loc":[-85.5844833,41.9954415]},"n1821014862":{"id":"n1821014862","loc":[-85.5223204,42.0295396]},"n1821014863":{"id":"n1821014863","loc":[-85.5777898,42.0035918]},"n1821014864":{"id":"n1821014864","loc":[-85.591044,41.9898078]},"n1821014865":{"id":"n1821014865","loc":[-85.5973204,41.980182]},"n1821014866":{"id":"n1821014866","loc":[-85.5699578,42.0085825]},"n1821014867":{"id":"n1821014867","loc":[-85.5210598,42.0305278]},"n1821014868":{"id":"n1821014868","loc":[-85.5929108,41.9819008]},"n1821014869":{"id":"n1821014869","loc":[-85.5279799,42.0242995]},"n1821014870":{"id":"n1821014870","loc":[-85.5196114,42.0320539]},"n1821014871":{"id":"n1821014871","loc":[-85.5785449,42.0040883]},"n1821014872":{"id":"n1821014872","loc":[-85.588292,41.9895766]},"n1821014873":{"id":"n1821014873","loc":[-85.5160172,42.0331775]},"n1821014874":{"id":"n1821014874","loc":[-85.5688849,42.0077016]},"n1821014876":{"id":"n1821014876","loc":[-85.5857976,41.9996036]},"n1821014879":{"id":"n1821014879","loc":[-85.5990906,41.9780765]},"n1821014881":{"id":"n1821014881","loc":[-85.5483647,42.0144279]},"n1821014883":{"id":"n1821014883","loc":[-85.5691209,42.0077972]},"n1821014885":{"id":"n1821014885","loc":[-85.6076844,41.9721103]},"n1821014886":{"id":"n1821014886","loc":[-85.6015489,41.9766147]},"n1821014887":{"id":"n1821014887","loc":[-85.574822,42.0052802]},"n1821014888":{"id":"n1821014888","loc":[-85.5880024,41.9899593]},"n1821014890":{"id":"n1821014890","loc":[-85.5909421,41.9893772]},"n1821014892":{"id":"n1821014892","loc":[-85.5497326,42.0138141]},"n1821014893":{"id":"n1821014893","loc":[-85.5167106,42.0357811]},"n1821014895":{"id":"n1821014895","loc":[-85.5844404,41.9952501]},"n1821014896":{"id":"n1821014896","loc":[-85.5362465,42.0192662]},"n1821014898":{"id":"n1821014898","loc":[-85.5906095,41.9889147]},"n1821014899":{"id":"n1821014899","loc":[-85.5590667,42.0089354]},"n1821014900":{"id":"n1821014900","loc":[-85.5921598,41.9844209]},"n1821014902":{"id":"n1821014902","loc":[-85.5778971,42.0039266]},"n1821014903":{"id":"n1821014903","loc":[-85.603012,41.9761981]},"n1821014904":{"id":"n1821014904","loc":[-85.6108977,41.9706787]},"n1821014905":{"id":"n1821014905","loc":[-85.5685738,42.0076139]},"n1821014906":{"id":"n1821014906","loc":[-85.5392787,42.0186304]},"n1821014907":{"id":"n1821014907","loc":[-85.5227885,42.0274972]},"n1821014908":{"id":"n1821014908","loc":[-85.5857547,41.9961431]},"n1821014910":{"id":"n1821014910","loc":[-85.5610354,42.0072812]},"n1821014911":{"id":"n1821014911","loc":[-85.5209632,42.0308705]},"n1821014912":{"id":"n1821014912","loc":[-85.5709757,42.0087959]},"n1821014913":{"id":"n1821014913","loc":[-85.59231,41.9839344]},"n1821014914":{"id":"n1821014914","loc":[-85.5375245,42.0185865]},"n1821014916":{"id":"n1821014916","loc":[-85.5901548,41.9839841]},"n1821014917":{"id":"n1821014917","loc":[-85.5611213,42.0086405]},"n1821014918":{"id":"n1821014918","loc":[-85.5360426,42.0198122]},"n1821014919":{"id":"n1821014919","loc":[-85.5862817,41.9948691]},"n1821014921":{"id":"n1821014921","loc":[-85.5469807,42.0144438]},"n1821014922":{"id":"n1821014922","loc":[-85.5761309,42.0053838]},"n1821014924":{"id":"n1821014924","loc":[-85.516264,42.0332971]},"n1821014925":{"id":"n1821014925","loc":[-85.5277224,42.0246661]},"n1821014926":{"id":"n1821014926","loc":[-85.5980016,41.9798231]},"n1821014928":{"id":"n1821014928","loc":[-85.5924548,41.9806965]},"n1821014930":{"id":"n1821014930","loc":[-85.5899121,41.985023]},"n1821014931":{"id":"n1821014931","loc":[-85.5706015,42.0089492]},"n1821014932":{"id":"n1821014932","loc":[-85.515926,42.033046]},"n1821014933":{"id":"n1821014933","loc":[-85.5982377,41.9796796]},"n1821014936":{"id":"n1821014936","loc":[-85.5475721,42.0145253]},"n1821014938":{"id":"n1821014938","loc":[-85.5895701,41.9902323]},"n1821014939":{"id":"n1821014939","loc":[-85.6030495,41.9759947]},"n1821014942":{"id":"n1821014942","loc":[-85.6094721,41.9724989]},"n1821014944":{"id":"n1821014944","loc":[-85.5921973,41.9811112]},"n1821014945":{"id":"n1821014945","loc":[-85.5223526,42.0291332]},"n1821014946":{"id":"n1821014946","loc":[-85.5965103,41.9808998]},"n1821014948":{"id":"n1821014948","loc":[-85.517766,42.0349227]},"n1821014950":{"id":"n1821014950","loc":[-85.5889894,41.990996]},"n1821014951":{"id":"n1821014951","loc":[-85.5601932,42.0092902]},"n1821014954":{"id":"n1821014954","loc":[-85.6028135,41.9764055]},"n1821014955":{"id":"n1821014955","loc":[-85.5520621,42.0130666]},"n1821014956":{"id":"n1821014956","loc":[-85.593002,41.9839344]},"n1821014957":{"id":"n1821014957","loc":[-85.515926,42.0369666]},"n1821014960":{"id":"n1821014960","loc":[-85.5761255,42.003877]},"n1821014961":{"id":"n1821014961","loc":[-85.5716355,42.007911]},"n1821014962":{"id":"n1821014962","loc":[-85.5575378,42.0109045]},"n1821014963":{"id":"n1821014963","loc":[-85.5735667,42.0068188]},"n1821014964":{"id":"n1821014964","loc":[-85.5915214,41.9865861]},"n1821014965":{"id":"n1821014965","loc":[-85.5866344,41.9923157]},"n1821014967":{"id":"n1821014967","loc":[-85.5283138,42.0242256]},"n1821014968":{"id":"n1821014968","loc":[-85.5177875,42.0355801]},"n1821014969":{"id":"n1821014969","loc":[-85.548071,42.0144934]},"n1821014972":{"id":"n1821014972","loc":[-85.5611159,42.0088557]},"n1821014973":{"id":"n1821014973","loc":[-85.541686,42.0188757]},"n1821014974":{"id":"n1821014974","loc":[-85.5917628,41.9862631]},"n1821014975":{"id":"n1821014975","loc":[-85.5854864,41.9959478]},"n1821014977":{"id":"n1821014977","loc":[-85.609102,41.9722317]},"n1821014980":{"id":"n1821014980","loc":[-85.5761202,42.0042438]},"n1821014982":{"id":"n1821014982","loc":[-85.5465944,42.0143601]},"n1821014983":{"id":"n1821014983","loc":[-85.5173261,42.0342732]},"n1821014984":{"id":"n1821014984","loc":[-85.5897297,41.9888509]},"n1821014985":{"id":"n1821014985","loc":[-85.5856688,41.999181]},"n1821014986":{"id":"n1821014986","loc":[-85.5344011,42.0217251]},"n1821014987":{"id":"n1821014987","loc":[-85.601467,41.9768203]},"n1821014988":{"id":"n1821014988","loc":[-85.5457254,42.0165123]},"n1821014989":{"id":"n1821014989","loc":[-85.6023482,41.9784332]},"n1821014991":{"id":"n1821014991","loc":[-85.5361606,42.01823]},"n1821014992":{"id":"n1821014992","loc":[-85.5178465,42.0351139]},"n1821014995":{"id":"n1821014995","loc":[-85.5634293,42.0078092]},"n1821014996":{"id":"n1821014996","loc":[-85.573497,42.0072015]},"n1821014997":{"id":"n1821014997","loc":[-85.5976328,41.9799725]},"n1821014998":{"id":"n1821014998","loc":[-85.5210651,42.0303166]},"n1821015003":{"id":"n1821015003","loc":[-85.5222131,42.0288064]},"n1821015004":{"id":"n1821015004","loc":[-85.5897941,41.984405]},"n1821015005":{"id":"n1821015005","loc":[-85.5975725,41.9776099]},"n1821015006":{"id":"n1821015006","loc":[-85.5765708,42.0034903]},"n1821015007":{"id":"n1821015007","loc":[-85.5250187,42.026559]},"n1821015009":{"id":"n1821015009","loc":[-85.5426998,42.0166279]},"n1821015010":{"id":"n1821015010","loc":[-85.5957606,41.9806584]},"n1821015011":{"id":"n1821015011","loc":[-85.5262753,42.0252497]},"n1821015012":{"id":"n1821015012","loc":[-85.5266455,42.0253374]},"n1821015014":{"id":"n1821015014","loc":[-85.5515632,42.0130187]},"n1821015015":{"id":"n1821015015","loc":[-85.6024058,41.9765212]},"n1821015017":{"id":"n1821015017","loc":[-85.5175032,42.0357156]},"n1821015018":{"id":"n1821015018","loc":[-85.5302718,42.0236039]},"n1821015019":{"id":"n1821015019","loc":[-85.6024005,41.9782759]},"n1821015020":{"id":"n1821015020","loc":[-85.5907758,41.9890821]},"n1821015021":{"id":"n1821015021","loc":[-85.6019445,41.9777215]},"n1821015022":{"id":"n1821015022","loc":[-85.5942854,41.9800881]},"n1821015024":{"id":"n1821015024","loc":[-85.5325826,42.0222711]},"n1821015029":{"id":"n1821015029","loc":[-85.555093,42.0105316]},"n1821015033":{"id":"n1821015033","loc":[-85.5249704,42.0270372]},"n1821015034":{"id":"n1821015034","loc":[-85.5243965,42.0272205]},"n1821015038":{"id":"n1821015038","loc":[-85.5413426,42.0190749]},"n1821015039":{"id":"n1821015039","loc":[-85.5920431,41.9848175]},"n1821015041":{"id":"n1821015041","loc":[-85.5577685,42.0106015]},"n1821015042":{"id":"n1821015042","loc":[-85.5453606,42.0158866]},"n1821015045":{"id":"n1821015045","loc":[-85.5333228,42.0217889]},"n1821015046":{"id":"n1821015046","loc":[-85.5426891,42.0175924]},"n1821015048":{"id":"n1821015048","loc":[-85.5886836,41.9936474]},"n1821015050":{"id":"n1821015050","loc":[-85.6001152,41.9786467]},"n1821015051":{"id":"n1821015051","loc":[-85.6094064,41.9723655]},"n1821015053":{"id":"n1821015053","loc":[-85.605721,41.9749738]},"n1821015055":{"id":"n1821015055","loc":[-85.6106791,41.9705048]},"n1821015057":{"id":"n1821015057","loc":[-85.5210437,42.0307071]},"n1821015059":{"id":"n1821015059","loc":[-85.5995694,41.9786725]},"n1821015060":{"id":"n1821015060","loc":[-85.5371638,42.0182938]},"n1821015062":{"id":"n1821015062","loc":[-85.6111766,41.9704593]},"n1821015065":{"id":"n1821015065","loc":[-85.577704,42.0034921]},"n1821015067":{"id":"n1821015067","loc":[-85.5570067,42.0093699]},"n1821015068":{"id":"n1821015068","loc":[-85.5920364,41.9845525]},"n1821015069":{"id":"n1821015069","loc":[-85.5252065,42.0253954]},"n1821015072":{"id":"n1821015072","loc":[-85.5664159,42.0088517]},"n1821015073":{"id":"n1821015073","loc":[-85.5880399,41.991905]},"n1821015075":{"id":"n1821015075","loc":[-85.6099871,41.9727861]},"n1821015076":{"id":"n1821015076","loc":[-85.5319603,42.0231478]},"n1821015078":{"id":"n1821015078","loc":[-85.6036088,41.9751112]},"n1821015080":{"id":"n1821015080","loc":[-85.5983128,41.9789179]},"n1821015082":{"id":"n1821015082","loc":[-85.5614069,42.0071395]},"n1821015083":{"id":"n1821015083","loc":[-85.60968,41.9709738]},"n1821015086":{"id":"n1821015086","loc":[-85.5914195,41.9837351]},"n1821015087":{"id":"n1821015087","loc":[-85.5895473,41.9948036]},"n1821015090":{"id":"n1821015090","loc":[-85.5929913,41.9851905]},"n1821015093":{"id":"n1821015093","loc":[-85.5907396,41.9838485]},"n1821015095":{"id":"n1821015095","loc":[-85.5893864,41.9880176]},"n1821015096":{"id":"n1821015096","loc":[-85.5788024,42.0039807]},"n1821015097":{"id":"n1821015097","loc":[-85.5630592,42.0078411]},"n1821015098":{"id":"n1821015098","loc":[-85.5350609,42.0211274]},"n1821015099":{"id":"n1821015099","loc":[-85.5967195,41.9808679]},"n1821015100":{"id":"n1821015100","loc":[-85.5666734,42.0088119]},"n1821015101":{"id":"n1821015101","loc":[-85.564694,42.0077675]},"n1821015103":{"id":"n1821015103","loc":[-85.6066544,41.9726527]},"n1821015104":{"id":"n1821015104","loc":[-85.6011827,41.9769838]},"n1821015105":{"id":"n1821015105","loc":[-85.5972131,41.9776697]},"n1821015106":{"id":"n1821015106","loc":[-85.5880828,41.9903341]},"n1821015107":{"id":"n1821015107","loc":[-85.5510268,42.0130626]},"n1821015108":{"id":"n1821015108","loc":[-85.6102164,41.970543]},"n1821015109":{"id":"n1821015109","loc":[-85.5905344,41.9853899]},"n1821015111":{"id":"n1821015111","loc":[-85.5888821,41.9913429]},"n1821015112":{"id":"n1821015112","loc":[-85.606295,41.9741921]},"n1821015114":{"id":"n1821015114","loc":[-85.5969556,41.9807443]},"n1821015115":{"id":"n1821015115","loc":[-85.5882223,41.9934081]},"n1821015116":{"id":"n1821015116","loc":[-85.6104471,41.9724971]},"n1821015118":{"id":"n1821015118","loc":[-85.5406091,42.0192162]},"n1821015120":{"id":"n1821015120","loc":[-85.589955,41.9888429]},"n1821015121":{"id":"n1821015121","loc":[-85.5598821,42.0092304]},"n1821015122":{"id":"n1821015122","loc":[-85.545598,42.0144097]},"n1821015123":{"id":"n1821015123","loc":[-85.5649528,42.0079965]},"n1821015125":{"id":"n1821015125","loc":[-85.5883993,41.9917814]},"n1821015126":{"id":"n1821015126","loc":[-85.5295785,42.0239967]},"n1821015129":{"id":"n1821015129","loc":[-85.5648723,42.0078809]},"n1821015132":{"id":"n1821015132","loc":[-85.564989,42.0081103]},"n1821015133":{"id":"n1821015133","loc":[-85.5946127,41.9800841]},"n1821015134":{"id":"n1821015134","loc":[-85.583448,42.0019078]},"n1821015135":{"id":"n1821015135","loc":[-85.5905934,41.9871842]},"n1821015137":{"id":"n1821015137","loc":[-85.610608,41.9704752]},"n1821015138":{"id":"n1821015138","loc":[-85.5752257,42.0052939]},"n1821015139":{"id":"n1821015139","loc":[-85.5893864,41.9943491]},"n1821015140":{"id":"n1821015140","loc":[-85.5426247,42.0169866]},"n1821015141":{"id":"n1821015141","loc":[-85.562001,42.0074526]},"n1821015142":{"id":"n1821015142","loc":[-85.5212046,42.0301094]},"n1821015143":{"id":"n1821015143","loc":[-85.602214,41.9784531]},"n1821015144":{"id":"n1821015144","loc":[-85.5858687,41.9948293]},"n1821015145":{"id":"n1821015145","loc":[-85.5608477,42.0074805]},"n1821015146":{"id":"n1821015146","loc":[-85.5651607,42.0083614]},"n1821015147":{"id":"n1821015147","loc":[-85.5288288,42.0242495]},"n1821015149":{"id":"n1821015149","loc":[-85.5450334,42.0146989]},"n1821015151":{"id":"n1821015151","loc":[-85.5578275,42.0092304]},"n1821015154":{"id":"n1821015154","loc":[-85.6056634,41.9724511]},"n1821015155":{"id":"n1821015155","loc":[-85.5902179,41.9852742]},"n1821015156":{"id":"n1821015156","loc":[-85.5156256,42.0387157]},"n1821015157":{"id":"n1821015157","loc":[-85.5734433,42.0059459]},"n1821015158":{"id":"n1821015158","loc":[-85.6050773,41.9731273]},"n1821015160":{"id":"n1821015160","loc":[-85.5223419,42.0275233]},"n1821015163":{"id":"n1821015163","loc":[-85.6053562,41.972525]},"n1821015164":{"id":"n1821015164","loc":[-85.5850412,41.9946082]},"n1821015165":{"id":"n1821015165","loc":[-85.5359031,42.0186326]},"n1821015166":{"id":"n1821015166","loc":[-85.5608745,42.0077635]},"n1821015169":{"id":"n1821015169","loc":[-85.572876,42.0073189]},"n1821015171":{"id":"n1821015171","loc":[-85.5875424,41.9919188]},"n1821015172":{"id":"n1821015172","loc":[-85.5240116,42.0272581]},"n1821015173":{"id":"n1821015173","loc":[-85.5318369,42.0236818]},"n1821015174":{"id":"n1821015174","loc":[-85.566888,42.0086923]},"n1821015175":{"id":"n1821015175","loc":[-85.5931522,41.9850669]},"n1821015176":{"id":"n1821015176","loc":[-85.5604842,42.0093199]},"n1821015177":{"id":"n1821015177","loc":[-85.5868168,41.9927543]},"n1821015178":{"id":"n1821015178","loc":[-85.6052275,41.9732549]},"n1821015179":{"id":"n1821015179","loc":[-85.5910118,41.9900431]},"n1821015182":{"id":"n1821015182","loc":[-85.5610032,42.0082897]},"n1821015183":{"id":"n1821015183","loc":[-85.5425443,42.0179431]},"n1821015184":{"id":"n1821015184","loc":[-85.5843277,42.0014055]},"n1821015186":{"id":"n1821015186","loc":[-85.5733307,42.0063564]},"n1821015188":{"id":"n1821015188","loc":[-85.5277385,42.0248694]},"n1821015189":{"id":"n1821015189","loc":[-85.5558427,42.0108168]},"n1821015190":{"id":"n1821015190","loc":[-85.5650587,42.0082618]},"n1821015191":{"id":"n1821015191","loc":[-85.5660351,42.0088278]},"n1821015192":{"id":"n1821015192","loc":[-85.5849768,41.9980049]},"n1821015194":{"id":"n1821015194","loc":[-85.5359139,42.0188199]},"n1821015195":{"id":"n1821015195","loc":[-85.593238,41.9849194]},"n1821015197":{"id":"n1821015197","loc":[-85.5850841,41.9983239]},"n1821015199":{"id":"n1821015199","loc":[-85.5983396,41.9794283]},"n1821015204":{"id":"n1821015204","loc":[-85.5452801,42.0145355]},"n1821015205":{"id":"n1821015205","loc":[-85.5340685,42.0218407]},"n1821015207":{"id":"n1821015207","loc":[-85.5773272,42.0034186]},"n1821015209":{"id":"n1821015209","loc":[-85.5535212,42.0132419]},"n1821015211":{"id":"n1821015211","loc":[-85.6107703,41.9706045]},"n1821015212":{"id":"n1821015212","loc":[-85.6030066,41.9758193]},"n1821015213":{"id":"n1821015213","loc":[-85.5359943,42.0184213]},"n1821015214":{"id":"n1821015214","loc":[-85.5922993,41.9813305]},"n1821015215":{"id":"n1821015215","loc":[-85.5672689,42.0080465]},"n1821015217":{"id":"n1821015217","loc":[-85.5160494,42.0365682]},"n1821015218":{"id":"n1821015218","loc":[-85.5401142,42.0190351]},"n1821015219":{"id":"n1821015219","loc":[-85.5607632,42.0092282]},"n1821015220":{"id":"n1821015220","loc":[-85.5866197,41.9947894]},"n1821015221":{"id":"n1821015221","loc":[-85.6017889,41.9765132]},"n1821015222":{"id":"n1821015222","loc":[-85.5595978,42.009059]},"n1821015226":{"id":"n1821015226","loc":[-85.5871494,41.9929018]},"n1821015227":{"id":"n1821015227","loc":[-85.5857708,41.9998866]},"n1821015228":{"id":"n1821015228","loc":[-85.5317135,42.0238094]},"n1821015231":{"id":"n1821015231","loc":[-85.5733521,42.0061372]},"n1821015233":{"id":"n1821015233","loc":[-85.5855991,42.0001936]},"n1821015234":{"id":"n1821015234","loc":[-85.5213924,42.029962]},"n1821015235":{"id":"n1821015235","loc":[-85.6052221,41.9726567]},"n1821015236":{"id":"n1821015236","loc":[-85.5763723,42.0035422]},"n1821015237":{"id":"n1821015237","loc":[-85.5858512,41.9966215]},"n1821015238":{"id":"n1821015238","loc":[-85.567061,42.008439]},"n1821015239":{"id":"n1821015239","loc":[-85.5250563,42.0269057]},"n1821015240":{"id":"n1821015240","loc":[-85.5347551,42.0214263]},"n1821015241":{"id":"n1821015241","loc":[-85.6098463,41.9707066]},"n1821015242":{"id":"n1821015242","loc":[-85.5676927,42.0076519]},"n1821015243":{"id":"n1821015243","loc":[-85.516775,42.0322669]},"n1821015244":{"id":"n1821015244","loc":[-85.5762275,42.0036538]},"n1821015245":{"id":"n1821015245","loc":[-85.5583639,42.0090949]},"n1821015246":{"id":"n1821015246","loc":[-85.5554041,42.0106432]},"n1821015247":{"id":"n1821015247","loc":[-85.5973364,41.9776099]},"n1821015248":{"id":"n1821015248","loc":[-85.6098945,41.9717513]},"n1821015249":{"id":"n1821015249","loc":[-85.6045315,41.9751511]},"n1821015250":{"id":"n1821015250","loc":[-85.5579938,42.0092264]},"n1821015253":{"id":"n1821015253","loc":[-85.6058873,41.9724652]},"n1821015254":{"id":"n1821015254","loc":[-85.5869456,41.9947517]},"n1821015255":{"id":"n1821015255","loc":[-85.5936565,41.9823713]},"n1821015256":{"id":"n1821015256","loc":[-85.5218269,42.0278102]},"n1821015258":{"id":"n1821015258","loc":[-85.5887802,41.9905534]},"n1821015259":{"id":"n1821015259","loc":[-85.5901924,41.9904515]},"n1821015263":{"id":"n1821015263","loc":[-85.5249222,42.0255787]},"n1821015265":{"id":"n1821015265","loc":[-85.5175206,42.0321672]},"n1821015266":{"id":"n1821015266","loc":[-85.5275722,42.0254034]},"n1821015267":{"id":"n1821015267","loc":[-85.6016226,41.9765451]},"n1821015269":{"id":"n1821015269","loc":[-85.5569316,42.011032]},"n1821015271":{"id":"n1821015271","loc":[-85.6010714,41.9785209]},"n1821015272":{"id":"n1821015272","loc":[-85.6050666,41.9729917]},"n1821015273":{"id":"n1821015273","loc":[-85.5891235,41.99529]},"n1821015274":{"id":"n1821015274","loc":[-85.515454,42.0376439]},"n1821015276":{"id":"n1821015276","loc":[-85.5776021,42.0034443]},"n1821015277":{"id":"n1821015277","loc":[-85.6041707,41.9751453]},"n1821015278":{"id":"n1821015278","loc":[-85.5444701,42.0167435]},"n1821015280":{"id":"n1821015280","loc":[-85.5923274,41.9852202]},"n1821015283":{"id":"n1821015283","loc":[-85.5893649,41.9900271]},"n1821015284":{"id":"n1821015284","loc":[-85.5933453,41.9804412]},"n1821015285":{"id":"n1821015285","loc":[-85.5247237,42.026017]},"n1821015286":{"id":"n1821015286","loc":[-85.5286182,42.0242477]},"n1821015287":{"id":"n1821015287","loc":[-85.5904003,41.9888549]},"n1821015288":{"id":"n1821015288","loc":[-85.6062146,41.9739369]},"n1821015290":{"id":"n1821015290","loc":[-85.5762596,42.0052602]},"n1821015292":{"id":"n1821015292","loc":[-85.5849715,41.9975465]},"n1821015293":{"id":"n1821015293","loc":[-85.585229,42.0006241]},"n1821015294":{"id":"n1821015294","loc":[-85.5926922,41.9805946]},"n1821015295":{"id":"n1821015295","loc":[-85.5703387,42.0089133]},"n1821015299":{"id":"n1821015299","loc":[-85.5789955,42.0038611]},"n1821015301":{"id":"n1821015301","loc":[-85.6072888,41.9721918]},"n1821015302":{"id":"n1821015302","loc":[-85.5356349,42.0200992]},"n1821015304":{"id":"n1821015304","loc":[-85.5891772,41.994066]},"n1821015306":{"id":"n1821015306","loc":[-85.606295,41.9744952]},"n1821015307":{"id":"n1821015307","loc":[-85.538871,42.0186583]},"n1821015308":{"id":"n1821015308","loc":[-85.587997,41.994971]},"n1821015311":{"id":"n1821015311","loc":[-85.606869,41.9725809]},"n1821015312":{"id":"n1821015312","loc":[-85.5171974,42.0339943]},"n1821015314":{"id":"n1821015314","loc":[-85.5327435,42.0220479]},"n1821015315":{"id":"n1821015315","loc":[-85.5383439,42.0187282]},"n1821015316":{"id":"n1821015316","loc":[-85.5248095,42.0263119]},"n1821015318":{"id":"n1821015318","loc":[-85.5732502,42.0073051]},"n1821015319":{"id":"n1821015319","loc":[-85.5924226,41.9852663]},"n1821015321":{"id":"n1821015321","loc":[-85.5179001,42.0353052]},"n1821015322":{"id":"n1821015322","loc":[-85.5456771,42.0162413]},"n1821015323":{"id":"n1821015323","loc":[-85.5936618,41.9829096]},"n1821015325":{"id":"n1821015325","loc":[-85.5656931,42.0086582]},"n1821015326":{"id":"n1821015326","loc":[-85.5448456,42.0150975]},"n1821015327":{"id":"n1821015327","loc":[-85.5220039,42.027615]},"n1821015329":{"id":"n1821015329","loc":[-85.517884,42.0354885]},"n1821015330":{"id":"n1821015330","loc":[-85.5576666,42.0101671]},"n1821015332":{"id":"n1821015332","loc":[-85.5368754,42.0181402]},"n1821015333":{"id":"n1821015333","loc":[-85.5367078,42.0181145]},"n1821015334":{"id":"n1821015334","loc":[-85.5903909,41.9904316]},"n1821015335":{"id":"n1821015335","loc":[-85.5430767,42.0163587]},"n1821015336":{"id":"n1821015336","loc":[-85.5277492,42.0252878]},"n1821015337":{"id":"n1821015337","loc":[-85.5312146,42.0236898]},"n1821015338":{"id":"n1821015338","loc":[-85.5886568,41.991614]},"n1821015339":{"id":"n1821015339","loc":[-85.5782498,42.0040883]},"n1821015341":{"id":"n1821015341","loc":[-85.562233,42.0076457]},"n1821015342":{"id":"n1821015342","loc":[-85.588626,41.9952479]},"n1821015343":{"id":"n1821015343","loc":[-85.5762865,42.005033]},"n1821015344":{"id":"n1821015344","loc":[-85.5850841,41.9971478]},"n1821015346":{"id":"n1821015346","loc":[-85.5643144,42.0076936]},"n1821015347":{"id":"n1821015347","loc":[-85.5164893,42.0359467]},"n1821015348":{"id":"n1821015348","loc":[-85.5906846,41.9903541]},"n1821015349":{"id":"n1821015349","loc":[-85.557688,42.0107769]},"n1821015350":{"id":"n1821015350","loc":[-85.5363698,42.0181424]},"n1821015351":{"id":"n1821015351","loc":[-85.5939636,41.9801918]},"n1821015352":{"id":"n1821015352","loc":[-85.5524041,42.0131644]},"n1821015354":{"id":"n1821015354","loc":[-85.5308606,42.0236221]},"n1821015355":{"id":"n1821015355","loc":[-85.5877449,41.9932367]},"n1821015356":{"id":"n1821015356","loc":[-85.519885,42.0318586]},"n1821015357":{"id":"n1821015357","loc":[-85.5454035,42.0168431]},"n1821015358":{"id":"n1821015358","loc":[-85.5970629,41.9781881]},"n1821015359":{"id":"n1821015359","loc":[-85.5932541,41.9844767]},"n1821015360":{"id":"n1821015360","loc":[-85.5970736,41.9778252]},"n1821015361":{"id":"n1821015361","loc":[-85.537031,42.0181601]},"n1821015362":{"id":"n1821015362","loc":[-85.5548355,42.0105156]},"n1821015363":{"id":"n1821015363","loc":[-85.5168648,42.0336158]},"n1821015365":{"id":"n1821015365","loc":[-85.5870435,41.9919507]},"n1821015366":{"id":"n1821015366","loc":[-85.5719681,42.0075443]},"n1821015367":{"id":"n1821015367","loc":[-85.5969985,41.9780446]},"n1821015368":{"id":"n1821015368","loc":[-85.5926761,41.98528]},"n1821015369":{"id":"n1821015369","loc":[-85.5224009,42.0293444]},"n1821015371":{"id":"n1821015371","loc":[-85.518737,42.0322651]},"n1821015372":{"id":"n1821015372","loc":[-85.6064573,41.9726465]},"n1821015373":{"id":"n1821015373","loc":[-85.5201103,42.0313088]},"n1821015375":{"id":"n1821015375","loc":[-85.5378182,42.0186844]},"n1821015376":{"id":"n1821015376","loc":[-85.6109741,41.9706882]},"n1821015377":{"id":"n1821015377","loc":[-85.5993333,41.9785488]},"n1821015378":{"id":"n1821015378","loc":[-85.5889787,41.9907368]},"n1821015380":{"id":"n1821015380","loc":[-85.6060161,41.9737375]},"n1821015381":{"id":"n1821015381","loc":[-85.5743016,42.0053679]},"n1821015382":{"id":"n1821015382","loc":[-85.6014724,41.9776099]},"n1821015383":{"id":"n1821015383","loc":[-85.5574426,42.0091644]},"n1821015385":{"id":"n1821015385","loc":[-85.5208613,42.0309302]},"n1821015386":{"id":"n1821015386","loc":[-85.5919023,41.9837789]},"n1821015387":{"id":"n1821015387","loc":[-85.5455484,42.0160221]},"n1821015392":{"id":"n1821015392","loc":[-85.5801757,42.0028964]},"n1821015395":{"id":"n1821015395","loc":[-85.5493785,42.0139974]},"n1821015396":{"id":"n1821015396","loc":[-85.5449475,42.015488]},"n1821015398":{"id":"n1821015398","loc":[-85.611123,41.9706627]},"n1821015400":{"id":"n1821015400","loc":[-85.5935706,41.9822477]},"n1821015401":{"id":"n1821015401","loc":[-85.5724254,42.0073508]},"n1821015403":{"id":"n1821015403","loc":[-85.5486812,42.0143442]},"n1821015404":{"id":"n1821015404","loc":[-85.5161835,42.0327711]},"n1821015406":{"id":"n1821015406","loc":[-85.5921705,41.9851107]},"n1821015407":{"id":"n1821015407","loc":[-85.531912,42.0234069]},"n1821015410":{"id":"n1821015410","loc":[-85.5292566,42.024176]},"n1821015411":{"id":"n1821015411","loc":[-85.5845316,41.9948315]},"n1821015413":{"id":"n1821015413","loc":[-85.5217947,42.0280413]},"n1821015414":{"id":"n1821015414","loc":[-85.5527367,42.013272]},"n1821015415":{"id":"n1821015415","loc":[-85.5191179,42.0321973]},"n1821015416":{"id":"n1821015416","loc":[-85.5540241,42.0128655]},"n1821015418":{"id":"n1821015418","loc":[-85.5272892,42.0254849]},"n1821015419":{"id":"n1821015419","loc":[-85.5449744,42.016867]},"n1821015420":{"id":"n1821015420","loc":[-85.5852665,41.9986787]},"n1821015421":{"id":"n1821015421","loc":[-85.6102701,41.972186]},"n1821015423":{"id":"n1821015423","loc":[-85.6026365,41.9764972]},"n1821015427":{"id":"n1821015427","loc":[-85.5898692,41.9841498]},"n1821015429":{"id":"n1821015429","loc":[-85.5422546,42.0183855]},"n1821015430":{"id":"n1821015430","loc":[-85.5866505,41.9925549]},"n1821015431":{"id":"n1821015431","loc":[-85.5234376,42.0273577]},"n1821015432":{"id":"n1821015432","loc":[-85.6096746,41.9727284]},"n1821015433":{"id":"n1821015433","loc":[-85.5824891,42.0021567]},"n1821015434":{"id":"n1821015434","loc":[-85.5923905,41.9841139]},"n1821015435":{"id":"n1821015435","loc":[-85.5874565,41.9948014]},"n1821015437":{"id":"n1821015437","loc":[-85.6055279,41.9734423]},"n1821015438":{"id":"n1821015438","loc":[-85.5299379,42.0237376]},"n1821015439":{"id":"n1821015439","loc":[-85.5155022,42.0383651]},"n1821015442":{"id":"n1821015442","loc":[-85.527422,42.0254711]},"n1821015443":{"id":"n1821015443","loc":[-85.5920699,41.9849291]},"n1821015444":{"id":"n1821015444","loc":[-85.5639711,42.0077494]},"n1821015445":{"id":"n1821015445","loc":[-85.5162586,42.0361777]},"n1821015446":{"id":"n1821015446","loc":[-85.5220039,42.029695]},"n1821015448":{"id":"n1821015448","loc":[-85.5176641,42.0356956]},"n1821015449":{"id":"n1821015449","loc":[-85.5930556,41.9841577]},"n1821015451":{"id":"n1821015451","loc":[-85.5320783,42.0228848]},"n1821015452":{"id":"n1821015452","loc":[-85.5170096,42.0357235]},"n1821015453":{"id":"n1821015453","loc":[-85.5571355,42.009613]},"n1821015454":{"id":"n1821015454","loc":[-85.5609979,42.009059]},"n1821015455":{"id":"n1821015455","loc":[-85.6097336,41.9708342]},"n1821015456":{"id":"n1821015456","loc":[-85.5884476,41.9904218]},"w170843846":{"id":"w170843846","tags":{"waterway":"river"},"nodes":["n1819790555","n1819790762","n1819790547","n1819790618","n1819790898","n1819790796","n1819790534","n1819790543","n1819790541","n1819790687","n1819790877","n1819790811","n1819790670"]},"w209083541":{"id":"w209083541","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1821014848","n1821015156","n1821015439","n1821014763","n1821014824","n1821015274","n1821014764","n1821014791","n1821014957","n1821015217","n1821015445","n1821015347","n1821014893","n1821015452","n1821015017","n1821015448","n1821014968","n1821015329","n1821015321","n1821014992","n1821014948","n1821014757","n1821014983","n1821015312","n1821015363","n1821014924","n1821014873","n1821014932","n1821014668","n1821015404","n1821014716","n1821015243","n1821015265","n1821014710","n1821015371","n1821015415","n1821014870","n1821015356","n1821015373","n1821014681","n1821014714","n1821015385","n1821014911","n1821015057","n1821014867","n1821014998","n1821015142","n1821015234","n1821015446","n1821014862","n1821015369","n1821014945","n1821015003","n1821014667","n1821015413","n1821015256","n1821015327","n1821015160","n1821014907","n1821015431","n1821015172","n1821015034","n1821014741","n1821015033","n1821015239","n1821015007","n1821015316","n1821015285","n1821014633","n1821015263","n1821015069","n1821014846","n1821014779","n1821015011","n1821015012","n1821014845","n1821015418","n1821015442","n1821015266","n1821015336","n1821014755","n1821015188","n1821014925","n1821014816","n1821014869","n1821014967","n1821015286","n1821015147","n1821015410","n1821015126","n1821015438","n1821015018","n1821015354","n1821015337","n1821014766","n1821014740","n1821015228","n1821015173","n1821015407","n1821015076","n1821015451","n1821015024","n1821015314","n1821014784","n1821015045","n1821014735","n1821015205","n1821014986","n1821015240","n1821015098","n1821014825","n1821015302","n1821014918","n1821014718","n1821014896","n1821014853","n1821015194","n1821015165","n1821015213","n1821014991","n1821015350","n1821015333","n1821015332","n1821015361","n1821015060","n1821014914","n1821015375","n1821015315","n1821015307","n1821014906","n1821014751","n1821014666","n1821015218","n1821014817","n1821015118","n1821014674","n1821015038","n1821014973","n1821015429","n1821015183","n1821015046","n1821014803","n1821015140","n1821015009","n1821014749","n1821015335","n1821014711","n1821014854","n1821015278","n1821015419","n1821014648","n1821015357","n1821014637","n1821014988","n1821015322","n1821015387","n1821015042","n1821014833","n1821015396","n1821015326","n1821014691","n1821015149","n1821015204","n1821015122","n1821014782","n1821014982","n1821014921","n1821014936","n1821014969","n1821014881","n1821015403","n1821014805","n1821015395","n1821014892","n1821014826","n1821014844","n1821015107","n1821015014","n1821014955","n1821015352","n1821015414","n1821014746","n1821015209","n1821014770","n1821015416","n1821014661","n1821014857","n1821014814","n1821014754","n1821014721","n1821014727","n1821015362","n1821015029","n1821015246","n1821015189","n1821014627","n1821015269","n1821014808","n1821014962","n1821015349","n1821015041","n1821014722","n1821015330","n1821015453","n1821015067","n1821014643","n1821014802","n1821015383","n1821015151","n1821015250","n1821015245","n1821014772","n1821014899","n1821015222","n1821015121","n1821014951","n1821015176","n1821015219","n1821015454","n1821014972","n1821014917","n1821015182","n1821015166","n1821015145","n1821014910","n1821015082","n1821014678","n1821014806","n1821015141","n1821015341","n1821014788","n1821015097","n1821014995","n1821015444","n1821015346","n1821015101","n1821015129","n1821015123","n1821015132","n1821015190","n1821015146","n1821015325","n1821015191","n1821015072","n1821015100","n1821015174","n1821015238","n1821015215","n1821014700","n1821015242","n1821014841","n1821014905","n1821014874","n1821014883","n1821014729","n1821014866","n1821014828","n1821015295","n1821014931","n1821014859","n1821014912","n1821014783","n1821014752","n1821014961","n1821015366","n1821015401","n1821015169","n1821015318","n1821014996","n1821014747","n1821014963","n1821014670","n1821015186","n1821015231","n1821015157","n1821014812","n1821015381","n1821014887","n1821015138","n1821014704","n1821014787","n1821014922","n1821015290","n1821015343","n1821014651","n1821014980","n1821014960","n1821015244","n1821015236","n1821015006","n1821014694","n1821014759","n1821015207","n1821015276","n1821015065","n1821014863","n1821014660","n1821014902","n1821014645","n1821015339","n1821014871","n1821015096","n1821015299","n1821014798","n1821014638","n1821015392","n1821014835","n1821014762","n1821014642","n1821015433","n1821014786","n1821015134","n1821014855","n1821015184","n1821014850","n1821015293","n1821015233","n1821015227","n1821014876","n1821014985","n1821014843","n1821015420","n1821015197","n1821015192","n1821015292","n1821015344","n1821014742","n1821014726","n1821015237","n1821014796","n1821014908","n1821014975","n1821014769","n1821014688","n1821014860","n1821014895","n1821014676","n1821015411","n1821014736","n1821015164","n1821014647","n1821015144","n1821014919","n1821015220","n1821015254","n1821015435","n1821015308","n1821015342","n1821014830","n1821015273","n1821014658","n1821014781","n1821015087","n1821015139","n1821015304","n1821014839","n1821015048","n1821015115","n1821015355","n1821015226","n1821015177","n1821015430","n1821014965","n1821014725","n1821015365","n1821015171","n1821015073","n1821015125","n1821015338","n1821015111","n1821014950","n1821015378","n1821015258","n1821015456","n1821015106","n1821014832","n1821014888","n1821014795","n1821014872","n1821014810","n1821014705","n1821014804","n1821014820","n1821015283","n1821014938","n1821014689","n1821015259","n1821015334","n1821015348","n1821014635","n1821015179","n1821014864","n1821014890","n1821015020","n1821014898","n1821015287","n1821015120","n1821014984","n1821014743","n1821014790","n1821014765","n1821014777","n1821015095","n1821014653","n1821015135","n1821014836","n1821014964","n1821014974","n1821014636","n1821014682","n1821014663","n1821014665","n1821015109","n1821015155","n1821014930","n1821014669","n1821015004","n1821015427","n1821014916","n1821015093","n1821015086","n1821015386","n1821014799","n1821014913","n1821015434","n1821014728","n1821014900","n1821015068","n1821015039","n1821015443","n1821015406","n1821015280","n1821015319","n1821015368","n1821014774","n1821015090","n1821015175","n1821015195","n1821014687","n1821015359","n1821015449","n1821014956","n1821014838","n1821014768","n1821014698","n1821015323","n1821014756","n1821015255","n1821015400","n1821014717","n1821014868","n1821014778","n1821015214","n1821014944","n1821014697","n1821014671","n1821014928","n1821015294","n1821014822","n1821015284","n1821015351","n1821015022","n1821015133","n1821014644","n1821015010","n1821014625","n1821014657","n1821014946","n1821015099","n1821015114","n1821014629","n1821014865","n1821014997","n1821014926","n1821014933","n1821015199","n1821014819","n1821015080","n1821014692","n1821014677","n1821015358","n1821015367","n1821015360","n1821015105","n1821015247","n1821015005","n1821014809","n1821014794","n1821014761","n1821014879","n1821014801","n1821015377","n1821015059","n1821014730","n1821015050","n1821015271","n1821015143","n1821014989","n1821015019","n1821014672","n1821014649","n1821014684","n1821014703","n1821015021","n1821015382","n1821014842","n1821014720","n1821014847","n1821015104","n1821014987","n1821014886","n1821015267","n1821015221","n1821015015","n1821015423","n1821014954","n1821014903","n1821014939","n1821015212","n1821014789","n1821014712","n1821014708","n1821015078","n1821015277","n1821015249","n1821014646","n1821014793","n1821015053","n1821014707","n1821015306","n1821015112","n1821015288","n1821015380","n1821015437","n1821015178","n1821015158","n1821015272","n1821015235","n1821015163","n1821015154","n1821015253","n1821014632","n1821015372","n1821015103","n1821015311","n1821015301","n1821014885","n1821014811","n1821014977","n1821015051","n1821014942","n1821014745","n1821015432","n1821015075","n1821014664","n1821014695","n1821015116","n1821014639","n1821015421","n1821015248","n1821014758","n1821014834","n1821015083","n1821015455","n1821015241","n1821015108","n1821014713","n1821015137","n1821015055","n1821015211","n1821014904","n1821015376","n1821015398","n1821014771","n1821014840","n1821015062","n1819790554","n1819790560","n1819790767","n1819790696","n1819790706","n1819790606","n1819790607","n1819790544","n1819790779","n1819790760","n1819790926","n1819790927","n1819790647","n1819790657","n1819790649","n1819790679","n1819790915","n1819790739","n1819790549","n1819790671","n1819790686","n1819790798","n1819790791","n1819790563","n1819790720","n1819790704","n1819790795","n1819790836","n1819790622","n1819790615","n1819790654","n1819790931","n1819790595","n1819790753","n1819790612","n1819790623","n1819790564","n1819790552","n1819790645","n1819790625","n1819790605","n1819790668","n1819790731","n1819790718","n1819790781","n1819790665","n1819790659","n1819790726","n1819790642","n1819790854","n1819790697","n1819790867","n1819790833","n1819790555","n1819790774","n1819790881","n1819790530","n1819790909","n1819790891","n1819790590","n1819790738","n1819790609","n1819790528","n1819790674","n1819790583","n1819790559","n1819790863","n1819790912","n1819790685","n1819790913"]},"n185955128":{"id":"n185955128","loc":[-85.6189367,41.9519432]},"n185948818":{"id":"n185948818","loc":[-85.616755,41.952231]},"n185978819":{"id":"n185978819","loc":[-85.616773,41.954737]},"n185978821":{"id":"n185978821","loc":[-85.616699,41.954742]},"n2138420714":{"id":"n2138420714","loc":[-85.6176304,41.9515154]},"n2138420715":{"id":"n2138420715","loc":[-85.6177355,41.9515717]},"n2138420716":{"id":"n2138420716","loc":[-85.6192901,41.951573]},"n2138420718":{"id":"n2138420718","loc":[-85.6171481,41.9513579]},"n2138420719":{"id":"n2138420719","loc":[-85.6165981,41.9519199]},"n2138420720":{"id":"n2138420720","loc":[-85.6165719,41.9519922]},"n2138420721":{"id":"n2138420721","loc":[-85.6165832,41.9520757]},"n2138420722":{"id":"n2138420722","loc":[-85.6166355,41.9521453]},"n2138420723":{"id":"n2138420723","loc":[-85.6169161,41.9522788]},"n2138420724":{"id":"n2138420724","loc":[-85.6170882,41.9522538]},"n2138420725":{"id":"n2138420725","loc":[-85.6189204,41.9514674]},"n2138420726":{"id":"n2138420726","loc":[-85.6180346,41.9514735]},"n2138420727":{"id":"n2138420727","loc":[-85.6180362,41.9515719]},"n2138420728":{"id":"n2138420728","loc":[-85.6189204,41.9515727]},"n2138420744":{"id":"n2138420744","loc":[-85.618919,41.9519571]},"n2138420745":{"id":"n2138420745","loc":[-85.6194575,41.9522374]},"n2138420746":{"id":"n2138420746","loc":[-85.6181777,41.9536179]},"n2138420747":{"id":"n2138420747","loc":[-85.6176582,41.9533658]},"n2138420748":{"id":"n2138420748","loc":[-85.6179871,41.9530242]},"n2138420749":{"id":"n2138420749","loc":[-85.618429,41.9532476]},"n2138420750":{"id":"n2138420750","loc":[-85.6185538,41.9531194]},"n2138420751":{"id":"n2138420751","loc":[-85.6180765,41.9528677]},"n2138420752":{"id":"n2138420752","loc":[-85.6180394,41.9528855]},"n2138420753":{"id":"n2138420753","loc":[-85.6193752,41.9521695]},"n2138420754":{"id":"n2138420754","loc":[-85.6181374,41.9535376]},"n2138420755":{"id":"n2138420755","loc":[-85.6179898,41.9535545]},"n2138420756":{"id":"n2138420756","loc":[-85.6177286,41.9534228]},"n2138420757":{"id":"n2138420757","loc":[-85.6181011,41.9530292]},"n2138420759":{"id":"n2138420759","loc":[-85.6185158,41.9531194]},"n2138420760":{"id":"n2138420760","loc":[-85.6191318,41.9520425]},"n2138420761":{"id":"n2138420761","loc":[-85.6182348,41.9529815]},"n2138420762":{"id":"n2138420762","loc":[-85.6184853,41.9524248]},"n2138420763":{"id":"n2138420763","loc":[-85.6186764,41.9525193]},"n2138420764":{"id":"n2138420764","loc":[-85.6189421,41.9526483]},"n2138420765":{"id":"n2138420765","loc":[-85.6182875,41.9531222]},"n2138420766":{"id":"n2138420766","loc":[-85.6179141,41.9535163]},"n2138420767":{"id":"n2138420767","loc":[-85.6178363,41.9535735]},"n185948824":{"id":"n185948824","loc":[-85.6165667,41.9529715]},"n2138420758":{"id":"n2138420758","loc":[-85.6184408,41.953201]},"n2138422349":{"id":"n2138422349","loc":[-85.6175136,41.9533346]},"n2138422350":{"id":"n2138422350","loc":[-85.6171867,41.9531679]},"n2138422351":{"id":"n2138422351","loc":[-85.61722,41.9531305]},"n2138422352":{"id":"n2138422352","loc":[-85.6171889,41.9531158]},"n2138422353":{"id":"n2138422353","loc":[-85.6171733,41.9531284]},"n2138422354":{"id":"n2138422354","loc":[-85.616765,41.9529207]},"n2138422355":{"id":"n2138422355","loc":[-85.6167565,41.9529355]},"n2138422356":{"id":"n2138422356","loc":[-85.6164772,41.9527911]},"n2138422357":{"id":"n2138422357","loc":[-85.6168227,41.9524261]},"n2138422358":{"id":"n2138422358","loc":[-85.6171913,41.9526158]},"n2138422359":{"id":"n2138422359","loc":[-85.6172403,41.9525589]},"n2138422360":{"id":"n2138422360","loc":[-85.6172097,41.952542]},"n2138422361":{"id":"n2138422361","loc":[-85.6173948,41.9523512]},"n2138422362":{"id":"n2138422362","loc":[-85.6174256,41.9523678]},"n2138422363":{"id":"n2138422363","loc":[-85.6174831,41.9523086]},"n2138422364":{"id":"n2138422364","loc":[-85.6173316,41.9522289]},"n2138422365":{"id":"n2138422365","loc":[-85.6174507,41.9521024]},"n2138422366":{"id":"n2138422366","loc":[-85.6174773,41.9521155]},"n2138422367":{"id":"n2138422367","loc":[-85.6176577,41.9519232]},"n2138422368":{"id":"n2138422368","loc":[-85.6176336,41.9519105]},"n2138422369":{"id":"n2138422369","loc":[-85.617747,41.9517861]},"n2138422370":{"id":"n2138422370","loc":[-85.6182675,41.9520559]},"n2138422371":{"id":"n2138422371","loc":[-85.6182105,41.9521219]},"n2138422372":{"id":"n2138422372","loc":[-85.6183863,41.9522203]},"n2138422373":{"id":"n2138422373","loc":[-85.6180984,41.9525266]},"n2138422374":{"id":"n2138422374","loc":[-85.6179159,41.9524295]},"n2138422375":{"id":"n2138422375","loc":[-85.617854,41.9524979]},"n2138422376":{"id":"n2138422376","loc":[-85.6177686,41.9524531]},"n2138422377":{"id":"n2138422377","loc":[-85.6174716,41.9527765]},"n2138422378":{"id":"n2138422378","loc":[-85.6178545,41.9529756]},"n2138425424":{"id":"n2138425424","loc":[-85.6171736,41.9536385]},"n2138425425":{"id":"n2138425425","loc":[-85.6180159,41.9535782]},"n2138425426":{"id":"n2138425426","loc":[-85.6181068,41.9536282]},"n2138425427":{"id":"n2138425427","loc":[-85.6180673,41.9542678]},"n2138425428":{"id":"n2138425428","loc":[-85.6178636,41.9542634]},"n2138425429":{"id":"n2138425429","loc":[-85.6176204,41.9542046]},"n2138425430":{"id":"n2138425430","loc":[-85.6174366,41.9541031]},"n2138425431":{"id":"n2138425431","loc":[-85.6172942,41.9539781]},"n2138425432":{"id":"n2138425432","loc":[-85.6172171,41.9538399]},"n2138425433":{"id":"n2138425433","loc":[-85.6168138,41.9543266]},"n2138425434":{"id":"n2138425434","loc":[-85.6167779,41.9538098]},"n2138425435":{"id":"n2138425435","loc":[-85.6165849,41.9537073]},"n2138425441":{"id":"n2138425441","loc":[-85.616458,41.9543184]},"n2138425442":{"id":"n2138425442","loc":[-85.6166428,41.954345]},"n2138425445":{"id":"n2138425445","loc":[-85.6181332,41.9514117]},"n2138425446":{"id":"n2138425446","loc":[-85.6183263,41.9514111]},"n2138425447":{"id":"n2138425447","loc":[-85.6185033,41.9514102]},"n2138425449":{"id":"n2138425449","loc":[-85.6186809,41.9514093]},"n2138425451":{"id":"n2138425451","loc":[-85.6188681,41.9514082]},"n2138436008":{"id":"n2138436008","loc":[-85.6170474,41.9513604]},"n2138436009":{"id":"n2138436009","loc":[-85.6164937,41.9519586]},"n2138436010":{"id":"n2138436010","loc":[-85.616497,41.9520725]},"n2138436011":{"id":"n2138436011","loc":[-85.6165654,41.9521645]},"n2138436012":{"id":"n2138436012","loc":[-85.6166631,41.9522178]},"n2138436013":{"id":"n2138436013","loc":[-85.6167327,41.9522554]},"n2138436014":{"id":"n2138436014","loc":[-85.6172383,41.9525125]},"n2138439319":{"id":"n2138439319","loc":[-85.6170432,41.9524057]},"n2138439320":{"id":"n2138439320","loc":[-85.617691,41.9517107]},"n2138439321":{"id":"n2138439321","loc":[-85.6177727,41.9516794]},"n2138439322":{"id":"n2138439322","loc":[-85.619085,41.9516811]},"n2138439323":{"id":"n2138439323","loc":[-85.6179432,41.952895]},"n2138439324":{"id":"n2138439324","loc":[-85.6180389,41.9529384]},"n2138439325":{"id":"n2138439325","loc":[-85.6176303,41.9533604]},"n2138439326":{"id":"n2138439326","loc":[-85.6175538,41.9534396]},"n2138439327":{"id":"n2138439327","loc":[-85.6173806,41.9523658]},"n2138439328":{"id":"n2138439328","loc":[-85.6171841,41.9522542]},"n2138439329":{"id":"n2138439329","loc":[-85.6172077,41.9524958]},"n2138439330":{"id":"n2138439330","loc":[-85.6171235,41.9525809]},"n2138439331":{"id":"n2138439331","loc":[-85.6180938,41.9527349]},"n2138439332":{"id":"n2138439332","loc":[-85.6177023,41.9525253]},"n2138439333":{"id":"n2138439333","loc":[-85.6175543,41.9526865]},"n2138439334":{"id":"n2138439334","loc":[-85.6179589,41.9528783]},"n185948820":{"id":"n185948820","loc":[-85.6163249,41.952701]},"n185948822":{"id":"n185948822","loc":[-85.6163757,41.952855]},"n185955123":{"id":"n185955123","loc":[-85.6198103,41.9510408]},"n185958839":{"id":"n185958839","loc":[-85.611651,41.954761]},"n185965033":{"id":"n185965033","loc":[-85.614195,41.954754]},"n185976502":{"id":"n185976502","loc":[-85.617375,41.947559]},"n185976504":{"id":"n185976504","loc":[-85.6174164,41.9510804]},"n185978828":{"id":"n185978828","loc":[-85.613542,41.954756]},"n185978830":{"id":"n185978830","loc":[-85.610373,41.954774]},"n2138420713":{"id":"n2138420713","loc":[-85.6174641,41.9506942]},"n2138420717":{"id":"n2138420717","loc":[-85.6173027,41.9512895]},"n2138420768":{"id":"n2138420768","loc":[-85.61745,41.9501974]},"n2138420773":{"id":"n2138420773","loc":[-85.6174135,41.9489136]},"n2138425436":{"id":"n2138425436","loc":[-85.6159148,41.9538036]},"n2138425437":{"id":"n2138425437","loc":[-85.6159534,41.9539677]},"n2138425438":{"id":"n2138425438","loc":[-85.6160306,41.9540846]},"n2138425439":{"id":"n2138425439","loc":[-85.6161354,41.954181]},"n2138425440":{"id":"n2138425440","loc":[-85.6162733,41.954263]},"n2138425443":{"id":"n2138425443","loc":[-85.6183273,41.9510826]},"n2138425444":{"id":"n2138425444","loc":[-85.6181354,41.9510835]},"n2138425448":{"id":"n2138425448","loc":[-85.6185033,41.9510816]},"n2138425450":{"id":"n2138425450","loc":[-85.6186816,41.9510808]},"n2138425452":{"id":"n2138425452","loc":[-85.6188641,41.9510818]},"n2138435984":{"id":"n2138435984","loc":[-85.6167607,41.9501009]},"n2138436000":{"id":"n2138436000","loc":[-85.6173169,41.947558]},"n2138436001":{"id":"n2138436001","loc":[-85.6173362,41.948883]},"n2138436002":{"id":"n2138436002","loc":[-85.6167791,41.9492952]},"n2138436003":{"id":"n2138436003","loc":[-85.6167543,41.949349]},"n2138436004":{"id":"n2138436004","loc":[-85.6167648,41.9509125]},"n2138436005":{"id":"n2138436005","loc":[-85.6168832,41.9510412]},"n2138436006":{"id":"n2138436006","loc":[-85.6170045,41.9511417]},"n2138436007":{"id":"n2138436007","loc":[-85.6170624,41.9512483]},"n2138436017":{"id":"n2138436017","loc":[-85.6168094,41.9492729]},"n2138436021":{"id":"n2138436021","loc":[-85.6167553,41.9494886]},"n2138436023":{"id":"n2138436023","loc":[-85.6167585,41.9499707]},"n2138436025":{"id":"n2138436025","loc":[-85.6167567,41.9497018]},"w203838284":{"id":"w203838284","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425424","n2138425425","n2138425426","n2138425427","n2138425428","n2138425429","n2138425430","n2138425431","n2138425432","n2138425424"]},"w203837928":{"id":"w203837928","tags":{"highway":"service"},"nodes":["n2138420717","n2138420718","n2138420719","n2138420720","n2138420721","n2138420722","n185948818","n2138420723","n2138420724","n2138420715"]},"w203839364":{"id":"w203839364","tags":{"highway":"footway"},"nodes":["n2138439331","n2138439332"]},"w203837932":{"id":"w203837932","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420744","n2138420745","n2138420746","n2138420747","n2138420748","n2138420749","n2138420750","n2138420751","n2138420744"]},"w203839362":{"id":"w203839362","tags":{"highway":"footway"},"nodes":["n2138439327","n2138439328"]},"w203839363":{"id":"w203839363","tags":{"highway":"footway"},"nodes":["n2138439329","n2138439330"]},"w203837933":{"id":"w203837933","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n185955128","n2138420760","n2138420753","n2138420764","n2138420759","n2138420758","n2138420754","n2138420755","n2138420766","n2138420756"]},"w203837936":{"id":"w203837936","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420765","n2138420766"]},"w17966364":{"id":"w17966364","tags":{"access":"private","highway":"service","name":"Collins Dr"},"nodes":["n185961362","n185976502","n2138420773","n2138420768","n2138420713","n185976504","n2138420717","n2138420714","n2138420715","n2138420727","n2138420728","n2138420716"]},"w203838040":{"id":"w203838040","tags":{"amenity":"school","area":"yes","building":"yes","name":"Three Rivers Middle School"},"nodes":["n2138422349","n2138422350","n2138422351","n2138422352","n2138422353","n2138422354","n2138422355","n2138422356","n2138422357","n2138439330","n2138422358","n2138422359","n2138422360","n2138436014","n2138439327","n2138422361","n2138422362","n2138422363","n2138422364","n2138422365","n2138422366","n2138422367","n2138422368","n2138422369","n2138422370","n2138422371","n2138422372","n2138422373","n2138422374","n2138422375","n2138422376","n2138439332","n2138439333","n2138422377","n2138422378","n2138422349"]},"w17964049":{"id":"w17964049","tags":{"highway":"service"},"nodes":["n185955120","n185955123","n2138420716","n185955128","n2138420762","n2138420752","n2138420761","n2138420759"]},"w41074899":{"id":"w41074899","tags":{"highway":"secondary","name":"E Hoffman St","ref":"M 60"},"nodes":["n185978817","n185978819","n185978821","n185965033","n185978828","n185958839","n185978830"]},"w203839365":{"id":"w203839365","tags":{"highway":"footway"},"nodes":["n2138439333","n2138439334"]},"w203837935":{"id":"w203837935","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420762","n2138420763","n2138420764"]},"w203838287":{"id":"w203838287","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425446","n2138425447","n2138425448","n2138425443","n2138425446"]},"w203837934":{"id":"w203837934","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420760","n2138420763","n2138420761"]},"w203838289":{"id":"w203838289","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425449","n2138425451","n2138425452","n2138425450","n2138425449"]},"w17963047":{"id":"w17963047","tags":{"highway":"service"},"nodes":["n185948818","n2138436013","n185948820","n185948822","n185948824","n2138439326","n2138420767","n2138420766"]},"w203839091":{"id":"w203839091","tags":{"highway":"footway"},"nodes":["n185976502","n2138436000","n2138436001","n2138436017","n2138436002","n2138436003","n2138436021","n2138436025","n2138436023","n2138435984","n2138436004","n2138436005","n2138436006","n2138436007","n2138436008","n2138436009","n2138436010","n2138436011","n2138436012","n2138436013","n2138439319","n2138439329","n2138436014"]},"w204830797":{"id":"w204830797","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420756","n2138420757","n2138420765","n2138420758"]},"w203838288":{"id":"w203838288","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425447","n2138425449","n2138425450","n2138425448","n2138425447"]},"w203838285":{"id":"w203838285","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425433","n2138425434","n2138425435","n2138425436","n2138425437","n2138425438","n2138425439","n2138425440","n2138425441","n2138425442","n2138425433"]},"w203838286":{"id":"w203838286","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425443","n2138425444","n2138425445","n2138425446","n2138425443"]},"w203837929":{"id":"w203837929","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420725","n2138420726","n2138420727","n2138420728","n2138420725"]},"w203839361":{"id":"w203839361","tags":{"highway":"footway"},"nodes":["n2138439319","n2138439328","n2138439320","n2138439321","n2138439322","n2138439331","n2138439334","n2138439323","n2138439324","n2138439325","n2138439326"]},"n394381698":{"id":"n394381698","loc":[-85.614471,41.954755]},"n394381699":{"id":"n394381699","loc":[-85.6152,41.954744]},"n394381700":{"id":"n394381700","loc":[-85.615201,41.954081]},"n394381701":{"id":"n394381701","loc":[-85.614426,41.954042]},"n394381702":{"id":"n394381702","loc":[-85.616319,41.954749]},"n394381704":{"id":"n394381704","loc":[-85.616152,41.954752]},"n394381706":{"id":"n394381706","loc":[-85.615201,41.95483]},"n394490775":{"id":"n394490775","loc":[-85.613971,41.954839]},"n394490782":{"id":"n394490782","loc":[-85.614372,41.954841]},"n185958835":{"id":"n185958835","loc":[-85.611615,41.953704]},"n185958837":{"id":"n185958837","loc":[-85.611636,41.953938]},"n185958842":{"id":"n185958842","loc":[-85.611187,41.951686]},"n185958844":{"id":"n185958844","loc":[-85.611087,41.951741]},"n185958845":{"id":"n185958845","loc":[-85.611034,41.951852]},"n185958847":{"id":"n185958847","loc":[-85.611016,41.95196]},"n185958849":{"id":"n185958849","loc":[-85.610989,41.95328]},"n185958851":{"id":"n185958851","loc":[-85.611021,41.953484]},"n185958852":{"id":"n185958852","loc":[-85.611091,41.953603]},"n185958853":{"id":"n185958853","loc":[-85.6112,41.953661]},"n185958855":{"id":"n185958855","loc":[-85.611364,41.953686]},"n185965031":{"id":"n185965031","loc":[-85.614204,41.953696]},"n185965032":{"id":"n185965032","loc":[-85.6142,41.953978]},"n185965062":{"id":"n185965062","loc":[-85.614617,41.951639]},"n185965064":{"id":"n185965064","loc":[-85.61463,41.951852]},"n185965066":{"id":"n185965066","loc":[-85.614642,41.953436]},"n185965068":{"id":"n185965068","loc":[-85.6146,41.953551]},"n185965071":{"id":"n185965071","loc":[-85.614487,41.95363]},"n185965073":{"id":"n185965073","loc":[-85.614354,41.953672]},"n185966288":{"id":"n185966288","loc":[-85.61179,41.953695]},"n185966290":{"id":"n185966290","loc":[-85.612232,41.953685]},"n185966293":{"id":"n185966293","loc":[-85.613438,41.953677]},"n185966349":{"id":"n185966349","loc":[-85.611323,41.951653]},"n185966351":{"id":"n185966351","loc":[-85.611892,41.951642]},"n185966352":{"id":"n185966352","loc":[-85.612216,41.951641]},"n185966353":{"id":"n185966353","loc":[-85.613111,41.951639]},"n185966354":{"id":"n185966354","loc":[-85.613396,41.95164]},"n185966355":{"id":"n185966355","loc":[-85.614221,41.95164]},"n185973839":{"id":"n185973839","loc":[-85.61341,41.951919]},"n185973840":{"id":"n185973840","loc":[-85.613438,41.953308]},"n185980222":{"id":"n185980222","loc":[-85.613781,41.955164]},"n185980223":{"id":"n185980223","loc":[-85.613815,41.955237]},"n185980225":{"id":"n185980225","loc":[-85.613837,41.955316]},"n185990345":{"id":"n185990345","loc":[-85.612211,41.951977]},"n185955743":{"id":"n185955743","loc":[-85.613873,41.95635]},"n185980227":{"id":"n185980227","loc":[-85.613851,41.955415]},"n185980229":{"id":"n185980229","loc":[-85.613918,41.957134]},"n394381703":{"id":"n394381703","loc":[-85.616287,41.955674]},"n394381705":{"id":"n394381705","loc":[-85.615164,41.955676]},"n394490777":{"id":"n394490777","loc":[-85.613973,41.955979]},"n394490780":{"id":"n394490780","loc":[-85.614364,41.955987]},"w17965307":{"id":"w17965307","tags":{"highway":"residential","name":"Bates Ave"},"nodes":["n185958842","n185966349","n185966351","n185966352","n185966353","n185966354","n185966355","n185965062"]},"w17967957":{"id":"w17967957","tags":{"highway":"residential","name":"Krum Ave"},"nodes":["n185966352","n185990345","n185966290"]},"w17964508":{"id":"w17964508","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958842","n185958844","n185958845","n185958847","n185958849","n185958851","n185958852","n185958853","n185958855","n185958835"]},"w17964507":{"id":"w17964507","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958835","n185958837","n185958839"]},"w34367080":{"id":"w34367080","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381699","n394381706","n394381705","n394381703","n394381702","n394381704","n394381699"]},"w17965302":{"id":"w17965302","tags":{"highway":"residential","name":"Clausen Ave"},"nodes":["n185958835","n185966288","n185966290","n185966293","n185965031"]},"w17965156":{"id":"w17965156","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965062","n185965064","n185965066","n185965068","n185965071","n185965073","n185965031"]},"w34369812":{"id":"w34369812","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490775","n394490777","n394490780","n394490782","n394490775"]},"w17965151":{"id":"w17965151","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965031","n185965032","n185965033"]},"w17966756":{"id":"w17966756","tags":{"access":"private","highway":"service","name":"Lockport Dr"},"nodes":["n185978828","n185980222","n185980223","n185980225","n185980227","n185955743","n185980229"]},"w17966056":{"id":"w17966056","tags":{"highway":"residential","name":"Angell Ave"},"nodes":["n185966354","n185973839","n185973840","n185966293"]},"w34367079":{"id":"w34367079","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381700","n394381701","n394381698","n394381699","n394381700"]},"n185955744":{"id":"n185955744","loc":[-85.611753,41.956208]},"n185988932":{"id":"n185988932","loc":[-85.6159,41.956336]},"n185988934":{"id":"n185988934","loc":[-85.6159158,41.9590646]},"n185988935":{"id":"n185988935","loc":[-85.6157358,41.959364],"tags":{"highway":"turning_circle"}},"n2138447007":{"id":"n2138447007","loc":[-85.6130784,41.9590689]},"n2138447008":{"id":"n2138447008","loc":[-85.6133328,41.9593805]},"n2138447003":{"id":"n2138447003","loc":[-85.610238,41.9547745]},"n2138447004":{"id":"n2138447004","loc":[-85.6102652,41.9566041]},"n2138447005":{"id":"n2138447005","loc":[-85.610325,41.9568823]},"n2138447006":{"id":"n2138447006","loc":[-85.6105644,41.9571383]},"n2138447009":{"id":"n2138447009","loc":[-85.6135946,41.959948]},"n2138447010":{"id":"n2138447010","loc":[-85.6136071,41.9629372]},"n2138447011":{"id":"n2138447011","loc":[-85.6134392,41.9633182]},"n2138447012":{"id":"n2138447012","loc":[-85.6130151,41.9636073]},"n2138447013":{"id":"n2138447013","loc":[-85.6122729,41.9637125]},"n2138447014":{"id":"n2138447014","loc":[-85.6056682,41.963752]},"w17964174":{"id":"w17964174","tags":{"access":"private","highway":"service"},"nodes":["n185955743","n185955744"]},"w17967743":{"id":"w17967743","tags":{"access":"private","highway":"service","name":"Manistee River Rd"},"nodes":["n185971574","n185988932","n185971407","n185981301","n185967987","n185988934","n185988935"]},"w203839666":{"id":"w203839666","tags":{"highway":"residential","name":"Hov Aire Drive"},"nodes":["n2138447003","n2138447004","n2138447005","n2138447006","n2138447007","n2138447008","n2138447009","n2138447010","n2138447011","n2138447012","n2138447013","n2138447014"]}}';iD.data = {"deprecated":[{"old":{"amenity":"firepit"},"replace":{"leisure":"firepit"}},{"old":{"barrier":"wire_fence"},"replace":{"barrier":"fence","fence_type":"chain"}},{"old":{"barrier":"wood_fence"},"replace":{"barrier":"fence","fence_type":"wood"}},{"old":{"highway":"ford"},"replace":{"ford":"yes"}},{"old":{"highway":"stile"},"replace":{"barrier":"stile"}},{"old":{"highway":"incline"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"incline_steep"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"unsurfaced"},"replace":{"highway":"road","incline":"unpaved"}},{"old":{"landuse":"wood"},"replace":{"landuse":"forest","natural":"wood"}},{"old":{"natural":"marsh"},"replace":{"natural":"wetland","wetland":"marsh"}},{"old":{"power_source":"*"},"replace":{"generator:source":"$1"}},{"old":{"power_rating":"*"},"replace":{"generator:output":"$1"}},{"old":{"shop":"organic"},"replace":{"shop":"supermarket","organic":"only"}}],"discarded":["created_by","odbl","odbl:note","tiger:upload_uuid","tiger:tlid","tiger:source","tiger:separated","geobase:datasetName","geobase:uuid","sub_sea:type","KSJ2:ADS","KSJ2:ARE","KSJ2:AdminArea","KSJ2:COP_label","KSJ2:DFD","KSJ2:INT","KSJ2:INT_label","KSJ2:LOC","KSJ2:LPN","KSJ2:OPC","KSJ2:PubFacAdmin","KSJ2:RAC","KSJ2:RAC_label","KSJ2:RIC","KSJ2:RIN","KSJ2:WSC","KSJ2:coordinate","KSJ2:curve_id","KSJ2:curve_type","KSJ2:filename","KSJ2:lake_id","KSJ2:lat","KSJ2:long","KSJ2:river_id","yh:LINE_NAME","yh:LINE_NUM","yh:STRUCTURE","yh:TOTYUMONO","yh:TYPE","yh:WIDTH","yh:WIDTH_RANK","SK53_bulk:load"],"wikipedia":[["Abkhazian","Аҧсшәа","ab"],["Achinese","Acèh","ace"],["Adyghe","адыгабзэ","ady"],["Afrikaans","Afrikaans","af"],["Akan","Akan","ak"],["Alemannisch","Alemannisch","als"],["Amharic","አማርኛ","am"],["Aragonese","aragonés","an"],["Old English","Ænglisc","ang"],["Arabic","العربية","ar"],["Aramaic","ܐܪܡܝܐ","arc"],["Egyptian Arabic","مصرى","arz"],["Assamese","অসমীয়া","as"],["Asturian","asturianu","ast"],["Avaric","авар","av"],["Aymara","Aymar aru","ay"],["Azerbaijani","azərbaycanca","az"],["تۆرکجه","تۆرکجه","azb"],["Bashkir","башҡортса","ba"],["Bavarian","Boarisch","bar"],["Samogitian","žemaitėška","bat-smg"],["Bikol Central","Bikol Central","bcl"],["Belarusian","беларуская","be"],["беларуская (тарашкевіца)‎","беларуская (тарашкевіца)‎","be-x-old"],["Bulgarian","български","bg"],["भोजपुरी","भोजपुरी","bh"],["Bislama","Bislama","bi"],["Banjar","Bahasa Banjar","bjn"],["Bambara","bamanankan","bm"],["Bengali","বাংলা","bn"],["Tibetan","བོད་ཡིག","bo"],["Bishnupriya","বিষ্ণুপ্রিয়া মণিপুরী","bpy"],["Breton","brezhoneg","br"],["Bosnian","bosanski","bs"],["Buginese","ᨅᨔ ᨕᨘᨁᨗ","bug"],["буряад","буряад","bxr"],["Catalan","català","ca"],["Chavacano de Zamboanga","Chavacano de Zamboanga","cbk-zam"],["Min Dong Chinese","Mìng-dĕ̤ng-ngṳ̄","cdo"],["Chechen","нохчийн","ce"],["Cebuano","Cebuano","ceb"],["Chamorro","Chamoru","ch"],["Cherokee","ᏣᎳᎩ","chr"],["Cheyenne","Tsetsêhestâhese","chy"],["Central Kurdish","کوردیی ناوەندی","ckb"],["Corsican","corsu","co"],["Cree","Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ","cr"],["Crimean Turkish","qırımtatarca","crh"],["Czech","čeština","cs"],["Kashubian","kaszëbsczi","csb"],["Church Slavic","словѣньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ","cu"],["Chuvash","Чӑвашла","cv"],["Welsh","Cymraeg","cy"],["Danish","dansk","da"],["German","Deutsch","de"],["Zazaki","Zazaki","diq"],["Lower Sorbian","dolnoserbski","dsb"],["Divehi","ދިވެހިބަސް","dv"],["Dzongkha","ཇོང་ཁ","dz"],["Ewe","eʋegbe","ee"],["Greek","Ελληνικά","el"],["Emiliano-Romagnolo","emiliàn e rumagnòl","eml"],["English","English","en"],["Esperanto","Esperanto","eo"],["Spanish","español","es"],["Estonian","eesti","et"],["Basque","euskara","eu"],["Extremaduran","estremeñu","ext"],["Persian","فارسی","fa"],["Fulah","Fulfulde","ff"],["Finnish","suomi","fi"],["Võro","Võro","fiu-vro"],["Fijian","Na Vosa Vakaviti","fj"],["Faroese","føroyskt","fo"],["French","français","fr"],["Arpitan","arpetan","frp"],["Northern Frisian","Nordfriisk","frr"],["Friulian","furlan","fur"],["Western Frisian","Frysk","fy"],["Irish","Gaeilge","ga"],["Gagauz","Gagauz","gag"],["Gan Chinese","贛語","gan"],["Scottish Gaelic","Gàidhlig","gd"],["Galician","galego","gl"],["Gilaki","گیلکی","glk"],["Guarani","Avañe'ẽ","gn"],["Goan Konkani","गोंयची कोंकणी / Gõychi Konknni","gom"],["Gothic","𐌲𐌿𐍄𐌹𐍃𐌺","got"],["Gujarati","ગુજરાતી","gu"],["Manx","Gaelg","gv"],["Hausa","Hausa","ha"],["Hakka Chinese","客家語/Hak-kâ-ngî","hak"],["Hawaiian","Hawaiʻi","haw"],["Hebrew","עברית","he"],["Hindi","हिन्दी","hi"],["Fiji Hindi","Fiji Hindi","hif"],["Croatian","hrvatski","hr"],["Upper Sorbian","hornjoserbsce","hsb"],["Haitian Creole","Kreyòl ayisyen","ht"],["Hungarian","magyar","hu"],["Armenian","Հայերեն","hy"],["Interlingua","interlingua","ia"],["Indonesian","Bahasa Indonesia","id"],["Interlingue","Interlingue","ie"],["Igbo","Igbo","ig"],["Inupiaq","Iñupiak","ik"],["Iloko","Ilokano","ilo"],["Ido","Ido","io"],["Icelandic","íslenska","is"],["Italian","italiano","it"],["Inuktitut","ᐃᓄᒃᑎᑐᑦ/inuktitut","iu"],["Japanese","日本語","ja"],["Jamaican Creole English","Patois","jam"],["Lojban","la .lojban.","jbo"],["Javanese","Basa Jawa","jv"],["Georgian","ქართული","ka"],["Kara-Kalpak","Qaraqalpaqsha","kaa"],["Kabyle","Taqbaylit","kab"],["Kabardian","Адыгэбзэ","kbd"],["Kongo","Kongo","kg"],["Kikuyu","Gĩkũyũ","ki"],["Kazakh","қазақша","kk"],["Kalaallisut","kalaallisut","kl"],["Khmer","ភាសាខ្មែរ","km"],["Kannada","ಕನ್ನಡ","kn"],["Korean","한국어","ko"],["Komi-Permyak","Перем Коми","koi"],["Karachay-Balkar","къарачай-малкъар","krc"],["Kashmiri","कॉशुर / کٲشُر","ks"],["Colognian","Ripoarisch","ksh"],["Kurdish","Kurdî","ku"],["Komi","коми","kv"],["Cornish","kernowek","kw"],["Kyrgyz","Кыргызча","ky"],["Latin","Latina","la"],["Ladino","Ladino","lad"],["Luxembourgish","Lëtzebuergesch","lb"],["лакку","лакку","lbe"],["Lezghian","лезги","lez"],["Ganda","Luganda","lg"],["Limburgish","Limburgs","li"],["Ligurian","Ligure","lij"],["Lombard","lumbaart","lmo"],["Lingala","lingála","ln"],["Lao","ລາວ","lo"],["Northern Luri","لۊری شومالی","lrc"],["Lithuanian","lietuvių","lt"],["Latgalian","latgaļu","ltg"],["Latvian","latviešu","lv"],["Maithili","मैथिली","mai"],["Basa Banyumasan","Basa Banyumasan","map-bms"],["Moksha","мокшень","mdf"],["Malagasy","Malagasy","mg"],["Eastern Mari","олык марий","mhr"],["Maori","Māori","mi"],["Minangkabau","Baso Minangkabau","min"],["Macedonian","македонски","mk"],["Malayalam","മലയാളം","ml"],["Mongolian","монгол","mn"],["Marathi","मराठी","mr"],["Western Mari","кырык мары","mrj"],["Malay","Bahasa Melayu","ms"],["Maltese","Malti","mt"],["Mirandese","Mirandés","mwl"],["Burmese","မြန်မာဘာသာ","my"],["Erzya","эрзянь","myv"],["Mazanderani","مازِرونی","mzn"],["Nauru","Dorerin Naoero","na"],["Nāhuatl","Nāhuatl","nah"],["Neapolitan","Napulitano","nap"],["Low German","Plattdüütsch","nds"],["Low Saxon","Nedersaksies","nds-nl"],["Nepali","नेपाली","ne"],["Newari","नेपाल भाषा","new"],["Dutch","Nederlands","nl"],["Norwegian Nynorsk","norsk nynorsk","nn"],["Norwegian","norsk bokmål","no"],["Novial","Novial","nov"],["Nouormand","Nouormand","nrm"],["Northern Sotho","Sesotho sa Leboa","nso"],["Navajo","Diné bizaad","nv"],["Nyanja","Chi-Chewa","ny"],["Occitan","occitan","oc"],["Oromo","Oromoo","om"],["Oriya","ଓଡ଼ିଆ","or"],["Ossetic","Ирон","os"],["Punjabi","ਪੰਜਾਬੀ","pa"],["Pangasinan","Pangasinan","pag"],["Pampanga","Kapampangan","pam"],["Papiamento","Papiamentu","pap"],["Picard","Picard","pcd"],["Pennsylvania German","Deitsch","pdc"],["Palatine German","Pälzisch","pfl"],["Pali","पालि","pi"],["Norfuk / Pitkern","Norfuk / Pitkern","pih"],["Polish","polski","pl"],["Piedmontese","Piemontèis","pms"],["Western Punjabi","پنجابی","pnb"],["Pontic","Ποντιακά","pnt"],["Pashto","پښتو","ps"],["Portuguese","português","pt"],["Quechua","Runa Simi","qu"],["Romansh","rumantsch","rm"],["Romani","Romani","rmy"],["Rundi","Kirundi","rn"],["Romanian","română","ro"],["Aromanian","armãneashti","roa-rup"],["tarandíne","tarandíne","roa-tara"],["Russian","русский","ru"],["Rusyn","русиньскый","rue"],["Kinyarwanda","Kinyarwanda","rw"],["Sanskrit","संस्कृतम्","sa"],["Sakha","саха тыла","sah"],["Sardinian","sardu","sc"],["Sicilian","sicilianu","scn"],["Scots","Scots","sco"],["Sindhi","سنڌي","sd"],["Northern Sami","sámegiella","se"],["Sango","Sängö","sg"],["Serbo-Croatian","srpskohrvatski / српскохрватски","sh"],["Sinhala","සිංහල","si"],["Simple English","Simple English","simple"],["Slovak","slovenčina","sk"],["Slovenian","slovenščina","sl"],["Samoan","Gagana Samoa","sm"],["Shona","chiShona","sn"],["Somali","Soomaaliga","so"],["Albanian","shqip","sq"],["Serbian","српски / srpski","sr"],["Sranan Tongo","Sranantongo","srn"],["Swati","SiSwati","ss"],["Southern Sotho","Sesotho","st"],["Saterland Frisian","Seeltersk","stq"],["Sundanese","Basa Sunda","su"],["Swedish","svenska","sv"],["Swahili","Kiswahili","sw"],["Silesian","ślůnski","szl"],["Tamil","தமிழ்","ta"],["Telugu","తెలుగు","te"],["Tetum","tetun","tet"],["Tajik","тоҷикӣ","tg"],["Thai","ไทย","th"],["Tigrinya","ትግርኛ","ti"],["Turkmen","Türkmençe","tk"],["Tagalog","Tagalog","tl"],["Tswana","Setswana","tn"],["Tongan","lea faka-Tonga","to"],["Tok Pisin","Tok Pisin","tpi"],["Turkish","Türkçe","tr"],["Tsonga","Xitsonga","ts"],["Tatar","татарча/tatarça","tt"],["Tumbuka","chiTumbuka","tum"],["Twi","Twi","tw"],["Tahitian","reo tahiti","ty"],["Tuvinian","тыва дыл","tyv"],["Udmurt","удмурт","udm"],["Uyghur","ئۇيغۇرچە / Uyghurche","ug"],["Ukrainian","українська","uk"],["Urdu","اردو","ur"],["Uzbek","oʻzbekcha/ўзбекча","uz"],["Venda","Tshivenda","ve"],["Venetian","vèneto","vec"],["Veps","vepsän kel’","vep"],["Vietnamese","Tiếng Việt","vi"],["West Flemish","West-Vlams","vls"],["Volapük","Volapük","vo"],["Walloon","walon","wa"],["Waray","Winaray","war"],["Wolof","Wolof","wo"],["Wu Chinese","吴语","wuu"],["Kalmyk","хальмг","xal"],["Xhosa","isiXhosa","xh"],["Mingrelian","მარგალური","xmf"],["Yiddish","ייִדיש","yi"],["Yoruba","Yorùbá","yo"],["Zhuang","Vahcuengh","za"],["Zeelandic","Zeêuws","zea"],["Chinese","中文","zh"],["Classical Chinese","文言","zh-classical"],["Chinese (Min Nan)","Bân-lâm-gú","zh-min-nan"],["Cantonese","粵語","zh-yue"],["Zulu","isiZulu","zu"]],"imperial":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"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","switch":"Switch back to this background","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"Adjust imagery offset","imagery_source_faq":"Where does this imagery come from?","reset":"reset","offset":"Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.","minimap":{"description":"Minimap","tooltip":"Show a zoomed out map to help locate the area currently displayed."}},"map_data":{"title":"Map Data","description":"Map Data","data_layers":"Data Layers","fill_area":"Fill Areas","map_features":"Map Features","autohidden":"These features have been automatically hidden because too many would be shown on the screen.  You can zoom in to edit them."},"feature":{"points":{"description":"Points","tooltip":"Points of Interest"},"traffic_roads":{"description":"Traffic Roads","tooltip":"Highways, Streets, etc."},"service_roads":{"description":"Service Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically.  When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-barrier":{"name":"Barrier Features"},"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"bin":{"label":"Waste Bin"},"boundary":{"label":"Type"},"brand":{"label":"Brand"},"building":{"label":"Building"},"building_area":{"label":"Building"},"capacity":{"label":"Capacity","placeholder":"50, 100, 200..."},"cardinal_direction":{"label":"Direction","options":{"N":"North","E":"East","S":"South","W":"West","NE":"Northeast","SE":"Southeast","SW":"Southwest","NW":"Northwest","NNE":"North-northeast","ENE":"East-northeast","ESE":"East-southeast","SSE":"South-southeast","SSW":"South-southwest","WSW":"West-southwest","WNW":"West-northwest","NNW":"North-northwest"}},"clock_direction":{"label":"Direction","options":{"clockwise":"Clockwise","anticlockwise":"Counterclockwise"}},"collection_times":{"label":"Collection Times"},"construction":{"label":"Type"},"content":{"label":"Contents"},"country":{"label":"Country"},"covered":{"label":"Covered"},"craft":{"label":"Type"},"crop":{"label":"Crop"},"crossing":{"label":"Type"},"cuisine":{"label":"Cuisine"},"currency_multi":{"label":"Currency Types"},"cycleway":{"label":"Bike Lanes","placeholder":"none","types":{"cycleway:left":"Left side","cycleway:right":"Right side"},"options":{"none":{"title":"None","description":"No bike lane"},"lane":{"title":"Standard bike lane","description":"A bike lane separated from auto traffic by a painted line"},"shared_lane":{"title":"Shared bike lane","description":"A bike lane with no separation from auto traffic"},"track":{"title":"Bike track","description":"A bike lane separated from traffic by a physical barrier"},"share_busway":{"title":"Bike lane shared with bus","description":"A bike lane shared with a bus lane"},"opposite_lane":{"title":"Opposite bike lane","description":"A bike lane that travels in the opposite direction of traffic"},"opposite":{"title":"Contraflow bike lane","description":"A bike lane that travels in both directions on a one-way street"}}},"delivery":{"label":"Delivery"},"denomination":{"label":"Denomination"},"denotation":{"label":"Denotation"},"description":{"label":"Description"},"diaper":{"label":"Diaper Changing Available"},"dock":{"label":"Type"},"drive_through":{"label":"Drive-Through"},"electrified":{"label":"Electrification","placeholder":"Contact Line, Electrified Rail...","options":{"contact_line":"Contact Line","rail":"Electrified Rail","yes":"Yes (unspecified)","no":"No"}},"elevation":{"label":"Elevation"},"emergency":{"label":"Emergency"},"entrance":{"label":"Type"},"except":{"label":"Exceptions"},"fax":{"label":"Fax","placeholder":"+31 42 123 4567"},"fee":{"label":"Fee"},"fire_hydrant/type":{"label":"Type","options":{"pillar":"Pillar/Aboveground","underground":"Underground","wall":"Wall","pond":"Pond"}},"fixme":{"label":"Fix Me"},"fuel":{"label":"Fuel"},"fuel_multi":{"label":"Fuel Types"},"gauge":{"label":"Gauge"},"gender":{"label":"Gender","placeholder":"Unknown","options":{"male":"Male","female":"Female","unisex":"Unisex"}},"generator/method":{"label":"Method"},"generator/source":{"label":"Source"},"generator/type":{"label":"Type"},"golf_hole":{"label":"Reference","placeholder":"Hole number (1-18)"},"handicap":{"label":"Handicap","placeholder":"1-18"},"handrail":{"label":"Handrail"},"highway":{"label":"Type"},"historic":{"label":"Type"},"hoops":{"label":"Hoops","placeholder":"1, 2, 4..."},"iata":{"label":"IATA"},"icao":{"label":"ICAO"},"incline":{"label":"Incline"},"incline_steps":{"label":"Incline","options":{"up":"Up","down":"Down"}},"information":{"label":"Type"},"internet_access":{"label":"Internet Access","options":{"yes":"Yes","no":"No","wlan":"Wifi","wired":"Wired","terminal":"Terminal"}},"lamp_type":{"label":"Type"},"landuse":{"label":"Type"},"lanes":{"label":"Lanes","placeholder":"1, 2, 3..."},"layer":{"label":"Layer"},"leaf_cycle":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous","mixed":"Mixed"}},"leaf_cycle_singular":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous"}},"leaf_type":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","mixed":"Mixed","leafless":"Leafless"}},"leaf_type_singular":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","leafless":"Leafless"}},"leisure":{"label":"Type"},"length":{"label":"Length (Meters)"},"level":{"label":"Level"},"levels":{"label":"Levels","placeholder":"2, 4, 6..."},"lit":{"label":"Lit"},"location":{"label":"Location"},"man_made":{"label":"Type"},"maxspeed":{"label":"Speed Limit","placeholder":"40, 50, 60..."},"maxstay":{"label":"Max Stay"},"mtb/scale":{"label":"Mountain Biking Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Solid gravel/packed earth, no obstacles, wide curves","1":"1: Some loose surface, small obstacles, wide curves","2":"2: Much loose surface, large obstacles, easy hairpins","3":"3: Slippery surface, large obstacles, tight hairpins","4":"4: Loose surface or boulders, dangerous hairpins","5":"5: Maximum difficulty, boulder fields, landslides","6":"6: Not rideable except by the very best mountain bikers"}},"mtb/scale/imba":{"label":"IMBA Trail Difficulty","placeholder":"Easy, Medium, Difficult...","options":{"0":"Easiest (white circle)","1":"Easy (green circle)","2":"Medium (blue square)","3":"Difficult (black diamond)","4":"Extremely Difficult (double black diamond)"}},"mtb/scale/uphill":{"label":"Mountain Biking Uphill Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Avg. incline <10%, gravel/packed earth, no obstacles","1":"1: Avg. incline <15%, gravel/packed earth, few small objects","2":"2: Avg. incline <20%, stable surface, fistsize rocks/roots","3":"3: Avg. incline <25%, variable surface, fistsize rocks/branches","4":"4: Avg. incline <30%, poor condition, big rocks/branches","5":"5: Very steep, bike generally needs to be pushed or carried"}},"name":{"label":"Name","placeholder":"Common name (if any)"},"natural":{"label":"Natural"},"network":{"label":"Network"},"note":{"label":"Note"},"office":{"label":"Type"},"oneway":{"label":"One Way","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"oneway_yes":{"label":"One Way","options":{"undefined":"Assumed to be Yes","yes":"Yes","no":"No"}},"opening_hours":{"label":"Hours"},"operator":{"label":"Operator"},"par":{"label":"Par","placeholder":"3, 4, 5..."},"park_ride":{"label":"Park and Ride"},"parking":{"label":"Type","options":{"surface":"Surface","multi-storey":"Multilevel","underground":"Underground","sheds":"Sheds","carports":"Carports","garage_boxes":"Garage Boxes","lane":"Roadside Lane"}},"payment_multi":{"label":"Payment Types"},"phone":{"label":"Phone","placeholder":"+31 42 123 4567"},"piste/difficulty":{"label":"Difficulty","placeholder":"Easy, Intermediate, Advanced...","options":{"novice":"Novice (instructional)","easy":"Easy (green circle)","intermediate":"Intermediate (blue square)","advanced":"Advanced (black diamond)","expert":"Expert (double black diamond)","freeride":"Freeride (off-piste)","extreme":"Extreme (climbing equipment required)"}},"piste/grooming":{"label":"Grooming","options":{"classic":"Classic","mogul":"Mogul","backcountry":"Backcountry","classic+skating":"Classic and Skating","scooter":"Scooter/Snowmobile","skating":"Skating"}},"piste/type":{"label":"Type","options":{"downhill":"Downhill","nordic":"Nordic","skitour":"Skitour","sled":"Sled","hike":"Hike","sleigh":"Sleigh","ice_skate":"Ice Skate","snow_park":"Snow Park","playground":"Playground"}},"place":{"label":"Type"},"population":{"label":"Population"},"power":{"label":"Type"},"power_supply":{"label":"Power Supply"},"railway":{"label":"Type"},"recycling_accepts":{"label":"Accepts"},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio":{"label":"Type"},"substation":{"label":"Type"},"supervised":{"label":"Supervised"},"surface":{"label":"Surface"},"tactile_paving":{"label":"Tactile Paving"},"takeaway":{"label":"Takeaway","placeholder":"Yes, No, Takeaway Only...","options":{"yes":"Yes","no":"No","only":"Takeaway Only"}},"toilets/disposal":{"label":"Disposal","options":{"flush":"Flush","pitlatrine":"Pit/Latrine","chemical":"Chemical","bucket":"Bucket"}},"tourism":{"label":"Type"},"towertype":{"label":"Tower type"},"tracktype":{"label":"Track Type","placeholder":"Solid, Mostly Solid, Soft...","options":{"grade1":"Solid: paved or heavily compacted hardcore surface","grade2":"Mostly Solid: gravel/rock with some soft material mixed in","grade3":"Even mixture of hard and soft materials","grade4":"Mostly Soft: soil/sand/grass with some hard material mixed in","grade5":"Soft: soil/sand/grass"}},"traffic_signals":{"label":"Type"},"trail_visibility":{"label":"Trail Visibility","placeholder":"Excellent, Good, Bad...","options":{"excellent":"Excellent: unambiguous path or markers everywhere","good":"Good: markers visible, sometimes require searching","intermediate":"Intermediate: few markers, path mostly visible","bad":"Bad: no markers, path sometimes invisible/pathless","horrible":"Horrible: often pathless, some orientation skills required","no":"No: pathless, excellent orientation skills required"}},"trees":{"label":"Trees"},"tunnel":{"label":"Tunnel"},"vending":{"label":"Type of Goods"},"water":{"label":"Type"},"water_point":{"label":"Water Point"},"waterway":{"label":"Type"},"website":{"label":"Website","placeholder":"http://example.com/"},"wetland":{"label":"Type"},"wheelchair":{"label":"Wheelchair Access"},"width":{"label":"Width (Meters)"},"wikipedia":{"label":"Wikipedia"}},"presets":{"address":{"name":"Address","terms":""},"aerialway":{"name":"Aerialway","terms":"ski lift,funifor,funitel"},"aerialway/cable_car":{"name":"Cable Car","terms":"tramway,ropeway"},"aerialway/chair_lift":{"name":"Chair Lift","terms":""},"aerialway/gondola":{"name":"Gondola","terms":""},"aerialway/magic_carpet":{"name":"Magic Carpet Lift","terms":""},"aerialway/platter":{"name":"Platter Lift","terms":"button lift,poma lift"},"aerialway/pylon":{"name":"Aerialway Pylon","terms":""},"aerialway/rope_tow":{"name":"Rope Tow Lift","terms":"handle tow,bugel lift"},"aerialway/station":{"name":"Aerialway Station","terms":""},"aerialway/t-bar":{"name":"T-bar Lift","terms":""},"aeroway":{"name":"Aeroway","terms":""},"aeroway/aerodrome":{"name":"Airport","terms":"airplane,airport,aerodrome"},"aeroway/apron":{"name":"Apron","terms":"ramp"},"aeroway/gate":{"name":"Airport gate","terms":""},"aeroway/hangar":{"name":"Hangar","terms":""},"aeroway/helipad":{"name":"Helipad","terms":"helicopter,helipad,heliport"},"aeroway/runway":{"name":"Runway","terms":"landing strip"},"aeroway/taxiway":{"name":"Taxiway","terms":""},"aeroway/terminal":{"name":"Airport terminal","terms":"airport,aerodrome"},"amenity":{"name":"Amenity","terms":""},"amenity/arts_centre":{"name":"Arts Center","terms":""},"amenity/atm":{"name":"ATM","terms":"money,cash,machine"},"amenity/bank":{"name":"Bank","terms":"credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"},"amenity/bar":{"name":"Bar","terms":"dive,beer,bier,booze"},"amenity/bbq":{"name":"Barbecue/Grill","terms":"bbq"},"amenity/bench":{"name":"Bench","terms":"seat"},"amenity/bicycle_parking":{"name":"Bicycle Parking","terms":"bike"},"amenity/bicycle_rental":{"name":"Bicycle Rental","terms":"bike"},"amenity/bicycle_repair_station":{"name":"Bicycle Repair Tool Stand","terms":"bike,repair,chain,pump"},"amenity/biergarten":{"name":"Beer Garden","terms":"beer,bier,booze"},"amenity/boat_rental":{"name":"Boat Rental","terms":""},"amenity/bureau_de_change":{"name":"Currency Exchange","terms":"bureau de change,money changer"},"amenity/bus_station":{"name":"Bus Station","terms":""},"amenity/cafe":{"name":"Cafe","terms":"bistro,coffee,tea"},"amenity/car_rental":{"name":"Car Rental","terms":""},"amenity/car_sharing":{"name":"Car Sharing","terms":""},"amenity/car_wash":{"name":"Car Wash","terms":""},"amenity/casino":{"name":"Casino","terms":"gambling,roulette,craps,poker,blackjack"},"amenity/charging_station":{"name":"Charging Station","terms":"EV,Electric Vehicle,Supercharger"},"amenity/childcare":{"name":"Nursery/Childcare","terms":"daycare,orphanage,playgroup"},"amenity/cinema":{"name":"Cinema","terms":"drive-in,film,flick,movie,theater,picture,show,screen"},"amenity/clinic":{"name":"Clinic","terms":"medical,urgentcare"},"amenity/clock":{"name":"Clock","terms":""},"amenity/college":{"name":"College Grounds","terms":"university"},"amenity/community_centre":{"name":"Community Center","terms":"event,hall"},"amenity/compressed_air":{"name":"Compressed Air","terms":""},"amenity/courthouse":{"name":"Courthouse","terms":""},"amenity/dentist":{"name":"Dentist","terms":"tooth,teeth"},"amenity/doctors":{"name":"Doctor","terms":"medic*"},"amenity/dojo":{"name":"Dojo / Martial Arts Academy","terms":"martial arts,dojang"},"amenity/drinking_water":{"name":"Drinking Water","terms":"fountain,potable"},"amenity/embassy":{"name":"Embassy","terms":""},"amenity/fast_food":{"name":"Fast Food","terms":"restaurant"},"amenity/ferry_terminal":{"name":"Ferry Terminal","terms":""},"amenity/fire_station":{"name":"Fire Station","terms":""},"amenity/fountain":{"name":"Fountain","terms":""},"amenity/fuel":{"name":"Gas Station","terms":"petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"},"amenity/grave_yard":{"name":"Graveyard","terms":""},"amenity/grit_bin":{"name":"Grit Bin","terms":"salt,sand"},"amenity/hospital":{"name":"Hospital Grounds","terms":"clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"},"amenity/hunting_stand":{"name":"Hunting Stand","terms":""},"amenity/kindergarten":{"name":"Preschool/Kindergarten Grounds","terms":"kindergarden,pre-school"},"amenity/library":{"name":"Library","terms":"book"},"amenity/marketplace":{"name":"Marketplace","terms":""},"amenity/motorcycle_parking":{"name":"Motorcycle Parking","terms":""},"amenity/nightclub":{"name":"Nightclub","terms":"disco*,night club,dancing,dance club"},"amenity/parking":{"name":"Car Parking","terms":""},"amenity/parking_entrance":{"name":"Parking Garage Entrance/Exit","terms":""},"amenity/parking_space":{"name":"Parking Space","terms":""},"amenity/pharmacy":{"name":"Pharmacy","terms":"drug,medicine"},"amenity/place_of_worship":{"name":"Place of Worship","terms":"abbey,basilica,bethel,cathedral,chancel,chantry,chapel,church,fold,house of God,house of prayer,house of worship,minster,mission,mosque,oratory,parish,sacellum,sanctuary,shrine,synagogue,tabernacle,temple"},"amenity/place_of_worship/buddhist":{"name":"Buddhist Temple","terms":"stupa,vihara,monastery,temple,pagoda,zendo,dojo"},"amenity/place_of_worship/christian":{"name":"Church","terms":"christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"},"amenity/place_of_worship/jewish":{"name":"Synagogue","terms":"jewish"},"amenity/place_of_worship/muslim":{"name":"Mosque","terms":"muslim"},"amenity/police":{"name":"Police","terms":"badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"},"amenity/post_box":{"name":"Mailbox","terms":"letter,post"},"amenity/post_office":{"name":"Post Office","terms":"letter,mail"},"amenity/prison":{"name":"Prison Grounds","terms":"cell,jail"},"amenity/pub":{"name":"Pub","terms":"dive,beer,bier,booze"},"amenity/public_bookcase":{"name":"Public Bookcase","terms":"library,bookcrossing"},"amenity/ranger_station":{"name":"Ranger Station","terms":"visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"},"amenity/recycling":{"name":"Recycling","terms":"can,bottle,garbage,scrap,trash"},"amenity/register_office":{"name":"Register Office","terms":""},"amenity/restaurant":{"name":"Restaurant","terms":"bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"},"amenity/sanitary_dump_station":{"name":"RV Toilet Disposal","terms":"Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"},"amenity/school":{"name":"School Grounds","terms":"academy,elementary school,middle school,high school"},"amenity/shelter":{"name":"Shelter","terms":"lean-to,gazebo,picnic"},"amenity/social_facility":{"name":"Social Facility","terms":""},"amenity/social_facility/food_bank":{"name":"Food Bank","terms":""},"amenity/social_facility/group_home":{"name":"Elderly Group Home","terms":"old,senior,living"},"amenity/social_facility/homeless_shelter":{"name":"Homeless Shelter","terms":"houseless,unhoused,displaced"},"amenity/studio":{"name":"Studio","terms":"recording,radio,television"},"amenity/swimming_pool":{"name":"Swimming Pool","terms":""},"amenity/taxi":{"name":"Taxi Stand","terms":"cab"},"amenity/telephone":{"name":"Telephone","terms":"phone"},"amenity/theatre":{"name":"Theater","terms":"theatre,performance,play,musical"},"amenity/toilets":{"name":"Toilets","terms":"bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."},"amenity/townhall":{"name":"Town Hall","terms":"village,city,government,courthouse,municipal"},"amenity/university":{"name":"University Grounds","terms":"college"},"amenity/vending_machine/cigarettes":{"name":"Cigarette Vending Machine","terms":"cigarette"},"amenity/vending_machine/condoms":{"name":"Condom Vending Machine","terms":"condom"},"amenity/vending_machine/drinks":{"name":"Drink Vending Machine","terms":"drink,soda,beverage,juice,pop"},"amenity/vending_machine/excrement_bags":{"name":"Excrement Bag Vending Machine","terms":"excrement bags,poop,dog,animal"},"amenity/vending_machine/news_papers":{"name":"Newspaper Vending Machine","terms":"newspaper"},"amenity/vending_machine/parcel_pickup_dropoff":{"name":"Parcel Pickup/Dropoff Vending Machine","terms":"parcel,mail,pickup"},"amenity/vending_machine/parking_tickets":{"name":"Parking Ticket Vending Machine","terms":"parking,ticket"},"amenity/vending_machine/public_transport_tickets":{"name":"Transit Ticket Vending Machine","terms":"bus,train,ferry,rail,ticket,transportation"},"amenity/vending_machine/sweets":{"name":"Snack Vending Machine","terms":"candy,gum,chip,pretzel,cookie,cracker"},"amenity/vending_machine/vending_machine":{"name":"Vending Machine","terms":""},"amenity/veterinary":{"name":"Veterinary","terms":"pet clinic,veterinarian,animal hospital,pet doctor"},"amenity/waste_basket":{"name":"Waste Basket","terms":"bin,rubbish,litter,trash,garbage"},"amenity/waste_disposal":{"name":"Garbage Dumpster","terms":"rubbish,litter,trash"},"amenity/water_point":{"name":"RV Drinking Water","terms":""},"area":{"name":"Area","terms":""},"area/highway":{"name":"Road Surface","terms":""},"barrier":{"name":"Barrier","terms":""},"barrier/block":{"name":"Block","terms":""},"barrier/bollard":{"name":"Bollard","terms":""},"barrier/cattle_grid":{"name":"Cattle Grid","terms":""},"barrier/city_wall":{"name":"City Wall","terms":""},"barrier/cycle_barrier":{"name":"Cycle Barrier","terms":""},"barrier/ditch":{"name":"Trench","terms":""},"barrier/entrance":{"name":"Entrance","terms":""},"barrier/fence":{"name":"Fence","terms":""},"barrier/gate":{"name":"Gate","terms":""},"barrier/hedge":{"name":"Hedge","terms":""},"barrier/kissing_gate":{"name":"Kissing Gate","terms":""},"barrier/lift_gate":{"name":"Lift Gate","terms":""},"barrier/retaining_wall":{"name":"Retaining Wall","terms":""},"barrier/stile":{"name":"Stile","terms":""},"barrier/toll_booth":{"name":"Toll Booth","terms":""},"barrier/wall":{"name":"Wall","terms":""},"boundary/administrative":{"name":"Administrative Boundary","terms":""},"building":{"name":"Building","terms":""},"building/apartments":{"name":"Apartments","terms":""},"building/barn":{"name":"Barn","terms":""},"building/bunker":{"name":"Bunker","terms":""},"building/cabin":{"name":"Cabin","terms":""},"building/cathedral":{"name":"Cathedral Building","terms":""},"building/chapel":{"name":"Chapel Building","terms":""},"building/church":{"name":"Church Building","terms":""},"building/college":{"name":"College Building","terms":"university"},"building/commercial":{"name":"Commercial Building","terms":""},"building/construction":{"name":"Building Under Construction","terms":""},"building/detached":{"name":"Detached House","terms":"home,single,family,residence,dwelling"},"building/dormitory":{"name":"Dormitory","terms":""},"building/entrance":{"name":"Entrance/Exit","terms":""},"building/garage":{"name":"Garage","terms":""},"building/garages":{"name":"Garages","terms":""},"building/greenhouse":{"name":"Greenhouse","terms":""},"building/hospital":{"name":"Hospital Building","terms":""},"building/hotel":{"name":"Hotel Building","terms":""},"building/house":{"name":"House","terms":"home,family,residence,dwelling"},"building/hut":{"name":"Hut","terms":""},"building/industrial":{"name":"Industrial Building","terms":""},"building/kindergarten":{"name":"Preschool/Kindergarten Building","terms":"kindergarden,pre-school"},"building/public":{"name":"Public Building","terms":""},"building/residential":{"name":"Residential Building","terms":""},"building/retail":{"name":"Retail Building","terms":""},"building/roof":{"name":"Roof","terms":""},"building/school":{"name":"School Building","terms":"academy,elementary school,middle school,high school"},"building/semidetached_house":{"name":"Semi-Detached House","terms":"home,double,duplex,twin,family,residence,dwelling"},"building/shed":{"name":"Shed","terms":""},"building/stable":{"name":"Stable","terms":""},"building/static_caravan":{"name":"Static Mobile Home","terms":""},"building/terrace":{"name":"Row Houses","terms":"home,terrace,brownstone,family,residence,dwelling"},"building/train_station":{"name":"Train Station","terms":""},"building/university":{"name":"University Building","terms":"college"},"building/warehouse":{"name":"Warehouse","terms":""},"craft":{"name":"Craft","terms":""},"craft/basket_maker":{"name":"Basket Maker","terms":""},"craft/beekeeper":{"name":"Beekeeper","terms":""},"craft/blacksmith":{"name":"Blacksmith","terms":""},"craft/boatbuilder":{"name":"Boat Builder","terms":""},"craft/bookbinder":{"name":"Bookbinder","terms":"book repair"},"craft/brewery":{"name":"Brewery","terms":"beer,bier"},"craft/carpenter":{"name":"Carpenter","terms":"woodworker"},"craft/carpet_layer":{"name":"Carpet Layer","terms":""},"craft/caterer":{"name":"Caterer","terms":""},"craft/clockmaker":{"name":"Clockmaker","terms":""},"craft/confectionery":{"name":"Confectionery","terms":"sweets,candy"},"craft/dressmaker":{"name":"Dressmaker","terms":"seamstress"},"craft/electrician":{"name":"Electrician","terms":"power,wire"},"craft/gardener":{"name":"Gardener","terms":"landscaper,grounds keeper"},"craft/glaziery":{"name":"Glaziery","terms":"glass,stained-glass,window"},"craft/handicraft":{"name":"Handicraft","terms":""},"craft/hvac":{"name":"HVAC","terms":"heat*,vent*,air conditioning"},"craft/insulator":{"name":"Insulator","terms":""},"craft/jeweler":{"name":"Jeweler","terms":""},"craft/key_cutter":{"name":"Key Cutter","terms":""},"craft/locksmith":{"name":"Locksmith","terms":""},"craft/metal_construction":{"name":"Metal Construction","terms":""},"craft/optician":{"name":"Optician","terms":""},"craft/painter":{"name":"Painter","terms":""},"craft/photographer":{"name":"Photographer","terms":""},"craft/photographic_laboratory":{"name":"Photographic Laboratory","terms":"film"},"craft/plasterer":{"name":"Plasterer","terms":""},"craft/plumber":{"name":"Plumber","terms":"pipe"},"craft/pottery":{"name":"Pottery","terms":"ceramic"},"craft/rigger":{"name":"Rigger","terms":""},"craft/roofer":{"name":"Roofer","terms":""},"craft/saddler":{"name":"Saddler","terms":""},"craft/sailmaker":{"name":"Sailmaker","terms":""},"craft/sawmill":{"name":"Sawmill","terms":"lumber"},"craft/scaffolder":{"name":"Scaffolder","terms":""},"craft/sculpter":{"name":"Sculpter","terms":""},"craft/shoemaker":{"name":"Shoemaker","terms":"cobbler"},"craft/stonemason":{"name":"Stonemason","terms":"masonry"},"craft/sweep":{"name":"Chimney Sweep","terms":""},"craft/tailor":{"name":"Tailor","terms":"clothes,suit"},"craft/tiler":{"name":"Tiler","terms":""},"craft/tinsmith":{"name":"Tinsmith","terms":""},"craft/upholsterer":{"name":"Upholsterer","terms":""},"craft/watchmaker":{"name":"Watchmaker","terms":""},"craft/window_construction":{"name":"Window Construction","terms":"glass"},"craft/winery":{"name":"Winery","terms":""},"embankment":{"name":"Embankment","terms":""},"emergency/ambulance_station":{"name":"Ambulance Station","terms":"EMS,EMT,rescue"},"emergency/fire_hydrant":{"name":"Fire Hydrant","terms":""},"emergency/phone":{"name":"Emergency Phone","terms":""},"entrance":{"name":"Entrance/Exit","terms":""},"footway/crossing":{"name":"Street Crossing","terms":""},"footway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"footway/sidewalk":{"name":"Sidewalk","terms":""},"ford":{"name":"Ford","terms":""},"golf/bunker":{"name":"Sand Trap","terms":"hazard,bunker"},"golf/fairway":{"name":"Fairway","terms":""},"golf/green":{"name":"Putting Green","terms":""},"golf/hole":{"name":"Golf Hole","terms":""},"golf/lateral_water_hazard":{"name":"Lateral Water Hazard","terms":""},"golf/rough":{"name":"Rough","terms":""},"golf/tee":{"name":"Tee Box","terms":"teeing ground"},"golf/water_hazard":{"name":"Water Hazard","terms":""},"highway":{"name":"Highway","terms":""},"highway/bridleway":{"name":"Bridle Path","terms":"bridleway,equestrian,horse"},"highway/bus_stop":{"name":"Bus Stop","terms":""},"highway/corridor":{"name":"Indoor Corridor","terms":"gallery,hall,hallway,indoor,passage,passageway"},"highway/crossing":{"name":"Street Crossing","terms":""},"highway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"highway/cycleway":{"name":"Cycle Path","terms":"bike"},"highway/footway":{"name":"Foot Path","terms":"hike,hiking,trackway,trail,walk"},"highway/living_street":{"name":"Living Street","terms":""},"highway/mini_roundabout":{"name":"Mini-Roundabout","terms":""},"highway/motorway":{"name":"Motorway","terms":""},"highway/motorway_junction":{"name":"Motorway Junction / Exit","terms":""},"highway/motorway_link":{"name":"Motorway Link","terms":"ramp,on ramp,off ramp"},"highway/path":{"name":"Path","terms":"hike,hiking,trackway,trail,walk"},"highway/pedestrian":{"name":"Pedestrian Street","terms":""},"highway/primary":{"name":"Primary Road","terms":""},"highway/primary_link":{"name":"Primary Link","terms":"ramp,on ramp,off ramp"},"highway/raceway":{"name":"Motor Raceway","terms":"auto*,race*,nascar"},"highway/residential":{"name":"Residential Road","terms":""},"highway/rest_area":{"name":"Rest Area","terms":"rest stop"},"highway/road":{"name":"Unknown Road","terms":""},"highway/secondary":{"name":"Secondary Road","terms":""},"highway/secondary_link":{"name":"Secondary Link","terms":"ramp,on ramp,off ramp"},"highway/service":{"name":"Service Road","terms":""},"highway/service/alley":{"name":"Alley","terms":""},"highway/service/drive-through":{"name":"Drive-Through","terms":""},"highway/service/driveway":{"name":"Driveway","terms":""},"highway/service/emergency_access":{"name":"Emergency Access","terms":""},"highway/service/parking_aisle":{"name":"Parking Aisle","terms":""},"highway/services":{"name":"Service Area","terms":"services,travel plaza,service station"},"highway/steps":{"name":"Steps","terms":"stairs,staircase"},"highway/stop":{"name":"Stop Sign","terms":"stop sign"},"highway/street_lamp":{"name":"Street Lamp","terms":"streetlight,street light,lamp,light,gaslight"},"highway/tertiary":{"name":"Tertiary Road","terms":""},"highway/tertiary_link":{"name":"Tertiary Link","terms":"ramp,on ramp,off ramp"},"highway/track":{"name":"Unmaintained Track Road","terms":"woods road,forest road,logging road,fire road,farm road,agricultural road,ranch road,carriage road,primitive,unmaintained,rut,offroad,4wd,4x4,four wheel drive,atv,quad,jeep,double track,two track"},"highway/traffic_signals":{"name":"Traffic Signals","terms":"light,stoplight,traffic light"},"highway/trunk":{"name":"Trunk Road","terms":""},"highway/trunk_link":{"name":"Trunk Link","terms":"ramp,on ramp,off ramp"},"highway/turning_circle":{"name":"Turning Circle","terms":"cul-de-sac"},"highway/unclassified":{"name":"Minor/Unclassified Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bird_hide":{"name":"Bird Hide","terms":"machan,ornithology"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":"dock"},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/coffee":{"name":"Coffee Store","terms":""},"shop/computer":{"name":"Computer Store","terms":""},"shop/confectionery":{"name":"Candy Store","terms":""},"shop/convenience":{"name":"Convenience Store","terms":""},"shop/copyshop":{"name":"Copy Store","terms":""},"shop/cosmetics":{"name":"Cosmetics Store","terms":""},"shop/craft":{"name":"Arts and Crafts Store","terms":"art*,paint*,frame"},"shop/curtain":{"name":"Curtain Store","terms":"drape*,window"},"shop/dairy":{"name":"Dairy Store","terms":"milk,egg,cheese"},"shop/deli":{"name":"Deli","terms":"lunch,meat,sandwich"},"shop/department_store":{"name":"Department Store","terms":""},"shop/doityourself":{"name":"DIY Store","terms":""},"shop/dry_cleaning":{"name":"Dry Cleaner","terms":""},"shop/electronics":{"name":"Electronics Store","terms":"appliance,audio,computer,tv"},"shop/erotic":{"name":"Erotic Store","terms":"sex,porn"},"shop/fabric":{"name":"Fabric Store","terms":"sew"},"shop/farm":{"name":"Produce Stand","terms":"farm shop,farm stand"},"shop/fashion":{"name":"Fashion Store","terms":""},"shop/fishmonger":{"name":"Fishmonger","terms":""},"shop/florist":{"name":"Florist","terms":"flower"},"shop/frame":{"name":"Framing Shop","terms":"art*,paint*,photo*,frame"},"shop/funeral_directors":{"name":"Funeral Home","terms":"undertaker,memorial home"},"shop/furnace":{"name":"Furnace Store","terms":"oven,stove"},"shop/furniture":{"name":"Furniture Store","terms":"chair,sofa,table"},"shop/garden_centre":{"name":"Garden Center","terms":"landscape,mulch,shrub,tree"},"shop/gift":{"name":"Gift Shop","terms":""},"shop/greengrocer":{"name":"Greengrocer","terms":"fruit,vegetable"},"shop/hairdresser":{"name":"Hairdresser","terms":""},"shop/hardware":{"name":"Hardware Store","terms":""},"shop/hearing_aids":{"name":"Hearing Aids Store","terms":""},"shop/herbalist":{"name":"Herbalist","terms":""},"shop/hifi":{"name":"Hifi Store","terms":"stereo,video"},"shop/houseware":{"name":"Houseware Store","terms":"home,household"},"shop/interior_decoration":{"name":"Interior Decoration Store","terms":""},"shop/jewelry":{"name":"Jeweler","terms":"diamond,gem,ring"},"shop/kiosk":{"name":"News Kiosk","terms":""},"shop/kitchen":{"name":"Kitchen Design Store","terms":""},"shop/laundry":{"name":"Laundry","terms":""},"shop/leather":{"name":"Leather Store","terms":""},"shop/locksmith":{"name":"Locksmith","terms":"key,lockpick"},"shop/lottery":{"name":"Lottery Shop","terms":""},"shop/mall":{"name":"Mall","terms":""},"shop/massage":{"name":"Massage Shop","terms":""},"shop/medical_supply":{"name":"Medical Supply Store","terms":""},"shop/mobile_phone":{"name":"Mobile Phone Store","terms":""},"shop/money_lender":{"name":"Money Lender","terms":""},"shop/motorcycle":{"name":"Motorcycle Dealership","terms":""},"shop/music":{"name":"Music Store","terms":"CD,vinyl"},"shop/musical_instrument":{"name":"Musical Instrument Store","terms":""},"shop/newsagent":{"name":"Newspaper/Magazine Shop","terms":""},"shop/nutrition_supplements":{"name":"Nutrition Supplements Store","terms":""},"shop/optician":{"name":"Optician","terms":"eye,glasses"},"shop/organic":{"name":"Organic Goods Store","terms":""},"shop/outdoor":{"name":"Outdoors Store","terms":"camping,climbing,hiking"},"shop/paint":{"name":"Paint Store","terms":""},"shop/pawnbroker":{"name":"Pawn Shop","terms":""},"shop/pet":{"name":"Pet Store","terms":"cat,dog,fish"},"shop/photo":{"name":"Photography Store","terms":"camera,film"},"shop/pyrotechnics":{"name":"Fireworks Store","terms":""},"shop/radiotechnics":{"name":"Radio/Electronic Component Store","terms":""},"shop/religion":{"name":"Religious Store","terms":""},"shop/scuba_diving":{"name":"Scuba Diving Shop","terms":""},"shop/seafood":{"name":"Seafood Shop","terms":"fishmonger"},"shop/second_hand":{"name":"Consignment/Thrift Store","terms":"secondhand,second hand,resale,thrift,used"},"shop/shoes":{"name":"Shoe Store","terms":""},"shop/sports":{"name":"Sporting Goods Store","terms":""},"shop/stationery":{"name":"Stationery Store","terms":"card,paper"},"shop/storage_rental":{"name":"Storage Rental","terms":""},"shop/supermarket":{"name":"Supermarket","terms":"grocery,store,shop"},"shop/tailor":{"name":"Tailor","terms":"clothes,suit"},"shop/tattoo":{"name":"Tattoo Parlor","terms":""},"shop/tea":{"name":"Tea Store","terms":""},"shop/ticket":{"name":"Ticket Seller","terms":""},"shop/tobacco":{"name":"Tobacco Shop","terms":""},"shop/toys":{"name":"Toy Store","terms":""},"shop/travel_agency":{"name":"Travel Agency","terms":""},"shop/tyres":{"name":"Tire Store","terms":""},"shop/vacant":{"name":"Vacant Shop","terms":""},"shop/vacuum_cleaner":{"name":"Vacuum Cleaner Store","terms":""},"shop/variety_store":{"name":"Variety Store","terms":""},"shop/video":{"name":"Video Store","terms":"DVD"},"shop/video_games":{"name":"Video Game Store","terms":""},"shop/water_sports":{"name":"Watersport/Swim Shop","terms":""},"shop/weapons":{"name":"Weapon Shop","terms":"ammo,gun,knife,knives"},"shop/window_blind":{"name":"Window Blind Store","terms":""},"shop/wine":{"name":"Wine Shop","terms":""},"tourism":{"name":"Tourism","terms":""},"tourism/alpine_hut":{"name":"Alpine Hut","terms":""},"tourism/artwork":{"name":"Artwork","terms":"mural,sculpture,statue"},"tourism/attraction":{"name":"Tourist Attraction","terms":""},"tourism/camp_site":{"name":"Camp Site","terms":"Tent"},"tourism/caravan_site":{"name":"RV Park","terms":"Motor Home,Camper"},"tourism/chalet":{"name":"Chalet","terms":""},"tourism/gallery":{"name":"Art Gallery","terms":"art*,exhibit*,paint*,photo*,sculpt*"},"tourism/guest_house":{"name":"Guest House","terms":"B&B,Bed and Breakfast"},"tourism/hostel":{"name":"Hostel","terms":""},"tourism/hotel":{"name":"Hotel","terms":""},"tourism/information":{"name":"Information","terms":""},"tourism/motel":{"name":"Motel","terms":""},"tourism/museum":{"name":"Museum","terms":"art*,exhibit*,gallery,foundation,hall,institution,paint*,photo*,sculpt*"},"tourism/picnic_site":{"name":"Picnic Site","terms":"camp"},"tourism/theme_park":{"name":"Theme Park","terms":""},"tourism/viewpoint":{"name":"Viewpoint","terms":""},"tourism/zoo":{"name":"Zoo","terms":""},"traffic_calming/bump":{"name":"Speed Bump","terms":"speed hump"},"traffic_calming/hump":{"name":"Speed Hump","terms":"speed bump"},"traffic_calming/rumble_strip":{"name":"Rumble Strip","terms":"sleeper lines,audible lines,growlers"},"traffic_calming/table":{"name":"Raised Pedestrian Crossing","terms":"speed table,flat top hump"},"type/boundary":{"name":"Boundary","terms":""},"type/boundary/administrative":{"name":"Administrative Boundary","terms":""},"type/multipolygon":{"name":"Multipolygon","terms":""},"type/restriction":{"name":"Restriction","terms":""},"type/restriction/no_left_turn":{"name":"No Left Turn","terms":""},"type/restriction/no_right_turn":{"name":"No Right Turn","terms":""},"type/restriction/no_straight_on":{"name":"No Straight On","terms":""},"type/restriction/no_u_turn":{"name":"No U-turn","terms":""},"type/restriction/only_left_turn":{"name":"Left Turn Only","terms":""},"type/restriction/only_right_turn":{"name":"Right Turn Only","terms":""},"type/restriction/only_straight_on":{"name":"No Turns","terms":""},"type/route":{"name":"Route","terms":""},"type/route/bicycle":{"name":"Cycle Route","terms":""},"type/route/bus":{"name":"Bus Route","terms":""},"type/route/detour":{"name":"Detour Route","terms":""},"type/route/ferry":{"name":"Ferry Route","terms":""},"type/route/foot":{"name":"Foot Route","terms":""},"type/route/hiking":{"name":"Hiking Route","terms":""},"type/route/horse":{"name":"Riding Route","terms":""},"type/route/pipeline":{"name":"Pipeline Route","terms":""},"type/route/power":{"name":"Power Route","terms":""},"type/route/road":{"name":"Road Route","terms":""},"type/route/train":{"name":"Train Route","terms":""},"type/route/tram":{"name":"Tram Route","terms":""},"type/route_master":{"name":"Route Master","terms":""},"type/site":{"name":"Site","terms":""},"vertex":{"name":"Other","terms":""},"waterway":{"name":"Waterway","terms":""},"waterway/boatyard":{"name":"Boatyard","terms":""},"waterway/canal":{"name":"Canal","terms":""},"waterway/dam":{"name":"Dam","terms":""},"waterway/ditch":{"name":"Ditch","terms":""},"waterway/dock":{"name":"Wet Dock / Dry Dock","terms":"boat,ship,vessel,marine"},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}],"phoneFormats":{"us":"+1-202-555-1234","ca":"+1-226-555-1234","bs":"+1-242-555-1234","bb":"+1-246-555-1234","ai":"+1-264-555-1234","ag":"+1-268-555-1234","vg":"+1-284-555-1234","vi":"+1-340-555-1234","ky":"+1-345-555-1234","bm":"+1-441-555-1234","gd":"+1-473-555-1234","tc":"+1-649-555-1234","ms":"+1-664-555-1234","mp":"+1-670-555-1234","gu":"+1-671-555-1234","as":"+1-684-555-1234","sx":"+1-721-555-1234","lc":"+1-758-555-1234","dm":"+1-767-555-1234","vc":"+1-784-555-1234","pr":"+1-787-555-1234","do":"+1-809-555-1234","tt":"+1-868-555-1234","kn":"+1-869-555-1234","jm":"+1-876-555-1234","za":"+27 11 907 1111","nl":"+31 42 123 4567","fr":"+33 1 23 45 67 89","es":"+34 989 12 34 56","pt":"+351 211 123456","fi":"+358 40 123 4567","hu":"+36 1 123 45 67","hr":"+385 01 123 4567","si":"+386 31 123 4567","it":"+39 01 123 456","va":"+39 01 123 456","gb":"+44 207 123456","gg":"+44 207 123456","im":"+44 207 123456","je":"+44 207 123456","se":"+46 31 123 4567","no":"+47 22 12 34 56","sj":"+47 22 12 34 56","pl":"+48 42 123 4567","de":"+49 89 1234567","br":"+55 11 0982 1098","ru":"+7 495 1234567","kz":"+7 495 1234567","vn":"+84 1 234 5678","hk":"+852 12345678"}};