]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Update bundle
[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"}), center0 = null;
2000   }
2001
2002   function mousedowned() {
2003     var that = this,
2004         target = d3.event.target,
2005         dispatch = event.of(that, arguments),
2006         dragged = 0,
2007         subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
2008         location0 = location(d3.mouse(that)),
2009         dragRestore = d3_event_dragSuppress(that);
2010
2011     d3_selection_interrupt.call(that);
2012     zoomstarted(dispatch);
2013
2014     function moved() {
2015       dragged = 1;
2016       translateTo(d3.mouse(that), location0);
2017       zoomed(dispatch);
2018     }
2019
2020     function ended() {
2021       subject.on(mousemove, null).on(mouseup, null);
2022       dragRestore(dragged && d3.event.target === target);
2023       zoomended(dispatch);
2024     }
2025   }
2026
2027   // These closures persist for as long as at least one touch is active.
2028   function touchstarted() {
2029     var that = this,
2030         dispatch = event.of(that, arguments),
2031         locations0 = {}, // touchstart locations
2032         distance0 = 0, // distance² between initial touches
2033         scale0, // scale when we started touching
2034         zoomName = ".zoom-" + d3.event.changedTouches[0].identifier,
2035         touchmove = "touchmove" + zoomName,
2036         touchend = "touchend" + zoomName,
2037         targets = [],
2038         subject = d3.select(that),
2039         dragRestore = d3_event_dragSuppress(that);
2040
2041     started();
2042     zoomstarted(dispatch);
2043
2044     // Workaround for Chrome issue 412723: the touchstart listener must be set
2045     // after the touchmove listener.
2046     subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events
2047
2048     // Updates locations of any touches in locations0.
2049     function relocate() {
2050       var touches = d3.touches(that);
2051       scale0 = view.k;
2052       touches.forEach(function(t) {
2053         if (t.identifier in locations0) locations0[t.identifier] = location(t);
2054       });
2055       return touches;
2056     }
2057
2058     // Temporarily override touchstart while gesture is active.
2059     function started() {
2060
2061       // Listen for touchmove and touchend on the target of touchstart.
2062       var target = d3.event.target;
2063       d3.select(target).on(touchmove, moved).on(touchend, ended);
2064       targets.push(target);
2065
2066       // Only track touches started on the same subject element.
2067       var changed = d3.event.changedTouches;
2068       for (var i = 0, n = changed.length; i < n; ++i) {
2069         locations0[changed[i].identifier] = null;
2070       }
2071
2072       var touches = relocate(),
2073           now = Date.now();
2074
2075       if (touches.length === 1) {
2076         if (now - touchtime < 500) { // dbltap
2077           var p = touches[0];
2078           zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2079           d3_eventPreventDefault();
2080         }
2081         touchtime = now;
2082       } else if (touches.length > 1) {
2083         var p = touches[0], q = touches[1],
2084             dx = p[0] - q[0], dy = p[1] - q[1];
2085         distance0 = dx * dx + dy * dy;
2086       }
2087     }
2088
2089     function moved() {
2090       var touches = d3.touches(that),
2091           p0, l0,
2092           p1, l1;
2093
2094       d3_selection_interrupt.call(that);
2095
2096       for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2097         p1 = touches[i];
2098         if (l1 = locations0[p1.identifier]) {
2099           if (l0) break;
2100           p0 = p1, l0 = l1;
2101         }
2102       }
2103
2104       if (l1) {
2105         var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
2106             scale1 = distance0 && Math.sqrt(distance1 / distance0);
2107         p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
2108         l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
2109         scaleTo(scale1 * scale0);
2110       }
2111
2112       touchtime = null;
2113       translateTo(p0, l0);
2114       zoomed(dispatch);
2115     }
2116
2117     function ended() {
2118       // If there are any globally-active touches remaining, remove the ended
2119       // touches from locations0.
2120       if (d3.event.touches.length) {
2121         var changed = d3.event.changedTouches;
2122         for (var i = 0, n = changed.length; i < n; ++i) {
2123           delete locations0[changed[i].identifier];
2124         }
2125         // If locations0 is not empty, then relocate and continue listening for
2126         // touchmove and touchend.
2127         for (var identifier in locations0) {
2128           return void relocate(); // locations may have detached due to rotation
2129         }
2130       }
2131       // Otherwise, remove touchmove and touchend listeners.
2132       d3.selectAll(targets).on(zoomName, null);
2133       subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2134       dragRestore();
2135       zoomended(dispatch);
2136     }
2137   }
2138
2139   function mousewheeled() {
2140     var dispatch = event.of(this, arguments);
2141     if (mousewheelTimer) clearTimeout(mousewheelTimer);
2142     else d3_selection_interrupt.call(this), translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
2143     mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50);
2144     d3_eventPreventDefault();
2145     scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
2146     translateTo(center0, translate0);
2147     zoomed(dispatch);
2148   }
2149
2150   function dblclicked() {
2151     var p = d3.mouse(this),
2152         k = Math.log(view.k) / Math.LN2;
2153
2154     zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2155   }
2156
2157   return d3.rebind(zoom, event, "on");
2158 };
2159
2160 var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
2161     d3_behavior_zoomDelta, // initialized lazily
2162     d3_behavior_zoomWheel;
2163 function d3_functor(v) {
2164   return typeof v === "function" ? v : function() { return v; };
2165 }
2166
2167 d3.functor = d3_functor;
2168
2169 d3.touch = function(container, touches, identifier) {
2170   if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
2171   if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
2172     if ((touch = touches[i]).identifier === identifier) {
2173       return d3_mousePoint(container, touch);
2174     }
2175   }
2176 };
2177
2178 var d3_timer_queueHead,
2179     d3_timer_queueTail,
2180     d3_timer_interval, // is an interval (or frame) active?
2181     d3_timer_timeout, // is a timeout active?
2182     d3_timer_active, // active timer object
2183     d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };
2184
2185 // The timer will continue to fire until callback returns true.
2186 d3.timer = function(callback, delay, then) {
2187   var n = arguments.length;
2188   if (n < 2) delay = 0;
2189   if (n < 3) then = Date.now();
2190
2191   // Add the callback to the tail of the queue.
2192   var time = then + delay, timer = {c: callback, t: time, f: false, n: null};
2193   if (d3_timer_queueTail) d3_timer_queueTail.n = timer;
2194   else d3_timer_queueHead = timer;
2195   d3_timer_queueTail = timer;
2196
2197   // Start animatin'!
2198   if (!d3_timer_interval) {
2199     d3_timer_timeout = clearTimeout(d3_timer_timeout);
2200     d3_timer_interval = 1;
2201     d3_timer_frame(d3_timer_step);
2202   }
2203 };
2204
2205 function d3_timer_step() {
2206   var now = d3_timer_mark(),
2207       delay = d3_timer_sweep() - now;
2208   if (delay > 24) {
2209     if (isFinite(delay)) {
2210       clearTimeout(d3_timer_timeout);
2211       d3_timer_timeout = setTimeout(d3_timer_step, delay);
2212     }
2213     d3_timer_interval = 0;
2214   } else {
2215     d3_timer_interval = 1;
2216     d3_timer_frame(d3_timer_step);
2217   }
2218 }
2219
2220 d3.timer.flush = function() {
2221   d3_timer_mark();
2222   d3_timer_sweep();
2223 };
2224
2225 function d3_timer_mark() {
2226   var now = Date.now();
2227   d3_timer_active = d3_timer_queueHead;
2228   while (d3_timer_active) {
2229     if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
2230     d3_timer_active = d3_timer_active.n;
2231   }
2232   return now;
2233 }
2234
2235 // Flush after callbacks to avoid concurrent queue modification.
2236 // Returns the time of the earliest active timer, post-sweep.
2237 function d3_timer_sweep() {
2238   var t0,
2239       t1 = d3_timer_queueHead,
2240       time = Infinity;
2241   while (t1) {
2242     if (t1.f) {
2243       t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2244     } else {
2245       if (t1.t < time) time = t1.t;
2246       t1 = (t0 = t1).n;
2247     }
2248   }
2249   d3_timer_queueTail = t0;
2250   return time;
2251 }
2252 d3.geo = {};
2253
2254 d3.geo.stream = function(object, listener) {
2255   if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
2256     d3_geo_streamObjectType[object.type](object, listener);
2257   } else {
2258     d3_geo_streamGeometry(object, listener);
2259   }
2260 };
2261
2262 function d3_geo_streamGeometry(geometry, listener) {
2263   if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
2264     d3_geo_streamGeometryType[geometry.type](geometry, listener);
2265   }
2266 }
2267
2268 var d3_geo_streamObjectType = {
2269   Feature: function(feature, listener) {
2270     d3_geo_streamGeometry(feature.geometry, listener);
2271   },
2272   FeatureCollection: function(object, listener) {
2273     var features = object.features, i = -1, n = features.length;
2274     while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
2275   }
2276 };
2277
2278 var d3_geo_streamGeometryType = {
2279   Sphere: function(object, listener) {
2280     listener.sphere();
2281   },
2282   Point: function(object, listener) {
2283     object = object.coordinates;
2284     listener.point(object[0], object[1], object[2]);
2285   },
2286   MultiPoint: function(object, listener) {
2287     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2288     while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
2289   },
2290   LineString: function(object, listener) {
2291     d3_geo_streamLine(object.coordinates, listener, 0);
2292   },
2293   MultiLineString: function(object, listener) {
2294     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2295     while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
2296   },
2297   Polygon: function(object, listener) {
2298     d3_geo_streamPolygon(object.coordinates, listener);
2299   },
2300   MultiPolygon: function(object, listener) {
2301     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2302     while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
2303   },
2304   GeometryCollection: function(object, listener) {
2305     var geometries = object.geometries, i = -1, n = geometries.length;
2306     while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
2307   }
2308 };
2309
2310 function d3_geo_streamLine(coordinates, listener, closed) {
2311   var i = -1, n = coordinates.length - closed, coordinate;
2312   listener.lineStart();
2313   while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
2314   listener.lineEnd();
2315 }
2316
2317 function d3_geo_streamPolygon(coordinates, listener) {
2318   var i = -1, n = coordinates.length;
2319   listener.polygonStart();
2320   while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
2321   listener.polygonEnd();
2322 }
2323
2324 d3.geo.length = function(object) {
2325   d3_geo_lengthSum = 0;
2326   d3.geo.stream(object, d3_geo_length);
2327   return d3_geo_lengthSum;
2328 };
2329
2330 var d3_geo_lengthSum;
2331
2332 var d3_geo_length = {
2333   sphere: d3_noop,
2334   point: d3_noop,
2335   lineStart: d3_geo_lengthLineStart,
2336   lineEnd: d3_noop,
2337   polygonStart: d3_noop,
2338   polygonEnd: d3_noop
2339 };
2340
2341 function d3_geo_lengthLineStart() {
2342   var λ0, sinφ0, cosφ0;
2343
2344   d3_geo_length.point = function(λ, φ) {
2345     λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
2346     d3_geo_length.point = nextPoint;
2347   };
2348
2349   d3_geo_length.lineEnd = function() {
2350     d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
2351   };
2352
2353   function nextPoint(λ, φ) {
2354     var sinφ = Math.sin(φ *= d3_radians),
2355         cosφ = Math.cos(φ),
2356         t = abs((λ *= d3_radians) - λ0),
2357         cosΔλ = Math.cos(t);
2358     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Δλ);
2359     λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
2360   }
2361 }
2362 function d3_identity(d) {
2363   return d;
2364 }
2365 function d3_true() {
2366   return true;
2367 }
2368
2369 function d3_geo_spherical(cartesian) {
2370   return [
2371     Math.atan2(cartesian[1], cartesian[0]),
2372     d3_asin(cartesian[2])
2373   ];
2374 }
2375
2376 function d3_geo_sphericalEqual(a, b) {
2377   return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
2378 }
2379
2380 // General spherical polygon clipping algorithm: takes a polygon, cuts it into
2381 // visible line segments and rejoins the segments by interpolating along the
2382 // clip edge.
2383 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
2384   var subject = [],
2385       clip = [];
2386
2387   segments.forEach(function(segment) {
2388     if ((n = segment.length - 1) <= 0) return;
2389     var n, p0 = segment[0], p1 = segment[n];
2390
2391     // If the first and last points of a segment are coincident, then treat as
2392     // a closed ring.
2393     // TODO if all rings are closed, then the winding order of the exterior
2394     // ring should be checked.
2395     if (d3_geo_sphericalEqual(p0, p1)) {
2396       listener.lineStart();
2397       for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
2398       listener.lineEnd();
2399       return;
2400     }
2401
2402     var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
2403         b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
2404     a.o = b;
2405     subject.push(a);
2406     clip.push(b);
2407     a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
2408     b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
2409     a.o = b;
2410     subject.push(a);
2411     clip.push(b);
2412   });
2413   clip.sort(compare);
2414   d3_geo_clipPolygonLinkCircular(subject);
2415   d3_geo_clipPolygonLinkCircular(clip);
2416   if (!subject.length) return;
2417
2418   for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
2419     clip[i].e = entry = !entry;
2420   }
2421
2422   var start = subject[0],
2423       points,
2424       point;
2425   while (1) {
2426     // Find first unvisited intersection.
2427     var current = start,
2428         isSubject = true;
2429     while (current.v) if ((current = current.n) === start) return;
2430     points = current.z;
2431     listener.lineStart();
2432     do {
2433       current.v = current.o.v = true;
2434       if (current.e) {
2435         if (isSubject) {
2436           for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
2437         } else {
2438           interpolate(current.x, current.n.x, 1, listener);
2439         }
2440         current = current.n;
2441       } else {
2442         if (isSubject) {
2443           points = current.p.z;
2444           for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
2445         } else {
2446           interpolate(current.x, current.p.x, -1, listener);
2447         }
2448         current = current.p;
2449       }
2450       current = current.o;
2451       points = current.z;
2452       isSubject = !isSubject;
2453     } while (!current.v);
2454     listener.lineEnd();
2455   }
2456 }
2457
2458 function d3_geo_clipPolygonLinkCircular(array) {
2459   if (!(n = array.length)) return;
2460   var n,
2461       i = 0,
2462       a = array[0],
2463       b;
2464   while (++i < n) {
2465     a.n = b = array[i];
2466     b.p = a;
2467     a = b;
2468   }
2469   a.n = b = array[0];
2470   b.p = a;
2471 }
2472
2473 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
2474   this.x = point;
2475   this.z = points;
2476   this.o = other; // another intersection
2477   this.e = entry; // is an entry?
2478   this.v = false; // visited
2479   this.n = this.p = null; // next & previous
2480 }
2481
2482 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
2483   return function(rotate, listener) {
2484     var line = clipLine(listener),
2485         rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
2486
2487     var clip = {
2488       point: point,
2489       lineStart: lineStart,
2490       lineEnd: lineEnd,
2491       polygonStart: function() {
2492         clip.point = pointRing;
2493         clip.lineStart = ringStart;
2494         clip.lineEnd = ringEnd;
2495         segments = [];
2496         polygon = [];
2497       },
2498       polygonEnd: function() {
2499         clip.point = point;
2500         clip.lineStart = lineStart;
2501         clip.lineEnd = lineEnd;
2502
2503         segments = d3.merge(segments);
2504         var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
2505         if (segments.length) {
2506           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2507           d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
2508         } else if (clipStartInside) {
2509           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2510           listener.lineStart();
2511           interpolate(null, null, 1, listener);
2512           listener.lineEnd();
2513         }
2514         if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
2515         segments = polygon = null;
2516       },
2517       sphere: function() {
2518         listener.polygonStart();
2519         listener.lineStart();
2520         interpolate(null, null, 1, listener);
2521         listener.lineEnd();
2522         listener.polygonEnd();
2523       }
2524     };
2525
2526     function point(λ, φ) {
2527       var point = rotate(λ, φ);
2528       if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
2529     }
2530     function pointLine(λ, φ) {
2531       var point = rotate(λ, φ);
2532       line.point(point[0], point[1]);
2533     }
2534     function lineStart() { clip.point = pointLine; line.lineStart(); }
2535     function lineEnd() { clip.point = point; line.lineEnd(); }
2536
2537     var segments;
2538
2539     var buffer = d3_geo_clipBufferListener(),
2540         ringListener = clipLine(buffer),
2541         polygonStarted = false,
2542         polygon,
2543         ring;
2544
2545     function pointRing(λ, φ) {
2546       ring.push([λ, φ]);
2547       var point = rotate(λ, φ);
2548       ringListener.point(point[0], point[1]);
2549     }
2550
2551     function ringStart() {
2552       ringListener.lineStart();
2553       ring = [];
2554     }
2555
2556     function ringEnd() {
2557       pointRing(ring[0][0], ring[0][1]);
2558       ringListener.lineEnd();
2559
2560       var clean = ringListener.clean(),
2561           ringSegments = buffer.buffer(),
2562           segment,
2563           n = ringSegments.length;
2564
2565       ring.pop();
2566       polygon.push(ring);
2567       ring = null;
2568
2569       if (!n) return;
2570
2571       // No intersections.
2572       if (clean & 1) {
2573         segment = ringSegments[0];
2574         var n = segment.length - 1,
2575             i = -1,
2576             point;
2577         if (n > 0) {
2578           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2579           listener.lineStart();
2580           while (++i < n) listener.point((point = segment[i])[0], point[1]);
2581           listener.lineEnd();
2582         }
2583         return;
2584       }
2585
2586       // Rejoin connected segments.
2587       // TODO reuse bufferListener.rejoin()?
2588       if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
2589
2590       segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
2591     }
2592
2593     return clip;
2594   };
2595 }
2596
2597 function d3_geo_clipSegmentLength1(segment) {
2598   return segment.length > 1;
2599 }
2600
2601 function d3_geo_clipBufferListener() {
2602   var lines = [],
2603       line;
2604   return {
2605     lineStart: function() { lines.push(line = []); },
2606     point: function(λ, φ) { line.push([λ, φ]); },
2607     lineEnd: d3_noop,
2608     buffer: function() {
2609       var buffer = lines;
2610       lines = [];
2611       line = null;
2612       return buffer;
2613     },
2614     rejoin: function() {
2615       if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
2616     }
2617   };
2618 }
2619
2620 // Intersection points are sorted along the clip edge. For both antimeridian
2621 // cutting and circle clipping, the same comparison is used.
2622 function d3_geo_clipSort(a, b) {
2623   return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1])
2624        - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
2625 }
2626
2627 var d3_geo_clipAntimeridian = d3_geo_clip(
2628     d3_true,
2629     d3_geo_clipAntimeridianLine,
2630     d3_geo_clipAntimeridianInterpolate,
2631     [-π, -π / 2]);
2632
2633 // Takes a line and cuts into visible segments. Return values:
2634 //   0: there were intersections or the line was empty.
2635 //   1: no intersections.
2636 //   2: there were intersections, and the first and last segments should be
2637 //      rejoined.
2638 function d3_geo_clipAntimeridianLine(listener) {
2639   var λ0 = NaN,
2640       φ0 = NaN,
2641       sλ0 = NaN,
2642       clean; // no intersections
2643
2644   return {
2645     lineStart: function() {
2646       listener.lineStart();
2647       clean = 1;
2648     },
2649     point: function(λ1, φ1) {
2650       var sλ1 = λ1 > 0 ? π : -π,
2651           dλ = abs(λ1 - λ0);
2652       if (abs(dλ - π) < ε) { // line crosses a pole
2653         listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
2654         listener.point(sλ0, φ0);
2655         listener.lineEnd();
2656         listener.lineStart();
2657         listener.point(sλ1, φ0);
2658         listener.point(λ1, φ0);
2659         clean = 0;
2660       } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian
2661         // handle degeneracies
2662         if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
2663         if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
2664         φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
2665         listener.point(sλ0, φ0);
2666         listener.lineEnd();
2667         listener.lineStart();
2668         listener.point(sλ1, φ0);
2669         clean = 0;
2670       }
2671       listener.point(λ0 = λ1, φ0 = φ1);
2672       sλ0 = sλ1;
2673     },
2674     lineEnd: function() {
2675       listener.lineEnd();
2676       λ0 = φ0 = NaN;
2677     },
2678     // if there are intersections, we always rejoin the first and last segments.
2679     clean: function() { return 2 - clean; }
2680   };
2681 }
2682
2683 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
2684   var cosφ0,
2685       cosφ1,
2686       sinλ0_λ1 = Math.sin(λ0 - λ1);
2687   return abs(sinλ0_λ1) > ε
2688       ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1)
2689                  - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0))
2690                  / (cosφ0 * cosφ1 * sinλ0_λ1))
2691       : (φ0 + φ1) / 2;
2692 }
2693
2694 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
2695   var φ;
2696   if (from == null) {
2697     φ = direction * halfπ;
2698     listener.point(-π,  φ);
2699     listener.point( 0,  φ);
2700     listener.point( π,  φ);
2701     listener.point( π,  0);
2702     listener.point( π, -φ);
2703     listener.point( 0, -φ);
2704     listener.point(-π, -φ);
2705     listener.point(-π,  0);
2706     listener.point(-π,  φ);
2707   } else if (abs(from[0] - to[0]) > ε) {
2708     var s = from[0] < to[0] ? π : -π;
2709     φ = direction * s / 2;
2710     listener.point(-s, φ);
2711     listener.point( 0, φ);
2712     listener.point( s, φ);
2713   } else {
2714     listener.point(to[0], to[1]);
2715   }
2716 }
2717 // TODO
2718 // cross and scale return new vectors,
2719 // whereas add and normalize operate in-place
2720
2721 function d3_geo_cartesian(spherical) {
2722   var λ = spherical[0],
2723       φ = spherical[1],
2724       cosφ = Math.cos(φ);
2725   return [
2726     cosφ * Math.cos(λ),
2727     cosφ * Math.sin(λ),
2728     Math.sin(φ)
2729   ];
2730 }
2731
2732 function d3_geo_cartesianDot(a, b) {
2733   return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2734 }
2735
2736 function d3_geo_cartesianCross(a, b) {
2737   return [
2738     a[1] * b[2] - a[2] * b[1],
2739     a[2] * b[0] - a[0] * b[2],
2740     a[0] * b[1] - a[1] * b[0]
2741   ];
2742 }
2743
2744 function d3_geo_cartesianAdd(a, b) {
2745   a[0] += b[0];
2746   a[1] += b[1];
2747   a[2] += b[2];
2748 }
2749
2750 function d3_geo_cartesianScale(vector, k) {
2751   return [
2752     vector[0] * k,
2753     vector[1] * k,
2754     vector[2] * k
2755   ];
2756 }
2757
2758 function d3_geo_cartesianNormalize(d) {
2759   var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
2760   d[0] /= l;
2761   d[1] /= l;
2762   d[2] /= l;
2763 }
2764 function d3_geo_compose(a, b) {
2765
2766   function compose(x, y) {
2767     return x = a(x, y), b(x[0], x[1]);
2768   }
2769
2770   if (a.invert && b.invert) compose.invert = function(x, y) {
2771     return x = b.invert(x, y), x && a.invert(x[0], x[1]);
2772   };
2773
2774   return compose;
2775 }
2776
2777 function d3_geo_equirectangular(λ, φ) {
2778   return [λ, φ];
2779 }
2780
2781 (d3.geo.equirectangular = function() {
2782   return d3_geo_projection(d3_geo_equirectangular);
2783 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
2784
2785 d3.geo.rotation = function(rotate) {
2786   rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
2787
2788   function forward(coordinates) {
2789     coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2790     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2791   }
2792
2793   forward.invert = function(coordinates) {
2794     coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2795     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2796   };
2797
2798   return forward;
2799 };
2800
2801 function d3_geo_identityRotation(λ, φ) {
2802   return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2803 }
2804
2805 d3_geo_identityRotation.invert = d3_geo_equirectangular;
2806
2807 // Note: |δλ| must be < 2π
2808 function d3_geo_rotation(δλ, δφ, δγ) {
2809   return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ))
2810     : d3_geo_rotationλ(δλ))
2811     : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ)
2812     : d3_geo_identityRotation);
2813 }
2814
2815 function d3_geo_forwardRotationλ(δλ) {
2816   return function(λ, φ) {
2817     return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2818   };
2819 }
2820
2821 function d3_geo_rotationλ(δλ) {
2822   var rotation = d3_geo_forwardRotationλ(δλ);
2823   rotation.invert = d3_geo_forwardRotationλ(-δλ);
2824   return rotation;
2825 }
2826
2827 function d3_geo_rotationφγ(δφ, δγ) {
2828   var cosδφ = Math.cos(δφ),
2829       sinδφ = Math.sin(δφ),
2830       cosδγ = Math.cos(δγ),
2831       sinδγ = Math.sin(δγ);
2832
2833   function rotation(λ, φ) {
2834     var cosφ = Math.cos(φ),
2835         x = Math.cos(λ) * cosφ,
2836         y = Math.sin(λ) * cosφ,
2837         z = Math.sin(φ),
2838         k = z * cosδφ + x * sinδφ;
2839     return [
2840       Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
2841       d3_asin(k * cosδγ + y * sinδγ)
2842     ];
2843   }
2844
2845   rotation.invert = function(λ, φ) {
2846     var cosφ = Math.cos(φ),
2847         x = Math.cos(λ) * cosφ,
2848         y = Math.sin(λ) * cosφ,
2849         z = Math.sin(φ),
2850         k = z * cosδγ - y * sinδγ;
2851     return [
2852       Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
2853       d3_asin(k * cosδφ - x * sinδφ)
2854     ];
2855   };
2856
2857   return rotation;
2858 }
2859
2860 d3.geo.circle = function() {
2861   var origin = [0, 0],
2862       angle,
2863       precision = 6,
2864       interpolate;
2865
2866   function circle() {
2867     var center = typeof origin === "function" ? origin.apply(this, arguments) : origin,
2868         rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert,
2869         ring = [];
2870
2871     interpolate(null, null, 1, {
2872       point: function(x, y) {
2873         ring.push(x = rotate(x, y));
2874         x[0] *= d3_degrees, x[1] *= d3_degrees;
2875       }
2876     });
2877
2878     return {type: "Polygon", coordinates: [ring]};
2879   }
2880
2881   circle.origin = function(x) {
2882     if (!arguments.length) return origin;
2883     origin = x;
2884     return circle;
2885   };
2886
2887   circle.angle = function(x) {
2888     if (!arguments.length) return angle;
2889     interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
2890     return circle;
2891   };
2892
2893   circle.precision = function(_) {
2894     if (!arguments.length) return precision;
2895     interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
2896     return circle;
2897   };
2898
2899   return circle.angle(90);
2900 };
2901
2902 // Interpolates along a circle centered at [0°, 0°], with a given radius and
2903 // precision.
2904 function d3_geo_circleInterpolate(radius, precision) {
2905   var cr = Math.cos(radius),
2906       sr = Math.sin(radius);
2907   return function(from, to, direction, listener) {
2908     var step = direction * precision;
2909     if (from != null) {
2910       from = d3_geo_circleAngle(cr, from);
2911       to = d3_geo_circleAngle(cr, to);
2912       if (direction > 0 ? from < to: from > to) from += direction * τ;
2913     } else {
2914       from = radius + direction * τ;
2915       to = radius - .5 * step;
2916     }
2917     for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
2918       listener.point((point = d3_geo_spherical([
2919         cr,
2920         -sr * Math.cos(t),
2921         -sr * Math.sin(t)
2922       ]))[0], point[1]);
2923     }
2924   };
2925 }
2926
2927 // Signed angle of a cartesian point relative to [cr, 0, 0].
2928 function d3_geo_circleAngle(cr, point) {
2929   var a = d3_geo_cartesian(point);
2930   a[0] -= cr;
2931   d3_geo_cartesianNormalize(a);
2932   var angle = d3_acos(-a[1]);
2933   return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
2934 }
2935 // Adds floating point numbers with twice the normal precision.
2936 // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
2937 // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
2938 // 305–363 (1997).
2939 // Code adapted from GeographicLib by Charles F. F. Karney,
2940 // http://geographiclib.sourceforge.net/
2941 // See lib/geographiclib/LICENSE for details.
2942
2943 function d3_adder() {}
2944
2945 d3_adder.prototype = {
2946   s: 0, // rounded value
2947   t: 0, // exact error
2948   add: function(y) {
2949     d3_adderSum(y, this.t, d3_adderTemp);
2950     d3_adderSum(d3_adderTemp.s, this.s, this);
2951     if (this.s) this.t += d3_adderTemp.t;
2952     else this.s = d3_adderTemp.t;
2953   },
2954   reset: function() {
2955     this.s = this.t = 0;
2956   },
2957   valueOf: function() {
2958     return this.s;
2959   }
2960 };
2961
2962 var d3_adderTemp = new d3_adder;
2963
2964 function d3_adderSum(a, b, o) {
2965   var x = o.s = a + b, // a + b
2966       bv = x - a, av = x - bv; // b_virtual & a_virtual
2967   o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff
2968 }
2969
2970 d3.geo.area = function(object) {
2971   d3_geo_areaSum = 0;
2972   d3.geo.stream(object, d3_geo_area);
2973   return d3_geo_areaSum;
2974 };
2975
2976 var d3_geo_areaSum,
2977     d3_geo_areaRingSum = new d3_adder;
2978
2979 var d3_geo_area = {
2980   sphere: function() { d3_geo_areaSum += 4 * π; },
2981   point: d3_noop,
2982   lineStart: d3_noop,
2983   lineEnd: d3_noop,
2984
2985   // Only count area for polygon rings.
2986   polygonStart: function() {
2987     d3_geo_areaRingSum.reset();
2988     d3_geo_area.lineStart = d3_geo_areaRingStart;
2989   },
2990   polygonEnd: function() {
2991     var area = 2 * d3_geo_areaRingSum;
2992     d3_geo_areaSum += area < 0 ? 4 * π + area : area;
2993     d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
2994   }
2995 };
2996
2997 function d3_geo_areaRingStart() {
2998   var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
2999
3000   // For the first point, …
3001   d3_geo_area.point = function(λ, φ) {
3002     d3_geo_area.point = nextPoint;
3003     λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ);
3004   };
3005
3006   // For subsequent points, …
3007   function nextPoint(λ, φ) {
3008     λ *= d3_radians;
3009     φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole
3010
3011     // Spherical excess E for a spherical triangle with vertices: south pole,
3012     // previous point, current point.  Uses a formula derived from Cagnoli’s
3013     // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
3014     var dλ = λ - λ0,
3015         sdλ = dλ >= 0 ? 1 : -1,
3016         adλ = sdλ * dλ,
3017         cosφ = Math.cos(φ),
3018         sinφ = Math.sin(φ),
3019         k = sinφ0 * sinφ,
3020         u = cosφ0 * cosφ + k * Math.cos(adλ),
3021         v = k * sdλ * Math.sin(adλ);
3022     d3_geo_areaRingSum.add(Math.atan2(v, u));
3023
3024     // Advance the previous points.
3025     λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3026   }
3027
3028   // For the last point, return to the start.
3029   d3_geo_area.lineEnd = function() {
3030     nextPoint(λ00, φ00);
3031   };
3032 }
3033
3034 function d3_geo_pointInPolygon(point, polygon) {
3035   var meridian = point[0],
3036       parallel = point[1],
3037       meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
3038       polarAngle = 0,
3039       winding = 0;
3040   d3_geo_areaRingSum.reset();
3041
3042   for (var i = 0, n = polygon.length; i < n; ++i) {
3043     var ring = polygon[i],
3044         m = ring.length;
3045     if (!m) continue;
3046     var point0 = ring[0],
3047         λ0 = point0[0],
3048         φ0 = point0[1] / 2 + π / 4,
3049         sinφ0 = Math.sin(φ0),
3050         cosφ0 = Math.cos(φ0),
3051         j = 1;
3052
3053     while (true) {
3054       if (j === m) j = 0;
3055       point = ring[j];
3056       var λ = point[0],
3057           φ = point[1] / 2 + π / 4,
3058           sinφ = Math.sin(φ),
3059           cosφ = Math.cos(φ),
3060           dλ = λ - λ0,
3061           sdλ = dλ >= 0 ? 1 : -1,
3062           adλ = sdλ * dλ,
3063           antimeridian = adλ > π,
3064           k = sinφ0 * sinφ;
3065       d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
3066
3067       polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
3068
3069       // Are the longitudes either side of the point's meridian, and are the
3070       // latitudes smaller than the parallel?
3071       if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
3072         var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
3073         d3_geo_cartesianNormalize(arc);
3074         var intersection = d3_geo_cartesianCross(meridianNormal, arc);
3075         d3_geo_cartesianNormalize(intersection);
3076         var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
3077         if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
3078           winding += antimeridian ^ dλ >= 0 ? 1 : -1;
3079         }
3080       }
3081       if (!j++) break;
3082       λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
3083     }
3084   }
3085
3086   // First, determine whether the South pole is inside or outside:
3087   //
3088   // It is inside if:
3089   // * the polygon winds around it in a clockwise direction.
3090   // * the polygon does not (cumulatively) wind around it, but has a negative
3091   //   (counter-clockwise) area.
3092   //
3093   // Second, count the (signed) number of times a segment crosses a meridian
3094   // from the point to the South pole.  If it is zero, then the point is the
3095   // same side as the South pole.
3096
3097   return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
3098 }
3099
3100 // Clip features against a small circle centered at [0°, 0°].
3101 function d3_geo_clipCircle(radius) {
3102   var cr = Math.cos(radius),
3103       smallRadius = cr > 0,
3104       notHemisphere = abs(cr) > ε, // TODO optimise for this common case
3105       interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
3106
3107   return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]);
3108
3109   function visible(λ, φ) {
3110     return Math.cos(λ) * Math.cos(φ) > cr;
3111   }
3112
3113   // Takes a line and cuts into visible segments. Return values used for
3114   // polygon clipping:
3115   //   0: there were intersections or the line was empty.
3116   //   1: no intersections.
3117   //   2: there were intersections, and the first and last segments should be
3118   //      rejoined.
3119   function clipLine(listener) {
3120     var point0, // previous point
3121         c0, // code for previous point
3122         v0, // visibility of previous point
3123         v00, // visibility of first point
3124         clean; // no intersections
3125     return {
3126       lineStart: function() {
3127         v00 = v0 = false;
3128         clean = 1;
3129       },
3130       point: function(λ, φ) {
3131         var point1 = [λ, φ],
3132             point2,
3133             v = visible(λ, φ),
3134             c = smallRadius
3135               ? v ? 0 : code(λ, φ)
3136               : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
3137         if (!point0 && (v00 = v0 = v)) listener.lineStart();
3138         // Handle degeneracies.
3139         // TODO ignore if not clipping polygons.
3140         if (v !== v0) {
3141           point2 = intersect(point0, point1);
3142           if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3143             point1[0] += ε;
3144             point1[1] += ε;
3145             v = visible(point1[0], point1[1]);
3146           }
3147         }
3148         if (v !== v0) {
3149           clean = 0;
3150           if (v) {
3151             // outside going in
3152             listener.lineStart();
3153             point2 = intersect(point1, point0);
3154             listener.point(point2[0], point2[1]);
3155           } else {
3156             // inside going out
3157             point2 = intersect(point0, point1);
3158             listener.point(point2[0], point2[1]);
3159             listener.lineEnd();
3160           }
3161           point0 = point2;
3162         } else if (notHemisphere && point0 && smallRadius ^ v) {
3163           var t;
3164           // If the codes for two points are different, or are both zero,
3165           // and there this segment intersects with the small circle.
3166           if (!(c & c0) && (t = intersect(point1, point0, true))) {
3167             clean = 0;
3168             if (smallRadius) {
3169               listener.lineStart();
3170               listener.point(t[0][0], t[0][1]);
3171               listener.point(t[1][0], t[1][1]);
3172               listener.lineEnd();
3173             } else {
3174               listener.point(t[1][0], t[1][1]);
3175               listener.lineEnd();
3176               listener.lineStart();
3177               listener.point(t[0][0], t[0][1]);
3178             }
3179           }
3180         }
3181         if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3182           listener.point(point1[0], point1[1]);
3183         }
3184         point0 = point1, v0 = v, c0 = c;
3185       },
3186       lineEnd: function() {
3187         if (v0) listener.lineEnd();
3188         point0 = null;
3189       },
3190       // Rejoin first and last segments if there were intersections and the first
3191       // and last points were visible.
3192       clean: function() { return clean | ((v00 && v0) << 1); }
3193     };
3194   }
3195
3196   // Intersects the great circle between a and b with the clip circle.
3197   function intersect(a, b, two) {
3198     var pa = d3_geo_cartesian(a),
3199         pb = d3_geo_cartesian(b);
3200
3201     // We have two planes, n1.p = d1 and n2.p = d2.
3202     // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
3203     var n1 = [1, 0, 0], // normal
3204         n2 = d3_geo_cartesianCross(pa, pb),
3205         n2n2 = d3_geo_cartesianDot(n2, n2),
3206         n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2),
3207         determinant = n2n2 - n1n2 * n1n2;
3208
3209     // Two polar points.
3210     if (!determinant) return !two && a;
3211
3212     var c1 =  cr * n2n2 / determinant,
3213         c2 = -cr * n1n2 / determinant,
3214         n1xn2 = d3_geo_cartesianCross(n1, n2),
3215         A = d3_geo_cartesianScale(n1, c1),
3216         B = d3_geo_cartesianScale(n2, c2);
3217     d3_geo_cartesianAdd(A, B);
3218
3219     // Solve |p(t)|^2 = 1.
3220     var u = n1xn2,
3221         w = d3_geo_cartesianDot(A, u),
3222         uu = d3_geo_cartesianDot(u, u),
3223         t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
3224
3225     if (t2 < 0) return;
3226
3227     var t = Math.sqrt(t2),
3228         q = d3_geo_cartesianScale(u, (-w - t) / uu);
3229     d3_geo_cartesianAdd(q, A);
3230     q = d3_geo_spherical(q);
3231     if (!two) return q;
3232
3233     // Two intersection points.
3234     var λ0 = a[0],
3235         λ1 = b[0],
3236         φ0 = a[1],
3237         φ1 = b[1],
3238         z;
3239     if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3240     var δλ = λ1 - λ0,
3241         polar = abs(δλ - π) < ε,
3242         meridian = polar || δλ < ε;
3243
3244     if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3245
3246     // Check that the first point is between a and b.
3247     if (meridian
3248         ? polar
3249           ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1)
3250           : φ0 <= q[1] && q[1] <= φ1
3251         : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
3252       var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
3253       d3_geo_cartesianAdd(q1, A);
3254       return [q, d3_geo_spherical(q1)];
3255     }
3256   }
3257
3258   // Generates a 4-bit vector representing the location of a point relative to
3259   // the small circle's bounding box.
3260   function code(λ, φ) {
3261     var r = smallRadius ? radius : π - radius,
3262         code = 0;
3263     if (λ < -r) code |= 1; // left
3264     else if (λ > r) code |= 2; // right
3265     if (φ < -r) code |= 4; // below
3266     else if (φ > r) code |= 8; // above
3267     return code;
3268   }
3269 }
3270
3271 // Liang–Barsky line clipping.
3272 function d3_geom_clipLine(x0, y0, x1, y1) {
3273   return function(line) {
3274     var a = line.a,
3275         b = line.b,
3276         ax = a.x,
3277         ay = a.y,
3278         bx = b.x,
3279         by = b.y,
3280         t0 = 0,
3281         t1 = 1,
3282         dx = bx - ax,
3283         dy = by - ay,
3284         r;
3285
3286     r = x0 - ax;
3287     if (!dx && r > 0) return;
3288     r /= dx;
3289     if (dx < 0) {
3290       if (r < t0) return;
3291       if (r < t1) t1 = r;
3292     } else if (dx > 0) {
3293       if (r > t1) return;
3294       if (r > t0) t0 = r;
3295     }
3296
3297     r = x1 - ax;
3298     if (!dx && r < 0) return;
3299     r /= dx;
3300     if (dx < 0) {
3301       if (r > t1) return;
3302       if (r > t0) t0 = r;
3303     } else if (dx > 0) {
3304       if (r < t0) return;
3305       if (r < t1) t1 = r;
3306     }
3307
3308     r = y0 - ay;
3309     if (!dy && r > 0) return;
3310     r /= dy;
3311     if (dy < 0) {
3312       if (r < t0) return;
3313       if (r < t1) t1 = r;
3314     } else if (dy > 0) {
3315       if (r > t1) return;
3316       if (r > t0) t0 = r;
3317     }
3318
3319     r = y1 - ay;
3320     if (!dy && r < 0) return;
3321     r /= dy;
3322     if (dy < 0) {
3323       if (r > t1) return;
3324       if (r > t0) t0 = r;
3325     } else if (dy > 0) {
3326       if (r < t0) return;
3327       if (r < t1) t1 = r;
3328     }
3329
3330     if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy};
3331     if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy};
3332     return line;
3333   };
3334 }
3335
3336 var d3_geo_clipExtentMAX = 1e9;
3337
3338 d3.geo.clipExtent = function() {
3339   var x0, y0, x1, y1,
3340       stream,
3341       clip,
3342       clipExtent = {
3343         stream: function(output) {
3344           if (stream) stream.valid = false;
3345           stream = clip(output);
3346           stream.valid = true; // allow caching by d3.geo.path
3347           return stream;
3348         },
3349         extent: function(_) {
3350           if (!arguments.length) return [[x0, y0], [x1, y1]];
3351           clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
3352           if (stream) stream.valid = false, stream = null;
3353           return clipExtent;
3354         }
3355       };
3356   return clipExtent.extent([[0, 0], [960, 500]]);
3357 };
3358
3359 function d3_geo_clipExtent(x0, y0, x1, y1) {
3360   return function(listener) {
3361     var listener_ = listener,
3362         bufferListener = d3_geo_clipBufferListener(),
3363         clipLine = d3_geom_clipLine(x0, y0, x1, y1),
3364         segments,
3365         polygon,
3366         ring;
3367
3368     var clip = {
3369       point: point,
3370       lineStart: lineStart,
3371       lineEnd: lineEnd,
3372       polygonStart: function() {
3373         listener = bufferListener;
3374         segments = [];
3375         polygon = [];
3376         clean = true;
3377       },
3378       polygonEnd: function() {
3379         listener = listener_;
3380         segments = d3.merge(segments);
3381         var clipStartInside = insidePolygon([x0, y1]),
3382             inside = clean && clipStartInside,
3383             visible = segments.length;
3384         if (inside || visible) {
3385           listener.polygonStart();
3386           if (inside) {
3387             listener.lineStart();
3388             interpolate(null, null, 1, listener);
3389             listener.lineEnd();
3390           }
3391           if (visible) {
3392             d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
3393           }
3394           listener.polygonEnd();
3395         }
3396         segments = polygon = ring = null;
3397       }
3398     };
3399
3400     function insidePolygon(p) {
3401       var wn = 0, // the winding number counter
3402           n = polygon.length,
3403           y = p[1];
3404
3405       for (var i = 0; i < n; ++i) {
3406         for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
3407           b = v[j];
3408           if (a[1] <= y) {
3409             if (b[1] >  y && d3_cross2d(a, b, p) > 0) ++wn;
3410           } else {
3411             if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
3412           }
3413           a = b;
3414         }
3415       }
3416       return wn !== 0;
3417     }
3418
3419     function interpolate(from, to, direction, listener) {
3420       var a = 0, a1 = 0;
3421       if (from == null ||
3422           (a = corner(from, direction)) !== (a1 = corner(to, direction)) ||
3423           comparePoints(from, to) < 0 ^ direction > 0) {
3424         do {
3425           listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
3426         } while ((a = (a + direction + 4) % 4) !== a1);
3427       } else {
3428         listener.point(to[0], to[1]);
3429       }
3430     }
3431
3432     function pointVisible(x, y) {
3433       return x0 <= x && x <= x1 && y0 <= y && y <= y1;
3434     }
3435
3436     function point(x, y) {
3437       if (pointVisible(x, y)) listener.point(x, y);
3438     }
3439
3440     var x__, y__, v__, // first point
3441         x_, y_, v_, // previous point
3442         first,
3443         clean;
3444
3445     function lineStart() {
3446       clip.point = linePoint;
3447       if (polygon) polygon.push(ring = []);
3448       first = true;
3449       v_ = false;
3450       x_ = y_ = NaN;
3451     }
3452
3453     function lineEnd() {
3454       // TODO rather than special-case polygons, simply handle them separately.
3455       // Ideally, coincident intersection points should be jittered to avoid
3456       // clipping issues.
3457       if (segments) {
3458         linePoint(x__, y__);
3459         if (v__ && v_) bufferListener.rejoin();
3460         segments.push(bufferListener.buffer());
3461       }
3462       clip.point = point;
3463       if (v_) listener.lineEnd();
3464     }
3465
3466     function linePoint(x, y) {
3467       x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
3468       y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
3469       var v = pointVisible(x, y);
3470       if (polygon) ring.push([x, y]);
3471       if (first) {
3472         x__ = x, y__ = y, v__ = v;
3473         first = false;
3474         if (v) {
3475           listener.lineStart();
3476           listener.point(x, y);
3477         }
3478       } else {
3479         if (v && v_) listener.point(x, y);
3480         else {
3481           var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
3482           if (clipLine(l)) {
3483             if (!v_) {
3484               listener.lineStart();
3485               listener.point(l.a.x, l.a.y);
3486             }
3487             listener.point(l.b.x, l.b.y);
3488             if (!v) listener.lineEnd();
3489             clean = false;
3490           } else if (v) {
3491             listener.lineStart();
3492             listener.point(x, y);
3493             clean = false;
3494           }
3495         }
3496       }
3497       x_ = x, y_ = y, v_ = v;
3498     }
3499
3500     return clip;
3501   };
3502
3503   function corner(p, direction) {
3504     return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
3505         : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
3506         : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
3507         : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε
3508   }
3509
3510   function compare(a, b) {
3511     return comparePoints(a.x, b.x);
3512   }
3513
3514   function comparePoints(a, b) {
3515     var ca = corner(a, 1),
3516         cb = corner(b, 1);
3517     return ca !== cb ? ca - cb
3518         : ca === 0 ? b[1] - a[1]
3519         : ca === 1 ? a[0] - b[0]
3520         : ca === 2 ? a[1] - b[1]
3521         : b[0] - a[0];
3522   }
3523 }
3524
3525 function d3_geo_conic(projectAt) {
3526   var φ0 = 0,
3527       φ1 = π / 3,
3528       m = d3_geo_projectionMutator(projectAt),
3529       p = m(φ0, φ1);
3530
3531   p.parallels = function(_) {
3532     if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180];
3533     return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
3534   };
3535
3536   return p;
3537 }
3538
3539 function d3_geo_conicEqualArea(φ0, φ1) {
3540   var sinφ0 = Math.sin(φ0),
3541       n = (sinφ0 + Math.sin(φ1)) / 2,
3542       C = 1 + sinφ0 * (2 * n - sinφ0),
3543       ρ0 = Math.sqrt(C) / n;
3544
3545   function forward(λ, φ) {
3546     var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
3547     return [
3548       ρ * Math.sin(λ *= n),
3549       ρ0 - ρ * Math.cos(λ)
3550     ];
3551   }
3552
3553   forward.invert = function(x, y) {
3554     var ρ0_y = ρ0 - y;
3555     return [
3556       Math.atan2(x, ρ0_y) / n,
3557       d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n))
3558     ];
3559   };
3560
3561   return forward;
3562 }
3563
3564 (d3.geo.conicEqualArea = function() {
3565   return d3_geo_conic(d3_geo_conicEqualArea);
3566 }).raw = d3_geo_conicEqualArea;
3567
3568 // ESRI:102003
3569 d3.geo.albers = function() {
3570   return d3.geo.conicEqualArea()
3571       .rotate([96, 0])
3572       .center([-.6, 38.7])
3573       .parallels([29.5, 45.5])
3574       .scale(1070);
3575 };
3576
3577 // A composite projection for the United States, configured by default for
3578 // 960×500. Also works quite well at 960×600 with scale 1285. The set of
3579 // standard parallels for each region comes from USGS, which is published here:
3580 // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
3581 d3.geo.albersUsa = function() {
3582   var lower48 = d3.geo.albers();
3583
3584   // EPSG:3338
3585   var alaska = d3.geo.conicEqualArea()
3586       .rotate([154, 0])
3587       .center([-2, 58.5])
3588       .parallels([55, 65]);
3589
3590   // ESRI:102007
3591   var hawaii = d3.geo.conicEqualArea()
3592       .rotate([157, 0])
3593       .center([-3, 19.9])
3594       .parallels([8, 18]);
3595
3596   var point,
3597       pointStream = {point: function(x, y) { point = [x, y]; }},
3598       lower48Point,
3599       alaskaPoint,
3600       hawaiiPoint;
3601
3602   function albersUsa(coordinates) {
3603     var x = coordinates[0], y = coordinates[1];
3604     point = null;
3605     (lower48Point(x, y), point)
3606         || (alaskaPoint(x, y), point)
3607         || hawaiiPoint(x, y);
3608     return point;
3609   }
3610
3611   albersUsa.invert = function(coordinates) {
3612     var k = lower48.scale(),
3613         t = lower48.translate(),
3614         x = (coordinates[0] - t[0]) / k,
3615         y = (coordinates[1] - t[1]) / k;
3616     return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska
3617         : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii
3618         : lower48).invert(coordinates);
3619   };
3620
3621   // A naïve multi-projection stream.
3622   // The projections must have mutually exclusive clip regions on the sphere,
3623   // as this will avoid emitting interleaving lines and polygons.
3624   albersUsa.stream = function(stream) {
3625     var lower48Stream = lower48.stream(stream),
3626         alaskaStream = alaska.stream(stream),
3627         hawaiiStream = hawaii.stream(stream);
3628     return {
3629       point: function(x, y) {
3630         lower48Stream.point(x, y);
3631         alaskaStream.point(x, y);
3632         hawaiiStream.point(x, y);
3633       },
3634       sphere: function() {
3635         lower48Stream.sphere();
3636         alaskaStream.sphere();
3637         hawaiiStream.sphere();
3638       },
3639       lineStart: function() {
3640         lower48Stream.lineStart();
3641         alaskaStream.lineStart();
3642         hawaiiStream.lineStart();
3643       },
3644       lineEnd: function() {
3645         lower48Stream.lineEnd();
3646         alaskaStream.lineEnd();
3647         hawaiiStream.lineEnd();
3648       },
3649       polygonStart: function() {
3650         lower48Stream.polygonStart();
3651         alaskaStream.polygonStart();
3652         hawaiiStream.polygonStart();
3653       },
3654       polygonEnd: function() {
3655         lower48Stream.polygonEnd();
3656         alaskaStream.polygonEnd();
3657         hawaiiStream.polygonEnd();
3658       }
3659     };
3660   };
3661
3662   albersUsa.precision = function(_) {
3663     if (!arguments.length) return lower48.precision();
3664     lower48.precision(_);
3665     alaska.precision(_);
3666     hawaii.precision(_);
3667     return albersUsa;
3668   };
3669
3670   albersUsa.scale = function(_) {
3671     if (!arguments.length) return lower48.scale();
3672     lower48.scale(_);
3673     alaska.scale(_ * .35);
3674     hawaii.scale(_);
3675     return albersUsa.translate(lower48.translate());
3676   };
3677
3678   albersUsa.translate = function(_) {
3679     if (!arguments.length) return lower48.translate();
3680     var k = lower48.scale(), x = +_[0], y = +_[1];
3681
3682     lower48Point = lower48
3683         .translate(_)
3684         .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
3685         .stream(pointStream).point;
3686
3687     alaskaPoint = alaska
3688         .translate([x - .307 * k, y + .201 * k])
3689         .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]])
3690         .stream(pointStream).point;
3691
3692     hawaiiPoint = hawaii
3693         .translate([x - .205 * k, y + .212 * k])
3694         .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]])
3695         .stream(pointStream).point;
3696
3697     return albersUsa;
3698   };
3699
3700   return albersUsa.scale(1070);
3701 };
3702
3703 d3.geo.bounds = (function() {
3704   var λ0, φ0, λ1, φ1, // bounds
3705       λ_, // previous λ-coordinate
3706       λ__, φ__, // first point
3707       p0, // previous 3D point
3708       dλSum,
3709       ranges,
3710       range;
3711
3712   var bound = {
3713     point: point,
3714     lineStart: lineStart,
3715     lineEnd: lineEnd,
3716
3717     polygonStart: function() {
3718       bound.point = ringPoint;
3719       bound.lineStart = ringStart;
3720       bound.lineEnd = ringEnd;
3721       dλSum = 0;
3722       d3_geo_area.polygonStart();
3723     },
3724     polygonEnd: function() {
3725       d3_geo_area.polygonEnd();
3726       bound.point = point;
3727       bound.lineStart = lineStart;
3728       bound.lineEnd = lineEnd;
3729       if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90);
3730       else if (dλSum > ε) φ1 = 90;
3731       else if (dλSum < -ε) φ0 = -90;
3732       range[0] = λ0, range[1] = λ1;
3733     }
3734   };
3735
3736   function point(λ, φ) {
3737     ranges.push(range = [λ0 = λ, λ1 = λ]);
3738     if (φ < φ0) φ0 = φ;
3739     if (φ > φ1) φ1 = φ;
3740   }
3741
3742   function linePoint(λ, φ) {
3743     var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]);
3744     if (p0) {
3745       var normal = d3_geo_cartesianCross(p0, p),
3746           equatorial = [normal[1], -normal[0], 0],
3747           inflection = d3_geo_cartesianCross(equatorial, normal);
3748       d3_geo_cartesianNormalize(inflection);
3749       inflection = d3_geo_spherical(inflection);
3750       var dλ = λ - λ_,
3751           s = dλ > 0 ? 1 : -1,
3752           λi = inflection[0] * d3_degrees * s,
3753           antimeridian = abs(dλ) > 180;
3754       if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3755         var φi = inflection[1] * d3_degrees;
3756         if (φi > φ1) φ1 = φi;
3757       } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3758         var φi = -inflection[1] * d3_degrees;
3759         if (φi < φ0) φ0 = φi;
3760       } else {
3761         if (φ < φ0) φ0 = φ;
3762         if (φ > φ1) φ1 = φ;
3763       }
3764       if (antimeridian) {
3765         if (λ < λ_) {
3766           if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3767         } else {
3768           if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3769         }
3770       } else {
3771         if (λ1 >= λ0) {
3772           if (λ < λ0) λ0 = λ;
3773           if (λ > λ1) λ1 = λ;
3774         } else {
3775           if (λ > λ_) {
3776             if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3777           } else {
3778             if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3779           }
3780         }
3781       }
3782     } else {
3783       point(λ, φ);
3784     }
3785     p0 = p, λ_ = λ;
3786   }
3787
3788   function lineStart() { bound.point = linePoint; }
3789   function lineEnd() {
3790     range[0] = λ0, range[1] = λ1;
3791     bound.point = point;
3792     p0 = null;
3793   }
3794
3795   function ringPoint(λ, φ) {
3796     if (p0) {
3797       var dλ = λ - λ_;
3798       dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3799     } else λ__ = λ, φ__ = φ;
3800     d3_geo_area.point(λ, φ);
3801     linePoint(λ, φ);
3802   }
3803
3804   function ringStart() {
3805     d3_geo_area.lineStart();
3806   }
3807
3808   function ringEnd() {
3809     ringPoint(λ__, φ__);
3810     d3_geo_area.lineEnd();
3811     if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
3812     range[0] = λ0, range[1] = λ1;
3813     p0 = null;
3814   }
3815
3816   // Finds the left-right distance between two longitudes.
3817   // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want
3818   // the distance between ±180° to be 360°.
3819   function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; }
3820
3821   function compareRanges(a, b) { return a[0] - b[0]; }
3822
3823   function withinRange(x, range) {
3824     return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3825   }
3826
3827   return function(feature) {
3828     φ1 = λ1 = -(λ0 = φ0 = Infinity);
3829     ranges = [];
3830
3831     d3.geo.stream(feature, bound);
3832
3833     var n = ranges.length;
3834     if (n) {
3835       // First, sort ranges by their minimum longitudes.
3836       ranges.sort(compareRanges);
3837
3838       // Then, merge any ranges that overlap.
3839       for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
3840         b = ranges[i];
3841         if (withinRange(b[0], a) || withinRange(b[1], a)) {
3842           if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
3843           if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
3844         } else {
3845           merged.push(a = b);
3846         }
3847       }
3848
3849       // Finally, find the largest gap between the merged ranges.
3850       // The final bounding box will be the inverse of this gap.
3851       var best = -Infinity, dλ;
3852       for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
3853         b = merged[i];
3854         if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3855       }
3856     }
3857     ranges = range = null;
3858
3859     return λ0 === Infinity || φ0 === Infinity
3860         ? [[NaN, NaN], [NaN, NaN]]
3861         : [[λ0, φ0], [λ1, φ1]];
3862   };
3863 })();
3864
3865 d3.geo.centroid = function(object) {
3866   d3_geo_centroidW0 = d3_geo_centroidW1 =
3867   d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
3868   d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
3869   d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
3870   d3.geo.stream(object, d3_geo_centroid);
3871
3872   var x = d3_geo_centroidX2,
3873       y = d3_geo_centroidY2,
3874       z = d3_geo_centroidZ2,
3875       m = x * x + y * y + z * z;
3876
3877   // If the area-weighted centroid is undefined, fall back to length-weighted centroid.
3878   if (m < ε2) {
3879     x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
3880     // If the feature has zero length, fall back to arithmetic mean of point vectors.
3881     if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
3882     m = x * x + y * y + z * z;
3883     // If the feature still has an undefined centroid, then return.
3884     if (m < ε2) return [NaN, NaN];
3885   }
3886
3887   return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
3888 };
3889
3890 var d3_geo_centroidW0,
3891     d3_geo_centroidW1,
3892     d3_geo_centroidX0,
3893     d3_geo_centroidY0,
3894     d3_geo_centroidZ0,
3895     d3_geo_centroidX1,
3896     d3_geo_centroidY1,
3897     d3_geo_centroidZ1,
3898     d3_geo_centroidX2,
3899     d3_geo_centroidY2,
3900     d3_geo_centroidZ2;
3901
3902 var d3_geo_centroid = {
3903   sphere: d3_noop,
3904   point: d3_geo_centroidPoint,
3905   lineStart: d3_geo_centroidLineStart,
3906   lineEnd: d3_geo_centroidLineEnd,
3907   polygonStart: function() {
3908     d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
3909   },
3910   polygonEnd: function() {
3911     d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3912   }
3913 };
3914
3915 // Arithmetic mean of Cartesian vectors.
3916 function d3_geo_centroidPoint(λ, φ) {
3917   λ *= d3_radians;
3918   var cosφ = Math.cos(φ *= d3_radians);
3919   d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3920 }
3921
3922 function d3_geo_centroidPointXYZ(x, y, z) {
3923   ++d3_geo_centroidW0;
3924   d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
3925   d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
3926   d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
3927 }
3928
3929 function d3_geo_centroidLineStart() {
3930   var x0, y0, z0; // previous point
3931
3932   d3_geo_centroid.point = function(λ, φ) {
3933     λ *= d3_radians;
3934     var cosφ = Math.cos(φ *= d3_radians);
3935     x0 = cosφ * Math.cos(λ);
3936     y0 = cosφ * Math.sin(λ);
3937     z0 = Math.sin(φ);
3938     d3_geo_centroid.point = nextPoint;
3939     d3_geo_centroidPointXYZ(x0, y0, z0);
3940   };
3941
3942   function nextPoint(λ, φ) {
3943     λ *= d3_radians;
3944     var cosφ = Math.cos(φ *= d3_radians),
3945         x = cosφ * Math.cos(λ),
3946         y = cosφ * Math.sin(λ),
3947         z = Math.sin(φ),
3948         w = Math.atan2(
3949           Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
3950           x0 * x + y0 * y + z0 * z);
3951     d3_geo_centroidW1 += w;
3952     d3_geo_centroidX1 += w * (x0 + (x0 = x));
3953     d3_geo_centroidY1 += w * (y0 + (y0 = y));
3954     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
3955     d3_geo_centroidPointXYZ(x0, y0, z0);
3956   }
3957 }
3958
3959 function d3_geo_centroidLineEnd() {
3960   d3_geo_centroid.point = d3_geo_centroidPoint;
3961 }
3962
3963 // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
3964 // J. Applied Mechanics 42, 239 (1975).
3965 function d3_geo_centroidRingStart() {
3966   var λ00, φ00, // first point
3967       x0, y0, z0; // previous point
3968
3969   d3_geo_centroid.point = function(λ, φ) {
3970     λ00 = λ, φ00 = φ;
3971     d3_geo_centroid.point = nextPoint;
3972     λ *= d3_radians;
3973     var cosφ = Math.cos(φ *= d3_radians);
3974     x0 = cosφ * Math.cos(λ);
3975     y0 = cosφ * Math.sin(λ);
3976     z0 = Math.sin(φ);
3977     d3_geo_centroidPointXYZ(x0, y0, z0);
3978   };
3979
3980   d3_geo_centroid.lineEnd = function() {
3981     nextPoint(λ00, φ00);
3982     d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
3983     d3_geo_centroid.point = d3_geo_centroidPoint;
3984   };
3985
3986   function nextPoint(λ, φ) {
3987     λ *= d3_radians;
3988     var cosφ = Math.cos(φ *= d3_radians),
3989         x = cosφ * Math.cos(λ),
3990         y = cosφ * Math.sin(λ),
3991         z = Math.sin(φ),
3992         cx = y0 * z - z0 * y,
3993         cy = z0 * x - x0 * z,
3994         cz = x0 * y - y0 * x,
3995         m = Math.sqrt(cx * cx + cy * cy + cz * cz),
3996         u = x0 * x + y0 * y + z0 * z,
3997         v = m && -d3_acos(u) / m, // area weight
3998         w = Math.atan2(m, u); // line weight
3999     d3_geo_centroidX2 += v * cx;
4000     d3_geo_centroidY2 += v * cy;
4001     d3_geo_centroidZ2 += v * cz;
4002     d3_geo_centroidW1 += w;
4003     d3_geo_centroidX1 += w * (x0 + (x0 = x));
4004     d3_geo_centroidY1 += w * (y0 + (y0 = y));
4005     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
4006     d3_geo_centroidPointXYZ(x0, y0, z0);
4007   }
4008 }
4009
4010 // TODO Unify this code with d3.geom.polygon area?
4011
4012 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4013   point: d3_noop,
4014   lineStart: d3_noop,
4015   lineEnd: d3_noop,
4016
4017   // Only count area for polygon rings.
4018   polygonStart: function() {
4019     d3_geo_pathAreaPolygon = 0;
4020     d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4021   },
4022   polygonEnd: function() {
4023     d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
4024     d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
4025   }
4026 };
4027
4028 function d3_geo_pathAreaRingStart() {
4029   var x00, y00, x0, y0;
4030
4031   // For the first point, …
4032   d3_geo_pathArea.point = function(x, y) {
4033     d3_geo_pathArea.point = nextPoint;
4034     x00 = x0 = x, y00 = y0 = y;
4035   };
4036
4037   // For subsequent points, …
4038   function nextPoint(x, y) {
4039     d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4040     x0 = x, y0 = y;
4041   }
4042
4043   // For the last point, return to the start.
4044   d3_geo_pathArea.lineEnd = function() {
4045     nextPoint(x00, y00);
4046   };
4047 }
4048
4049 var d3_geo_pathBoundsX0,
4050     d3_geo_pathBoundsY0,
4051     d3_geo_pathBoundsX1,
4052     d3_geo_pathBoundsY1;
4053
4054 var d3_geo_pathBounds = {
4055   point: d3_geo_pathBoundsPoint,
4056   lineStart: d3_noop,
4057   lineEnd: d3_noop,
4058   polygonStart: d3_noop,
4059   polygonEnd: d3_noop
4060 };
4061
4062 function d3_geo_pathBoundsPoint(x, y) {
4063   if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
4064   if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
4065   if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
4066   if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
4067 }
4068 function d3_geo_pathBuffer() {
4069   var pointCircle = d3_geo_pathBufferCircle(4.5),
4070       buffer = [];
4071
4072   var stream = {
4073     point: point,
4074
4075     // While inside a line, override point to moveTo then lineTo.
4076     lineStart: function() { stream.point = pointLineStart; },
4077     lineEnd: lineEnd,
4078
4079     // While inside a polygon, override lineEnd to closePath.
4080     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4081     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4082
4083     pointRadius: function(_) {
4084       pointCircle = d3_geo_pathBufferCircle(_);
4085       return stream;
4086     },
4087
4088     result: function() {
4089       if (buffer.length) {
4090         var result = buffer.join("");
4091         buffer = [];
4092         return result;
4093       }
4094     }
4095   };
4096
4097   function point(x, y) {
4098     buffer.push("M", x, ",", y, pointCircle);
4099   }
4100
4101   function pointLineStart(x, y) {
4102     buffer.push("M", x, ",", y);
4103     stream.point = pointLine;
4104   }
4105
4106   function pointLine(x, y) {
4107     buffer.push("L", x, ",", y);
4108   }
4109
4110   function lineEnd() {
4111     stream.point = point;
4112   }
4113
4114   function lineEndPolygon() {
4115     buffer.push("Z");
4116   }
4117
4118   return stream;
4119 }
4120
4121 function d3_geo_pathBufferCircle(radius) {
4122   return "m0," + radius
4123       + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
4124       + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
4125       + "z";
4126 }
4127
4128 // TODO Unify this code with d3.geom.polygon centroid?
4129 // TODO Enforce positive area for exterior, negative area for interior?
4130
4131 var d3_geo_pathCentroid = {
4132   point: d3_geo_pathCentroidPoint,
4133
4134   // For lines, weight by length.
4135   lineStart: d3_geo_pathCentroidLineStart,
4136   lineEnd: d3_geo_pathCentroidLineEnd,
4137
4138   // For polygons, weight by area.
4139   polygonStart: function() {
4140     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4141   },
4142   polygonEnd: function() {
4143     d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4144     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
4145     d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
4146   }
4147 };
4148
4149 function d3_geo_pathCentroidPoint(x, y) {
4150   d3_geo_centroidX0 += x;
4151   d3_geo_centroidY0 += y;
4152   ++d3_geo_centroidZ0;
4153 }
4154
4155 function d3_geo_pathCentroidLineStart() {
4156   var x0, y0;
4157
4158   d3_geo_pathCentroid.point = function(x, y) {
4159     d3_geo_pathCentroid.point = nextPoint;
4160     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4161   };
4162
4163   function nextPoint(x, y) {
4164     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4165     d3_geo_centroidX1 += z * (x0 + x) / 2;
4166     d3_geo_centroidY1 += z * (y0 + y) / 2;
4167     d3_geo_centroidZ1 += z;
4168     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4169   }
4170 }
4171
4172 function d3_geo_pathCentroidLineEnd() {
4173   d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4174 }
4175
4176 function d3_geo_pathCentroidRingStart() {
4177   var x00, y00, x0, y0;
4178
4179   // For the first point, …
4180   d3_geo_pathCentroid.point = function(x, y) {
4181     d3_geo_pathCentroid.point = nextPoint;
4182     d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
4183   };
4184
4185   // For subsequent points, …
4186   function nextPoint(x, y) {
4187     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4188     d3_geo_centroidX1 += z * (x0 + x) / 2;
4189     d3_geo_centroidY1 += z * (y0 + y) / 2;
4190     d3_geo_centroidZ1 += z;
4191
4192     z = y0 * x - x0 * y;
4193     d3_geo_centroidX2 += z * (x0 + x);
4194     d3_geo_centroidY2 += z * (y0 + y);
4195     d3_geo_centroidZ2 += z * 3;
4196     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4197   }
4198
4199   // For the last point, return to the start.
4200   d3_geo_pathCentroid.lineEnd = function() {
4201     nextPoint(x00, y00);
4202   };
4203 }
4204
4205 function d3_geo_pathContext(context) {
4206   var pointRadius = 4.5;
4207
4208   var stream = {
4209     point: point,
4210
4211     // While inside a line, override point to moveTo then lineTo.
4212     lineStart: function() { stream.point = pointLineStart; },
4213     lineEnd: lineEnd,
4214
4215     // While inside a polygon, override lineEnd to closePath.
4216     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4217     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4218
4219     pointRadius: function(_) {
4220       pointRadius = _;
4221       return stream;
4222     },
4223
4224     result: d3_noop
4225   };
4226
4227   function point(x, y) {
4228     context.moveTo(x + pointRadius, y);
4229     context.arc(x, y, pointRadius, 0, τ);
4230   }
4231
4232   function pointLineStart(x, y) {
4233     context.moveTo(x, y);
4234     stream.point = pointLine;
4235   }
4236
4237   function pointLine(x, y) {
4238     context.lineTo(x, y);
4239   }
4240
4241   function lineEnd() {
4242     stream.point = point;
4243   }
4244
4245   function lineEndPolygon() {
4246     context.closePath();
4247   }
4248
4249   return stream;
4250 }
4251
4252 function d3_geo_resample(project) {
4253   var δ2 = .5, // precision, px²
4254       cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance)
4255       maxDepth = 16;
4256
4257   function resample(stream) {
4258     return (maxDepth ? resampleRecursive : resampleNone)(stream);
4259   }
4260
4261   function resampleNone(stream) {
4262     return d3_geo_transformPoint(stream, function(x, y) {
4263       x = project(x, y);
4264       stream.point(x[0], x[1]);
4265     });
4266   }
4267
4268   function resampleRecursive(stream) {
4269     var λ00, φ00, x00, y00, a00, b00, c00, // first point
4270         λ0, x0, y0, a0, b0, c0; // previous point
4271
4272     var resample = {
4273       point: point,
4274       lineStart: lineStart,
4275       lineEnd: lineEnd,
4276       polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
4277       polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
4278     };
4279
4280     function point(x, y) {
4281       x = project(x, y);
4282       stream.point(x[0], x[1]);
4283     }
4284
4285     function lineStart() {
4286       x0 = NaN;
4287       resample.point = linePoint;
4288       stream.lineStart();
4289     }
4290
4291     function linePoint(λ, φ) {
4292       var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ);
4293       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);
4294       stream.point(x0, y0);
4295     }
4296
4297     function lineEnd() {
4298       resample.point = point;
4299       stream.lineEnd();
4300     }
4301
4302     function ringStart() {
4303       lineStart();
4304       resample.point = ringPoint;
4305       resample.lineEnd = ringEnd;
4306     }
4307
4308     function ringPoint(λ, φ) {
4309       linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4310       resample.point = linePoint;
4311     }
4312
4313     function ringEnd() {
4314       resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4315       resample.lineEnd = lineEnd;
4316       lineEnd();
4317     }
4318
4319     return resample;
4320   }
4321
4322   function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4323     var dx = x1 - x0,
4324         dy = y1 - y0,
4325         d2 = dx * dx + dy * dy;
4326     if (d2 > 4 * δ2 && depth--) {
4327       var a = a0 + a1,
4328           b = b0 + b1,
4329           c = c0 + c1,
4330           m = Math.sqrt(a * a + b * b + c * c),
4331           φ2 = Math.asin(c /= m),
4332           λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
4333           p = project(λ2, φ2),
4334           x2 = p[0],
4335           y2 = p[1],
4336           dx2 = x2 - x0,
4337           dy2 = y2 - y0,
4338           dz = dy * dx2 - dx * dy2;
4339       if (dz * dz / d2 > δ2 // perpendicular projected distance
4340           || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
4341           || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
4342         resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
4343         stream.point(x2, y2);
4344         resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
4345       }
4346     }
4347   }
4348
4349   resample.precision = function(_) {
4350     if (!arguments.length) return Math.sqrt(δ2);
4351     maxDepth = (δ2 = _ * _) > 0 && 16;
4352     return resample;
4353   };
4354
4355   return resample;
4356 }
4357
4358 d3.geo.path = function() {
4359   var pointRadius = 4.5,
4360       projection,
4361       context,
4362       projectStream,
4363       contextStream,
4364       cacheStream;
4365
4366   function path(object) {
4367     if (object) {
4368       if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
4369       if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
4370       d3.geo.stream(object, cacheStream);
4371     }
4372     return contextStream.result();
4373   }
4374
4375   path.area = function(object) {
4376     d3_geo_pathAreaSum = 0;
4377     d3.geo.stream(object, projectStream(d3_geo_pathArea));
4378     return d3_geo_pathAreaSum;
4379   };
4380
4381   path.centroid = function(object) {
4382     d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
4383     d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
4384     d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
4385     d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
4386     return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2]
4387         : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1]
4388         : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0]
4389         : [NaN, NaN];
4390   };
4391
4392   path.bounds = function(object) {
4393     d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
4394     d3.geo.stream(object, projectStream(d3_geo_pathBounds));
4395     return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]];
4396   };
4397
4398   path.projection = function(_) {
4399     if (!arguments.length) return projection;
4400     projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
4401     return reset();
4402   };
4403
4404   path.context = function(_) {
4405     if (!arguments.length) return context;
4406     contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_);
4407     if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
4408     return reset();
4409   };
4410
4411   path.pointRadius = function(_) {
4412     if (!arguments.length) return pointRadius;
4413     pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
4414     return path;
4415   };
4416
4417   function reset() {
4418     cacheStream = null;
4419     return path;
4420   }
4421
4422   return path.projection(d3.geo.albersUsa()).context(null);
4423 };
4424
4425 function d3_geo_pathProjectStream(project) {
4426   var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); });
4427   return function(stream) { return d3_geo_projectionRadians(resample(stream)); };
4428 }
4429
4430 d3.geo.transform = function(methods) {
4431   return {
4432     stream: function(stream) {
4433       var transform = new d3_geo_transform(stream);
4434       for (var k in methods) transform[k] = methods[k];
4435       return transform;
4436     }
4437   };
4438 };
4439
4440 function d3_geo_transform(stream) {
4441   this.stream = stream;
4442 }
4443
4444 d3_geo_transform.prototype = {
4445   point: function(x, y) { this.stream.point(x, y); },
4446   sphere: function() { this.stream.sphere(); },
4447   lineStart: function() { this.stream.lineStart(); },
4448   lineEnd: function() { this.stream.lineEnd(); },
4449   polygonStart: function() { this.stream.polygonStart(); },
4450   polygonEnd: function() { this.stream.polygonEnd(); }
4451 };
4452
4453 function d3_geo_transformPoint(stream, point) {
4454   return {
4455     point: point,
4456     sphere: function() { stream.sphere(); },
4457     lineStart: function() { stream.lineStart(); },
4458     lineEnd: function() { stream.lineEnd(); },
4459     polygonStart: function() { stream.polygonStart(); },
4460     polygonEnd: function() { stream.polygonEnd(); },
4461   };
4462 }
4463
4464 d3.geo.projection = d3_geo_projection;
4465 d3.geo.projectionMutator = d3_geo_projectionMutator;
4466
4467 function d3_geo_projection(project) {
4468   return d3_geo_projectionMutator(function() { return project; })();
4469 }
4470
4471 function d3_geo_projectionMutator(projectAt) {
4472   var project,
4473       rotate,
4474       projectRotate,
4475       projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
4476       k = 150, // scale
4477       x = 480, y = 250, // translate
4478       λ = 0, φ = 0, // center
4479       δλ = 0, δφ = 0, δγ = 0, // rotate
4480       δx, δy, // center
4481       preclip = d3_geo_clipAntimeridian,
4482       postclip = d3_identity,
4483       clipAngle = null,
4484       clipExtent = null,
4485       stream;
4486
4487   function projection(point) {
4488     point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
4489     return [point[0] * k + δx, δy - point[1] * k];
4490   }
4491
4492   function invert(point) {
4493     point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
4494     return point && [point[0] * d3_degrees, point[1] * d3_degrees];
4495   }
4496
4497   projection.stream = function(output) {
4498     if (stream) stream.valid = false;
4499     stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
4500     stream.valid = true; // allow caching by d3.geo.path
4501     return stream;
4502   };
4503
4504   projection.clipAngle = function(_) {
4505     if (!arguments.length) return clipAngle;
4506     preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
4507     return invalidate();
4508   };
4509
4510   projection.clipExtent = function(_) {
4511     if (!arguments.length) return clipExtent;
4512     clipExtent = _;
4513     postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
4514     return invalidate();
4515   };
4516
4517   projection.scale = function(_) {
4518     if (!arguments.length) return k;
4519     k = +_;
4520     return reset();
4521   };
4522
4523   projection.translate = function(_) {
4524     if (!arguments.length) return [x, y];
4525     x = +_[0];
4526     y = +_[1];
4527     return reset();
4528   };
4529
4530   projection.center = function(_) {
4531     if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
4532     λ = _[0] % 360 * d3_radians;
4533     φ = _[1] % 360 * d3_radians;
4534     return reset();
4535   };
4536
4537   projection.rotate = function(_) {
4538     if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees];
4539     δλ = _[0] % 360 * d3_radians;
4540     δφ = _[1] % 360 * d3_radians;
4541     δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
4542     return reset();
4543   };
4544
4545   d3.rebind(projection, projectResample, "precision");
4546
4547   function reset() {
4548     projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
4549     var center = project(λ, φ);
4550     δx = x - center[0] * k;
4551     δy = y + center[1] * k;
4552     return invalidate();
4553   }
4554
4555   function invalidate() {
4556     if (stream) stream.valid = false, stream = null;
4557     return projection;
4558   }
4559
4560   return function() {
4561     project = projectAt.apply(this, arguments);
4562     projection.invert = project.invert && invert;
4563     return reset();
4564   };
4565 }
4566
4567 function d3_geo_projectionRadians(stream) {
4568   return d3_geo_transformPoint(stream, function(x, y) {
4569     stream.point(x * d3_radians, y * d3_radians);
4570   });
4571 }
4572
4573 function d3_geo_mercator(λ, φ) {
4574   return [λ, Math.log(Math.tan(π / 4 + φ / 2))];
4575 }
4576
4577 d3_geo_mercator.invert = function(x, y) {
4578   return [x, 2 * Math.atan(Math.exp(y)) - halfπ];
4579 };
4580
4581 function d3_geo_mercatorProjection(project) {
4582   var m = d3_geo_projection(project),
4583       scale = m.scale,
4584       translate = m.translate,
4585       clipExtent = m.clipExtent,
4586       clipAuto;
4587
4588   m.scale = function() {
4589     var v = scale.apply(m, arguments);
4590     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4591   };
4592
4593   m.translate = function() {
4594     var v = translate.apply(m, arguments);
4595     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4596   };
4597
4598   m.clipExtent = function(_) {
4599     var v = clipExtent.apply(m, arguments);
4600     if (v === m) {
4601       if (clipAuto = _ == null) {
4602         var k = π * scale(), t = translate();
4603         clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]);
4604       }
4605     } else if (clipAuto) {
4606       v = null;
4607     }
4608     return v;
4609   };
4610
4611   return m.clipExtent(null);
4612 }
4613
4614 (d3.geo.mercator = function() {
4615   return d3_geo_mercatorProjection(d3_geo_mercator);
4616 }).raw = d3_geo_mercator;
4617 d3.geom = {};
4618
4619 d3.geom.polygon = function(coordinates) {
4620   d3_subclass(coordinates, d3_geom_polygonPrototype);
4621   return coordinates;
4622 };
4623
4624 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
4625
4626 d3_geom_polygonPrototype.area = function() {
4627   var i = -1,
4628       n = this.length,
4629       a,
4630       b = this[n - 1],
4631       area = 0;
4632
4633   while (++i < n) {
4634     a = b;
4635     b = this[i];
4636     area += a[1] * b[0] - a[0] * b[1];
4637   }
4638
4639   return area * .5;
4640 };
4641
4642 d3_geom_polygonPrototype.centroid = function(k) {
4643   var i = -1,
4644       n = this.length,
4645       x = 0,
4646       y = 0,
4647       a,
4648       b = this[n - 1],
4649       c;
4650
4651   if (!arguments.length) k = -1 / (6 * this.area());
4652
4653   while (++i < n) {
4654     a = b;
4655     b = this[i];
4656     c = a[0] * b[1] - b[0] * a[1];
4657     x += (a[0] + b[0]) * c;
4658     y += (a[1] + b[1]) * c;
4659   }
4660
4661   return [x * k, y * k];
4662 };
4663
4664 // The Sutherland-Hodgman clipping algorithm.
4665 // Note: requires the clip polygon to be counterclockwise and convex.
4666 d3_geom_polygonPrototype.clip = function(subject) {
4667   var input,
4668       closed = d3_geom_polygonClosed(subject),
4669       i = -1,
4670       n = this.length - d3_geom_polygonClosed(this),
4671       j,
4672       m,
4673       a = this[n - 1],
4674       b,
4675       c,
4676       d;
4677
4678   while (++i < n) {
4679     input = subject.slice();
4680     subject.length = 0;
4681     b = this[i];
4682     c = input[(m = input.length - closed) - 1];
4683     j = -1;
4684     while (++j < m) {
4685       d = input[j];
4686       if (d3_geom_polygonInside(d, a, b)) {
4687         if (!d3_geom_polygonInside(c, a, b)) {
4688           subject.push(d3_geom_polygonIntersect(c, d, a, b));
4689         }
4690         subject.push(d);
4691       } else if (d3_geom_polygonInside(c, a, b)) {
4692         subject.push(d3_geom_polygonIntersect(c, d, a, b));
4693       }
4694       c = d;
4695     }
4696     if (closed) subject.push(subject[0]);
4697     a = b;
4698   }
4699
4700   return subject;
4701 };
4702
4703 function d3_geom_polygonInside(p, a, b) {
4704   return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
4705 }
4706
4707 // Intersect two infinite lines cd and ab.
4708 function d3_geom_polygonIntersect(c, d, a, b) {
4709   var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3,
4710       y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3,
4711       ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
4712   return [x1 + ua * x21, y1 + ua * y21];
4713 }
4714
4715 // Returns true if the polygon is closed.
4716 function d3_geom_polygonClosed(coordinates) {
4717   var a = coordinates[0],
4718       b = coordinates[coordinates.length - 1];
4719   return !(a[0] - b[0] || a[1] - b[1]);
4720 }
4721 function d3_geom_pointX(d) {
4722   return d[0];
4723 }
4724
4725 function d3_geom_pointY(d) {
4726   return d[1];
4727 }
4728
4729 /**
4730  * Computes the 2D convex hull of a set of points using the monotone chain
4731  * algorithm:
4732  * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain)
4733  *
4734  * The runtime of this algorithm is O(n log n), where n is the number of input
4735  * points. However in practice it outperforms other O(n log n) hulls.
4736  *
4737  * @param vertices [[x1, y1], [x2, y2], ...]
4738  * @returns polygon [[x1, y1], [x2, y2], ...]
4739  */
4740 d3.geom.hull = function(vertices) {
4741   var x = d3_geom_pointX,
4742       y = d3_geom_pointY;
4743
4744   if (arguments.length) return hull(vertices);
4745
4746   function hull(data) {
4747     // Hull of < 3 points is not well-defined
4748     if (data.length < 3) return [];
4749
4750     var fx = d3_functor(x),
4751         fy = d3_functor(y),
4752         i,
4753         n = data.length,
4754         points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]]
4755         flippedPoints = [];
4756
4757     for (i = 0 ; i < n; i++) {
4758       points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]);
4759     }
4760
4761     // sort ascending by x-coord first, y-coord second
4762     points.sort(d3_geom_hullOrder);
4763
4764     // we flip bottommost points across y axis so we can use the upper hull routine on both
4765     for (i = 0; i < n; i++) flippedPoints.push([points[i][0], -points[i][1]]);
4766
4767     var upper = d3_geom_hullUpper(points),
4768         lower = d3_geom_hullUpper(flippedPoints);
4769
4770     // construct the polygon, removing possible duplicate endpoints
4771     var skipLeft = lower[0] === upper[0],
4772         skipRight  = lower[lower.length - 1] === upper[upper.length - 1],
4773         polygon = [];
4774
4775     // add upper hull in r->l order
4776     // then add lower hull in l->r order
4777     for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
4778     for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
4779
4780     return polygon;
4781   }
4782
4783   hull.x = function(_) {
4784     return arguments.length ? (x = _, hull) : x;
4785   };
4786
4787   hull.y = function(_) {
4788     return arguments.length ? (y = _, hull) : y;
4789   };
4790
4791   return hull;
4792 };
4793
4794 // finds the 'upper convex hull' (see wiki link above)
4795 // assumes points arg has >=3 elements, is sorted by x, unique in y
4796 // returns array of indices into points in left to right order
4797 function d3_geom_hullUpper(points) {
4798   var n = points.length,
4799       hull = [0, 1],
4800       hs = 2; // hull size
4801
4802   for (var i = 2; i < n; i++) {
4803     while (hs > 1 && d3_cross2d(points[hull[hs-2]], points[hull[hs-1]], points[i]) <= 0) --hs;
4804     hull[hs++] = i;
4805   }
4806
4807   // we slice to make sure that the points we 'popped' from hull don't stay behind
4808   return hull.slice(0, hs);
4809 }
4810
4811 // comparator for ascending sort by x-coord first, y-coord second
4812 function d3_geom_hullOrder(a, b) {
4813   return a[0] - b[0] || a[1] - b[1];
4814 }
4815 // import "../transition/transition";
4816
4817 d3_selectionPrototype.transition = function(name) {
4818   var id = d3_transitionInheritId || ++d3_transitionId,
4819       ns = d3_transitionNamespace(name),
4820       subgroups = [],
4821       subgroup,
4822       node,
4823       transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250};
4824
4825   for (var j = -1, m = this.length; ++j < m;) {
4826     subgroups.push(subgroup = []);
4827     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4828       if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
4829       subgroup.push(node);
4830     }
4831   }
4832
4833   return d3_transition(subgroups, ns, id);
4834 };
4835 // import "../transition/transition";
4836
4837 // TODO Interrupt transitions for all namespaces?
4838 d3_selectionPrototype.interrupt = function(name) {
4839   return this.each(name == null
4840       ? d3_selection_interrupt
4841       : d3_selection_interruptNS(d3_transitionNamespace(name)));
4842 };
4843
4844 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
4845
4846 function d3_selection_interruptNS(ns) {
4847   return function() {
4848     var lock, active;
4849     if ((lock = this[ns]) && (active = lock[lock.active])) {
4850       if (--lock.count) delete lock[lock.active];
4851       else delete this[ns];
4852       lock.active += .5;
4853       active.event && active.event.interrupt.call(this, this.__data__, active.index);
4854     }
4855   };
4856 }
4857
4858 function d3_transition(groups, ns, id) {
4859   d3_subclass(groups, d3_transitionPrototype);
4860
4861   // Note: read-only!
4862   groups.namespace = ns;
4863   groups.id = id;
4864
4865   return groups;
4866 }
4867
4868 var d3_transitionPrototype = [],
4869     d3_transitionId = 0,
4870     d3_transitionInheritId,
4871     d3_transitionInherit;
4872
4873 d3_transitionPrototype.call = d3_selectionPrototype.call;
4874 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
4875 d3_transitionPrototype.node = d3_selectionPrototype.node;
4876 d3_transitionPrototype.size = d3_selectionPrototype.size;
4877
4878 d3.transition = function(selection, name) {
4879   return selection && selection.transition
4880       ? (d3_transitionInheritId ? selection.transition(name) : selection)
4881       : d3.selection().transition(selection);
4882 };
4883
4884 d3.transition.prototype = d3_transitionPrototype;
4885
4886
4887 d3_transitionPrototype.select = function(selector) {
4888   var id = this.id,
4889       ns = this.namespace,
4890       subgroups = [],
4891       subgroup,
4892       subnode,
4893       node;
4894
4895   selector = d3_selection_selector(selector);
4896
4897   for (var j = -1, m = this.length; ++j < m;) {
4898     subgroups.push(subgroup = []);
4899     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4900       if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
4901         if ("__data__" in node) subnode.__data__ = node.__data__;
4902         d3_transitionNode(subnode, i, ns, id, node[ns][id]);
4903         subgroup.push(subnode);
4904       } else {
4905         subgroup.push(null);
4906       }
4907     }
4908   }
4909
4910   return d3_transition(subgroups, ns, id);
4911 };
4912
4913 d3_transitionPrototype.selectAll = function(selector) {
4914   var id = this.id,
4915       ns = this.namespace,
4916       subgroups = [],
4917       subgroup,
4918       subnodes,
4919       node,
4920       subnode,
4921       transition;
4922
4923   selector = d3_selection_selectorAll(selector);
4924
4925   for (var j = -1, m = this.length; ++j < m;) {
4926     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4927       if (node = group[i]) {
4928         transition = node[ns][id];
4929         subnodes = selector.call(node, node.__data__, i, j);
4930         subgroups.push(subgroup = []);
4931         for (var k = -1, o = subnodes.length; ++k < o;) {
4932           if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
4933           subgroup.push(subnode);
4934         }
4935       }
4936     }
4937   }
4938
4939   return d3_transition(subgroups, ns, id);
4940 };
4941
4942 d3_transitionPrototype.filter = function(filter) {
4943   var subgroups = [],
4944       subgroup,
4945       group,
4946       node;
4947
4948   if (typeof filter !== "function") filter = d3_selection_filter(filter);
4949
4950   for (var j = 0, m = this.length; j < m; j++) {
4951     subgroups.push(subgroup = []);
4952     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
4953       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
4954         subgroup.push(node);
4955       }
4956     }
4957   }
4958
4959   return d3_transition(subgroups, this.namespace, this.id);
4960 };
4961 d3.color = d3_color;
4962
4963 function d3_color() {}
4964
4965 d3_color.prototype.toString = function() {
4966   return this.rgb() + "";
4967 };
4968
4969 d3.hsl = d3_hsl;
4970
4971 function d3_hsl(h, s, l) {
4972   return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l)
4973       : arguments.length < 2 ? (h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l)
4974       : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl))
4975       : new d3_hsl(h, s, l);
4976 }
4977
4978 var d3_hslPrototype = d3_hsl.prototype = new d3_color;
4979
4980 d3_hslPrototype.brighter = function(k) {
4981   k = Math.pow(0.7, arguments.length ? k : 1);
4982   return new d3_hsl(this.h, this.s, this.l / k);
4983 };
4984
4985 d3_hslPrototype.darker = function(k) {
4986   k = Math.pow(0.7, arguments.length ? k : 1);
4987   return new d3_hsl(this.h, this.s, k * this.l);
4988 };
4989
4990 d3_hslPrototype.rgb = function() {
4991   return d3_hsl_rgb(this.h, this.s, this.l);
4992 };
4993
4994 function d3_hsl_rgb(h, s, l) {
4995   var m1,
4996       m2;
4997
4998   /* Some simple corrections for h, s and l. */
4999   h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
5000   s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
5001   l = l < 0 ? 0 : l > 1 ? 1 : l;
5002
5003   /* From FvD 13.37, CSS Color Module Level 3 */
5004   m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
5005   m1 = 2 * l - m2;
5006
5007   function v(h) {
5008     if (h > 360) h -= 360;
5009     else if (h < 0) h += 360;
5010     if (h < 60) return m1 + (m2 - m1) * h / 60;
5011     if (h < 180) return m2;
5012     if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
5013     return m1;
5014   }
5015
5016   function vv(h) {
5017     return Math.round(v(h) * 255);
5018   }
5019
5020   return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
5021 }
5022
5023 d3.hcl = d3_hcl;
5024
5025 function d3_hcl(h, c, l) {
5026   return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l)
5027       : arguments.length < 2 ? (h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l)
5028       : (h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b)
5029       : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b)))
5030       : new d3_hcl(h, c, l);
5031 }
5032
5033 var d3_hclPrototype = d3_hcl.prototype = new d3_color;
5034
5035 d3_hclPrototype.brighter = function(k) {
5036   return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
5037 };
5038
5039 d3_hclPrototype.darker = function(k) {
5040   return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
5041 };
5042
5043 d3_hclPrototype.rgb = function() {
5044   return d3_hcl_lab(this.h, this.c, this.l).rgb();
5045 };
5046
5047 function d3_hcl_lab(h, c, l) {
5048   if (isNaN(h)) h = 0;
5049   if (isNaN(c)) c = 0;
5050   return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
5051 }
5052
5053 d3.lab = d3_lab;
5054
5055 function d3_lab(l, a, b) {
5056   return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b)
5057       : arguments.length < 2 ? (l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b)
5058       : (l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l)
5059       : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b)))
5060       : new d3_lab(l, a, b);
5061 }
5062
5063 // Corresponds roughly to RGB brighter/darker
5064 var d3_lab_K = 18;
5065
5066 // D65 standard referent
5067 var d3_lab_X = 0.950470,
5068     d3_lab_Y = 1,
5069     d3_lab_Z = 1.088830;
5070
5071 var d3_labPrototype = d3_lab.prototype = new d3_color;
5072
5073 d3_labPrototype.brighter = function(k) {
5074   return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5075 };
5076
5077 d3_labPrototype.darker = function(k) {
5078   return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5079 };
5080
5081 d3_labPrototype.rgb = function() {
5082   return d3_lab_rgb(this.l, this.a, this.b);
5083 };
5084
5085 function d3_lab_rgb(l, a, b) {
5086   var y = (l + 16) / 116,
5087       x = y + a / 500,
5088       z = y - b / 200;
5089   x = d3_lab_xyz(x) * d3_lab_X;
5090   y = d3_lab_xyz(y) * d3_lab_Y;
5091   z = d3_lab_xyz(z) * d3_lab_Z;
5092   return new d3_rgb(
5093     d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
5094     d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
5095     d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
5096   );
5097 }
5098
5099 function d3_lab_hcl(l, a, b) {
5100   return l > 0
5101       ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l)
5102       : new d3_hcl(NaN, NaN, l);
5103 }
5104
5105 function d3_lab_xyz(x) {
5106   return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
5107 }
5108 function d3_xyz_lab(x) {
5109   return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
5110 }
5111
5112 function d3_xyz_rgb(r) {
5113   return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
5114 }
5115
5116 d3.rgb = d3_rgb;
5117
5118 function d3_rgb(r, g, b) {
5119   return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b)
5120       : arguments.length < 2 ? (r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b)
5121       : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb))
5122       : new d3_rgb(r, g, b);
5123 }
5124
5125 function d3_rgbNumber(value) {
5126   return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff);
5127 }
5128
5129 function d3_rgbString(value) {
5130   return d3_rgbNumber(value) + "";
5131 }
5132
5133 var d3_rgbPrototype = d3_rgb.prototype = new d3_color;
5134
5135 d3_rgbPrototype.brighter = function(k) {
5136   k = Math.pow(0.7, arguments.length ? k : 1);
5137   var r = this.r,
5138       g = this.g,
5139       b = this.b,
5140       i = 30;
5141   if (!r && !g && !b) return new d3_rgb(i, i, i);
5142   if (r && r < i) r = i;
5143   if (g && g < i) g = i;
5144   if (b && b < i) b = i;
5145   return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
5146 };
5147
5148 d3_rgbPrototype.darker = function(k) {
5149   k = Math.pow(0.7, arguments.length ? k : 1);
5150   return new d3_rgb(k * this.r, k * this.g, k * this.b);
5151 };
5152
5153 d3_rgbPrototype.hsl = function() {
5154   return d3_rgb_hsl(this.r, this.g, this.b);
5155 };
5156
5157 d3_rgbPrototype.toString = function() {
5158   return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
5159 };
5160
5161 function d3_rgb_hex(v) {
5162   return v < 0x10
5163       ? "0" + Math.max(0, v).toString(16)
5164       : Math.min(255, v).toString(16);
5165 }
5166
5167 function d3_rgb_parse(format, rgb, hsl) {
5168   var r = 0, // red channel; int in [0, 255]
5169       g = 0, // green channel; int in [0, 255]
5170       b = 0, // blue channel; int in [0, 255]
5171       m1, // CSS color specification match
5172       m2, // CSS color specification type (e.g., rgb)
5173       color;
5174
5175   /* Handle hsl, rgb. */
5176   m1 = /([a-z]+)\((.*)\)/i.exec(format);
5177   if (m1) {
5178     m2 = m1[2].split(",");
5179     switch (m1[1]) {
5180       case "hsl": {
5181         return hsl(
5182           parseFloat(m2[0]), // degrees
5183           parseFloat(m2[1]) / 100, // percentage
5184           parseFloat(m2[2]) / 100 // percentage
5185         );
5186       }
5187       case "rgb": {
5188         return rgb(
5189           d3_rgb_parseNumber(m2[0]),
5190           d3_rgb_parseNumber(m2[1]),
5191           d3_rgb_parseNumber(m2[2])
5192         );
5193       }
5194     }
5195   }
5196
5197   /* Named colors. */
5198   if (color = d3_rgb_names.get(format.toLowerCase())) {
5199     return rgb(color.r, color.g, color.b);
5200   }
5201
5202   /* Hexadecimal colors: #rgb and #rrggbb. */
5203   if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
5204     if (format.length === 4) {
5205       r = (color & 0xf00) >> 4; r = (r >> 4) | r;
5206       g = (color & 0xf0); g = (g >> 4) | g;
5207       b = (color & 0xf); b = (b << 4) | b;
5208     } else if (format.length === 7) {
5209       r = (color & 0xff0000) >> 16;
5210       g = (color & 0xff00) >> 8;
5211       b = (color & 0xff);
5212     }
5213   }
5214
5215   return rgb(r, g, b);
5216 }
5217
5218 function d3_rgb_hsl(r, g, b) {
5219   var min = Math.min(r /= 255, g /= 255, b /= 255),
5220       max = Math.max(r, g, b),
5221       d = max - min,
5222       h,
5223       s,
5224       l = (max + min) / 2;
5225   if (d) {
5226     s = l < .5 ? d / (max + min) : d / (2 - max - min);
5227     if (r == max) h = (g - b) / d + (g < b ? 6 : 0);
5228     else if (g == max) h = (b - r) / d + 2;
5229     else h = (r - g) / d + 4;
5230     h *= 60;
5231   } else {
5232     h = NaN;
5233     s = l > 0 && l < 1 ? 0 : h;
5234   }
5235   return new d3_hsl(h, s, l);
5236 }
5237
5238 function d3_rgb_lab(r, g, b) {
5239   r = d3_rgb_xyz(r);
5240   g = d3_rgb_xyz(g);
5241   b = d3_rgb_xyz(b);
5242   var x = d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / d3_lab_X),
5243       y = d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / d3_lab_Y),
5244       z = d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / d3_lab_Z);
5245   return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
5246 }
5247
5248 function d3_rgb_xyz(r) {
5249   return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
5250 }
5251
5252 function d3_rgb_parseNumber(c) { // either integer or percentage
5253   var f = parseFloat(c);
5254   return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
5255 }
5256
5257 var d3_rgb_names = d3.map({
5258   aliceblue: 0xf0f8ff,
5259   antiquewhite: 0xfaebd7,
5260   aqua: 0x00ffff,
5261   aquamarine: 0x7fffd4,
5262   azure: 0xf0ffff,
5263   beige: 0xf5f5dc,
5264   bisque: 0xffe4c4,
5265   black: 0x000000,
5266   blanchedalmond: 0xffebcd,
5267   blue: 0x0000ff,
5268   blueviolet: 0x8a2be2,
5269   brown: 0xa52a2a,
5270   burlywood: 0xdeb887,
5271   cadetblue: 0x5f9ea0,
5272   chartreuse: 0x7fff00,
5273   chocolate: 0xd2691e,
5274   coral: 0xff7f50,
5275   cornflowerblue: 0x6495ed,
5276   cornsilk: 0xfff8dc,
5277   crimson: 0xdc143c,
5278   cyan: 0x00ffff,
5279   darkblue: 0x00008b,
5280   darkcyan: 0x008b8b,
5281   darkgoldenrod: 0xb8860b,
5282   darkgray: 0xa9a9a9,
5283   darkgreen: 0x006400,
5284   darkgrey: 0xa9a9a9,
5285   darkkhaki: 0xbdb76b,
5286   darkmagenta: 0x8b008b,
5287   darkolivegreen: 0x556b2f,
5288   darkorange: 0xff8c00,
5289   darkorchid: 0x9932cc,
5290   darkred: 0x8b0000,
5291   darksalmon: 0xe9967a,
5292   darkseagreen: 0x8fbc8f,
5293   darkslateblue: 0x483d8b,
5294   darkslategray: 0x2f4f4f,
5295   darkslategrey: 0x2f4f4f,
5296   darkturquoise: 0x00ced1,
5297   darkviolet: 0x9400d3,
5298   deeppink: 0xff1493,
5299   deepskyblue: 0x00bfff,
5300   dimgray: 0x696969,
5301   dimgrey: 0x696969,
5302   dodgerblue: 0x1e90ff,
5303   firebrick: 0xb22222,
5304   floralwhite: 0xfffaf0,
5305   forestgreen: 0x228b22,
5306   fuchsia: 0xff00ff,
5307   gainsboro: 0xdcdcdc,
5308   ghostwhite: 0xf8f8ff,
5309   gold: 0xffd700,
5310   goldenrod: 0xdaa520,
5311   gray: 0x808080,
5312   green: 0x008000,
5313   greenyellow: 0xadff2f,
5314   grey: 0x808080,
5315   honeydew: 0xf0fff0,
5316   hotpink: 0xff69b4,
5317   indianred: 0xcd5c5c,
5318   indigo: 0x4b0082,
5319   ivory: 0xfffff0,
5320   khaki: 0xf0e68c,
5321   lavender: 0xe6e6fa,
5322   lavenderblush: 0xfff0f5,
5323   lawngreen: 0x7cfc00,
5324   lemonchiffon: 0xfffacd,
5325   lightblue: 0xadd8e6,
5326   lightcoral: 0xf08080,
5327   lightcyan: 0xe0ffff,
5328   lightgoldenrodyellow: 0xfafad2,
5329   lightgray: 0xd3d3d3,
5330   lightgreen: 0x90ee90,
5331   lightgrey: 0xd3d3d3,
5332   lightpink: 0xffb6c1,
5333   lightsalmon: 0xffa07a,
5334   lightseagreen: 0x20b2aa,
5335   lightskyblue: 0x87cefa,
5336   lightslategray: 0x778899,
5337   lightslategrey: 0x778899,
5338   lightsteelblue: 0xb0c4de,
5339   lightyellow: 0xffffe0,
5340   lime: 0x00ff00,
5341   limegreen: 0x32cd32,
5342   linen: 0xfaf0e6,
5343   magenta: 0xff00ff,
5344   maroon: 0x800000,
5345   mediumaquamarine: 0x66cdaa,
5346   mediumblue: 0x0000cd,
5347   mediumorchid: 0xba55d3,
5348   mediumpurple: 0x9370db,
5349   mediumseagreen: 0x3cb371,
5350   mediumslateblue: 0x7b68ee,
5351   mediumspringgreen: 0x00fa9a,
5352   mediumturquoise: 0x48d1cc,
5353   mediumvioletred: 0xc71585,
5354   midnightblue: 0x191970,
5355   mintcream: 0xf5fffa,
5356   mistyrose: 0xffe4e1,
5357   moccasin: 0xffe4b5,
5358   navajowhite: 0xffdead,
5359   navy: 0x000080,
5360   oldlace: 0xfdf5e6,
5361   olive: 0x808000,
5362   olivedrab: 0x6b8e23,
5363   orange: 0xffa500,
5364   orangered: 0xff4500,
5365   orchid: 0xda70d6,
5366   palegoldenrod: 0xeee8aa,
5367   palegreen: 0x98fb98,
5368   paleturquoise: 0xafeeee,
5369   palevioletred: 0xdb7093,
5370   papayawhip: 0xffefd5,
5371   peachpuff: 0xffdab9,
5372   peru: 0xcd853f,
5373   pink: 0xffc0cb,
5374   plum: 0xdda0dd,
5375   powderblue: 0xb0e0e6,
5376   purple: 0x800080,
5377   rebeccapurple: 0x663399,
5378   red: 0xff0000,
5379   rosybrown: 0xbc8f8f,
5380   royalblue: 0x4169e1,
5381   saddlebrown: 0x8b4513,
5382   salmon: 0xfa8072,
5383   sandybrown: 0xf4a460,
5384   seagreen: 0x2e8b57,
5385   seashell: 0xfff5ee,
5386   sienna: 0xa0522d,
5387   silver: 0xc0c0c0,
5388   skyblue: 0x87ceeb,
5389   slateblue: 0x6a5acd,
5390   slategray: 0x708090,
5391   slategrey: 0x708090,
5392   snow: 0xfffafa,
5393   springgreen: 0x00ff7f,
5394   steelblue: 0x4682b4,
5395   tan: 0xd2b48c,
5396   teal: 0x008080,
5397   thistle: 0xd8bfd8,
5398   tomato: 0xff6347,
5399   turquoise: 0x40e0d0,
5400   violet: 0xee82ee,
5401   wheat: 0xf5deb3,
5402   white: 0xffffff,
5403   whitesmoke: 0xf5f5f5,
5404   yellow: 0xffff00,
5405   yellowgreen: 0x9acd32
5406 });
5407
5408 d3_rgb_names.forEach(function(key, value) {
5409   d3_rgb_names.set(key, d3_rgbNumber(value));
5410 });
5411
5412 d3.interpolateRgb = d3_interpolateRgb;
5413
5414 function d3_interpolateRgb(a, b) {
5415   a = d3.rgb(a);
5416   b = d3.rgb(b);
5417   var ar = a.r,
5418       ag = a.g,
5419       ab = a.b,
5420       br = b.r - ar,
5421       bg = b.g - ag,
5422       bb = b.b - ab;
5423   return function(t) {
5424     return "#"
5425         + d3_rgb_hex(Math.round(ar + br * t))
5426         + d3_rgb_hex(Math.round(ag + bg * t))
5427         + d3_rgb_hex(Math.round(ab + bb * t));
5428   };
5429 }
5430
5431 d3.interpolateObject = d3_interpolateObject;
5432
5433 function d3_interpolateObject(a, b) {
5434   var i = {},
5435       c = {},
5436       k;
5437   for (k in a) {
5438     if (k in b) {
5439       i[k] = d3_interpolate(a[k], b[k]);
5440     } else {
5441       c[k] = a[k];
5442     }
5443   }
5444   for (k in b) {
5445     if (!(k in a)) {
5446       c[k] = b[k];
5447     }
5448   }
5449   return function(t) {
5450     for (k in i) c[k] = i[k](t);
5451     return c;
5452   };
5453 }
5454
5455 d3.interpolateArray = d3_interpolateArray;
5456
5457 function d3_interpolateArray(a, b) {
5458   var x = [],
5459       c = [],
5460       na = a.length,
5461       nb = b.length,
5462       n0 = Math.min(a.length, b.length),
5463       i;
5464   for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
5465   for (; i < na; ++i) c[i] = a[i];
5466   for (; i < nb; ++i) c[i] = b[i];
5467   return function(t) {
5468     for (i = 0; i < n0; ++i) c[i] = x[i](t);
5469     return c;
5470   };
5471 }
5472 d3.interpolateNumber = d3_interpolateNumber;
5473
5474 function d3_interpolateNumber(a, b) {
5475   a = +a, b = +b;
5476   return function(t) { return a * (1 - t) + b * t; };
5477 }
5478
5479 d3.interpolateString = d3_interpolateString;
5480
5481 function d3_interpolateString(a, b) {
5482   var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b
5483       am, // current match in a
5484       bm, // current match in b
5485       bs, // string preceding current number in b, if any
5486       i = -1, // index in s
5487       s = [], // string constants and placeholders
5488       q = []; // number interpolators
5489
5490   // Coerce inputs to strings.
5491   a = a + "", b = b + "";
5492
5493   // Interpolate pairs of numbers in a & b.
5494   while ((am = d3_interpolate_numberA.exec(a))
5495       && (bm = d3_interpolate_numberB.exec(b))) {
5496     if ((bs = bm.index) > bi) { // a string precedes the next number in b
5497       bs = b.slice(bi, bs);
5498       if (s[i]) s[i] += bs; // coalesce with previous string
5499       else s[++i] = bs;
5500     }
5501     if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
5502       if (s[i]) s[i] += bm; // coalesce with previous string
5503       else s[++i] = bm;
5504     } else { // interpolate non-matching numbers
5505       s[++i] = null;
5506       q.push({i: i, x: d3_interpolateNumber(am, bm)});
5507     }
5508     bi = d3_interpolate_numberB.lastIndex;
5509   }
5510
5511   // Add remains of b.
5512   if (bi < b.length) {
5513     bs = b.slice(bi);
5514     if (s[i]) s[i] += bs; // coalesce with previous string
5515     else s[++i] = bs;
5516   }
5517
5518   // Special optimization for only a single match.
5519   // Otherwise, interpolate each of the numbers and rejoin the string.
5520   return s.length < 2
5521       ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; })
5522       : function() { return b; })
5523       : (b = q.length, function(t) {
5524           for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
5525           return s.join("");
5526         });
5527 }
5528
5529 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
5530     d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
5531
5532 d3.interpolate = d3_interpolate;
5533
5534 function d3_interpolate(a, b) {
5535   var i = d3.interpolators.length, f;
5536   while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
5537   return f;
5538 }
5539
5540 d3.interpolators = [
5541   function(a, b) {
5542     var t = typeof b;
5543     return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString)
5544         : b instanceof d3_color ? d3_interpolateRgb
5545         : Array.isArray(b) ? d3_interpolateArray
5546         : t === "object" && isNaN(b) ? d3_interpolateObject
5547         : d3_interpolateNumber)(a, b);
5548   }
5549 ];
5550
5551 d3.transform = function(string) {
5552   var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
5553   return (d3.transform = function(string) {
5554     if (string != null) {
5555       g.setAttribute("transform", string);
5556       var t = g.transform.baseVal.consolidate();
5557     }
5558     return new d3_transform(t ? t.matrix : d3_transformIdentity);
5559   })(string);
5560 };
5561
5562 // Compute x-scale and normalize the first row.
5563 // Compute shear and make second row orthogonal to first.
5564 // Compute y-scale and normalize the second row.
5565 // Finally, compute the rotation.
5566 function d3_transform(m) {
5567   var r0 = [m.a, m.b],
5568       r1 = [m.c, m.d],
5569       kx = d3_transformNormalize(r0),
5570       kz = d3_transformDot(r0, r1),
5571       ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
5572   if (r0[0] * r1[1] < r1[0] * r0[1]) {
5573     r0[0] *= -1;
5574     r0[1] *= -1;
5575     kx *= -1;
5576     kz *= -1;
5577   }
5578   this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
5579   this.translate = [m.e, m.f];
5580   this.scale = [kx, ky];
5581   this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
5582 };
5583
5584 d3_transform.prototype.toString = function() {
5585   return "translate(" + this.translate
5586       + ")rotate(" + this.rotate
5587       + ")skewX(" + this.skew
5588       + ")scale(" + this.scale
5589       + ")";
5590 };
5591
5592 function d3_transformDot(a, b) {
5593   return a[0] * b[0] + a[1] * b[1];
5594 }
5595
5596 function d3_transformNormalize(a) {
5597   var k = Math.sqrt(d3_transformDot(a, a));
5598   if (k) {
5599     a[0] /= k;
5600     a[1] /= k;
5601   }
5602   return k;
5603 }
5604
5605 function d3_transformCombine(a, b, k) {
5606   a[0] += k * b[0];
5607   a[1] += k * b[1];
5608   return a;
5609 }
5610
5611 var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
5612
5613 d3.interpolateTransform = d3_interpolateTransform;
5614
5615 function d3_interpolateTransform(a, b) {
5616   var s = [], // string constants and placeholders
5617       q = [], // number interpolators
5618       n,
5619       A = d3.transform(a),
5620       B = d3.transform(b),
5621       ta = A.translate,
5622       tb = B.translate,
5623       ra = A.rotate,
5624       rb = B.rotate,
5625       wa = A.skew,
5626       wb = B.skew,
5627       ka = A.scale,
5628       kb = B.scale;
5629
5630   if (ta[0] != tb[0] || ta[1] != tb[1]) {
5631     s.push("translate(", null, ",", null, ")");
5632     q.push({i: 1, x: d3_interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3_interpolateNumber(ta[1], tb[1])});
5633   } else if (tb[0] || tb[1]) {
5634     s.push("translate(" + tb + ")");
5635   } else {
5636     s.push("");
5637   }
5638
5639   if (ra != rb) {
5640     if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
5641     q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb)});
5642   } else if (rb) {
5643     s.push(s.pop() + "rotate(" + rb + ")");
5644   }
5645
5646   if (wa != wb) {
5647     q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)});
5648   } else if (wb) {
5649     s.push(s.pop() + "skewX(" + wb + ")");
5650   }
5651
5652   if (ka[0] != kb[0] || ka[1] != kb[1]) {
5653     n = s.push(s.pop() + "scale(", null, ",", null, ")");
5654     q.push({i: n - 4, x: d3_interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3_interpolateNumber(ka[1], kb[1])});
5655   } else if (kb[0] != 1 || kb[1] != 1) {
5656     s.push(s.pop() + "scale(" + kb + ")");
5657   }
5658
5659   n = q.length;
5660   return function(t) {
5661     var i = -1, o;
5662     while (++i < n) s[(o = q[i]).i] = o.x(t);
5663     return s.join("");
5664   };
5665 }
5666
5667 d3_transitionPrototype.tween = function(name, tween) {
5668   var id = this.id, ns = this.namespace;
5669   if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
5670   return d3_selection_each(this, tween == null
5671         ? function(node) { node[ns][id].tween.remove(name); }
5672         : function(node) { node[ns][id].tween.set(name, tween); });
5673 };
5674
5675 function d3_transition_tween(groups, name, value, tween) {
5676   var id = groups.id, ns = groups.namespace;
5677   return d3_selection_each(groups, typeof value === "function"
5678       ? function(node, i, j) { node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); }
5679       : (value = tween(value), function(node) { node[ns][id].tween.set(name, value); }));
5680 }
5681
5682 d3_transitionPrototype.attr = function(nameNS, value) {
5683   if (arguments.length < 2) {
5684
5685     // For attr(object), the object specifies the names and values of the
5686     // attributes to transition. The values may be functions that are
5687     // evaluated for each element.
5688     for (value in nameNS) this.attr(value, nameNS[value]);
5689     return this;
5690   }
5691
5692   var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
5693       name = d3.ns.qualify(nameNS);
5694
5695   // For attr(string, null), remove the attribute with the specified name.
5696   function attrNull() {
5697     this.removeAttribute(name);
5698   }
5699   function attrNullNS() {
5700     this.removeAttributeNS(name.space, name.local);
5701   }
5702
5703   // For attr(string, string), set the attribute with the specified name.
5704   function attrTween(b) {
5705     return b == null ? attrNull : (b += "", function() {
5706       var a = this.getAttribute(name), i;
5707       return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
5708     });
5709   }
5710   function attrTweenNS(b) {
5711     return b == null ? attrNullNS : (b += "", function() {
5712       var a = this.getAttributeNS(name.space, name.local), i;
5713       return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
5714     });
5715   }
5716
5717   return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
5718 };
5719
5720 d3_transitionPrototype.attrTween = function(nameNS, tween) {
5721   var name = d3.ns.qualify(nameNS);
5722
5723   function attrTween(d, i) {
5724     var f = tween.call(this, d, i, this.getAttribute(name));
5725     return f && function(t) { this.setAttribute(name, f(t)); };
5726   }
5727   function attrTweenNS(d, i) {
5728     var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
5729     return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
5730   }
5731
5732   return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
5733 };
5734
5735 d3_transitionPrototype.style = function(name, value, priority) {
5736   var n = arguments.length;
5737   if (n < 3) {
5738
5739     // For style(object) or style(object, string), the object specifies the
5740     // names and values of the attributes to set or remove. The values may be
5741     // functions that are evaluated for each element. The optional string
5742     // specifies the priority.
5743     if (typeof name !== "string") {
5744       if (n < 2) value = "";
5745       for (priority in name) this.style(priority, name[priority], value);
5746       return this;
5747     }
5748
5749     // For style(string, string) or style(string, function), use the default
5750     // priority. The priority is ignored for style(string, null).
5751     priority = "";
5752   }
5753
5754   // For style(name, null) or style(name, null, priority), remove the style
5755   // property with the specified name. The priority is ignored.
5756   function styleNull() {
5757     this.style.removeProperty(name);
5758   }
5759
5760   // For style(name, string) or style(name, string, priority), set the style
5761   // property with the specified name, using the specified priority.
5762   // Otherwise, a name, value and priority are specified, and handled as below.
5763   function styleString(b) {
5764     return b == null ? styleNull : (b += "", function() {
5765       var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
5766       return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
5767     });
5768   }
5769
5770   return d3_transition_tween(this, "style." + name, value, styleString);
5771 };
5772
5773 d3_transitionPrototype.styleTween = function(name, tween, priority) {
5774   if (arguments.length < 3) priority = "";
5775
5776   function styleTween(d, i) {
5777     var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
5778     return f && function(t) { this.style.setProperty(name, f(t), priority); };
5779   }
5780
5781   return this.tween("style." + name, styleTween);
5782 };
5783
5784 d3_transitionPrototype.text = function(value) {
5785   return d3_transition_tween(this, "text", value, d3_transition_text);
5786 };
5787
5788 function d3_transition_text(b) {
5789   if (b == null) b = "";
5790   return function() { this.textContent = b; };
5791 }
5792
5793 d3_transitionPrototype.remove = function() {
5794   var ns = this.namespace;
5795   return this.each("end.transition", function() {
5796     var p;
5797     if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
5798   });
5799 };
5800
5801 var d3_ease_default = function() { return d3_identity; };
5802
5803 var d3_ease = d3.map({
5804   linear: d3_ease_default,
5805   poly: d3_ease_poly,
5806   quad: function() { return d3_ease_quad; },
5807   cubic: function() { return d3_ease_cubic; },
5808   sin: function() { return d3_ease_sin; },
5809   exp: function() { return d3_ease_exp; },
5810   circle: function() { return d3_ease_circle; },
5811   elastic: d3_ease_elastic,
5812   back: d3_ease_back,
5813   bounce: function() { return d3_ease_bounce; }
5814 });
5815
5816 var d3_ease_mode = d3.map({
5817   "in": d3_identity,
5818   "out": d3_ease_reverse,
5819   "in-out": d3_ease_reflect,
5820   "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); }
5821 });
5822
5823 d3.ease = function(name) {
5824   var i = name.indexOf("-"),
5825       t = i >= 0 ? name.slice(0, i) : name,
5826       m = i >= 0 ? name.slice(i + 1) : "in";
5827   t = d3_ease.get(t) || d3_ease_default;
5828   m = d3_ease_mode.get(m) || d3_identity;
5829   return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
5830 };
5831
5832 function d3_ease_clamp(f) {
5833   return function(t) {
5834     return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
5835   };
5836 }
5837
5838 function d3_ease_reverse(f) {
5839   return function(t) {
5840     return 1 - f(1 - t);
5841   };
5842 }
5843
5844 function d3_ease_reflect(f) {
5845   return function(t) {
5846     return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
5847   };
5848 }
5849
5850 function d3_ease_quad(t) {
5851   return t * t;
5852 }
5853
5854 function d3_ease_cubic(t) {
5855   return t * t * t;
5856 }
5857
5858 // Optimized clamp(reflect(poly(3))).
5859 function d3_ease_cubicInOut(t) {
5860   if (t <= 0) return 0;
5861   if (t >= 1) return 1;
5862   var t2 = t * t, t3 = t2 * t;
5863   return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
5864 }
5865
5866 function d3_ease_poly(e) {
5867   return function(t) {
5868     return Math.pow(t, e);
5869   };
5870 }
5871
5872 function d3_ease_sin(t) {
5873   return 1 - Math.cos(t * halfπ);
5874 }
5875
5876 function d3_ease_exp(t) {
5877   return Math.pow(2, 10 * (t - 1));
5878 }
5879
5880 function d3_ease_circle(t) {
5881   return 1 - Math.sqrt(1 - t * t);
5882 }
5883
5884 function d3_ease_elastic(a, p) {
5885   var s;
5886   if (arguments.length < 2) p = 0.45;
5887   if (arguments.length) s = p / τ * Math.asin(1 / a);
5888   else a = 1, s = p / 4;
5889   return function(t) {
5890     return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
5891   };
5892 }
5893
5894 function d3_ease_back(s) {
5895   if (!s) s = 1.70158;
5896   return function(t) {
5897     return t * t * ((s + 1) * t - s);
5898   };
5899 }
5900
5901 function d3_ease_bounce(t) {
5902   return t < 1 / 2.75 ? 7.5625 * t * t
5903       : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75
5904       : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375
5905       : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
5906 }
5907
5908 d3_transitionPrototype.ease = function(value) {
5909   var id = this.id, ns = this.namespace;
5910   if (arguments.length < 1) return this.node()[ns][id].ease;
5911   if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
5912   return d3_selection_each(this, function(node) { node[ns][id].ease = value; });
5913 };
5914
5915 d3_transitionPrototype.delay = function(value) {
5916   var id = this.id, ns = this.namespace;
5917   if (arguments.length < 1) return this.node()[ns][id].delay;
5918   return d3_selection_each(this, typeof value === "function"
5919       ? function(node, i, j) { node[ns][id].delay = +value.call(node, node.__data__, i, j); }
5920       : (value = +value, function(node) { node[ns][id].delay = value; }));
5921 };
5922
5923 d3_transitionPrototype.duration = function(value) {
5924   var id = this.id, ns = this.namespace;
5925   if (arguments.length < 1) return this.node()[ns][id].duration;
5926   return d3_selection_each(this, typeof value === "function"
5927       ? function(node, i, j) { node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); }
5928       : (value = Math.max(1, value), function(node) { node[ns][id].duration = value; }));
5929 };
5930
5931 d3_transitionPrototype.each = function(type, listener) {
5932   var id = this.id, ns = this.namespace;
5933   if (arguments.length < 2) {
5934     var inherit = d3_transitionInherit,
5935         inheritId = d3_transitionInheritId;
5936     try {
5937       d3_transitionInheritId = id;
5938       d3_selection_each(this, function(node, i, j) {
5939         d3_transitionInherit = node[ns][id];
5940         type.call(node, node.__data__, i, j);
5941       });
5942     } finally {
5943       d3_transitionInherit = inherit;
5944       d3_transitionInheritId = inheritId;
5945     }
5946   } else {
5947     d3_selection_each(this, function(node) {
5948       var transition = node[ns][id];
5949       (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
5950     });
5951   }
5952   return this;
5953 };
5954
5955 d3_transitionPrototype.transition = function() {
5956   var id0 = this.id,
5957       id1 = ++d3_transitionId,
5958       ns = this.namespace,
5959       subgroups = [],
5960       subgroup,
5961       group,
5962       node,
5963       transition;
5964
5965   for (var j = 0, m = this.length; j < m; j++) {
5966     subgroups.push(subgroup = []);
5967     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
5968       if (node = group[i]) {
5969         transition = node[ns][id0];
5970         d3_transitionNode(node, i, ns, id1, {time: transition.time, ease: transition.ease, delay: transition.delay + transition.duration, duration: transition.duration});
5971       }
5972       subgroup.push(node);
5973     }
5974   }
5975
5976   return d3_transition(subgroups, ns, id1);
5977 };
5978
5979 function d3_transitionNamespace(name) {
5980   return name == null ? "__transition__" : "__transition_" + name + "__";
5981 }
5982
5983 function d3_transitionNode(node, i, ns, id, inherit) {
5984   var lock = node[ns] || (node[ns] = {active: 0, count: 0}),
5985       transition = lock[id];
5986
5987   if (!transition) {
5988     var time = inherit.time;
5989
5990     transition = lock[id] = {
5991       tween: new d3_Map,
5992       time: time,
5993       delay: inherit.delay,
5994       duration: inherit.duration,
5995       ease: inherit.ease,
5996       index: i
5997     };
5998
5999     inherit = null; // allow gc
6000
6001     ++lock.count;
6002
6003     d3.timer(function(elapsed) {
6004       var delay = transition.delay,
6005           duration,
6006           ease,
6007           timer = d3_timer_active,
6008           tweened = [];
6009
6010       timer.t = delay + time;
6011       if (delay <= elapsed) return start(elapsed - delay);
6012       timer.c = start;
6013
6014       function start(elapsed) {
6015         if (lock.active > id) return stop();
6016
6017         var active = lock[lock.active];
6018         if (active) {
6019           --lock.count;
6020           delete lock[lock.active];
6021           active.event && active.event.interrupt.call(node, node.__data__, active.index);
6022         }
6023
6024         lock.active = id;
6025
6026         transition.event && transition.event.start.call(node, node.__data__, i);
6027
6028         transition.tween.forEach(function(key, value) {
6029           if (value = value.call(node, node.__data__, i)) {
6030             tweened.push(value);
6031           }
6032         });
6033
6034         // Deferred capture to allow tweens to initialize ease & duration.
6035         ease = transition.ease;
6036         duration = transition.duration;
6037
6038         d3.timer(function() { // defer to end of current frame
6039           timer.c = tick(elapsed || 1) ? d3_true : tick;
6040           return 1;
6041         }, 0, time);
6042       }
6043
6044       function tick(elapsed) {
6045         if (lock.active !== id) return 1;
6046
6047         var t = elapsed / duration,
6048             e = ease(t),
6049             n = tweened.length;
6050
6051         while (n > 0) {
6052           tweened[--n].call(node, e);
6053         }
6054
6055         if (t >= 1) {
6056           transition.event && transition.event.end.call(node, node.__data__, i);
6057           return stop();
6058         }
6059       }
6060
6061       function stop() {
6062         if (--lock.count) delete lock[id];
6063         else delete node[ns];
6064         return 1;
6065       }
6066     }, 0, time);
6067   }
6068 }
6069
6070 d3.xhr = d3_xhrType(d3_identity);
6071
6072 function d3_xhrType(response) {
6073   return function(url, mimeType, callback) {
6074     if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null;
6075     return d3_xhr(url, mimeType, response, callback);
6076   };
6077 }
6078
6079 function d3_xhr(url, mimeType, response, callback) {
6080   var xhr = {},
6081       dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
6082       headers = {},
6083       request = new XMLHttpRequest,
6084       responseType = null;
6085
6086   // If IE does not support CORS, use XDomainRequest.
6087   if (this.XDomainRequest
6088       && !("withCredentials" in request)
6089       && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest;
6090
6091   "onload" in request
6092       ? request.onload = request.onerror = respond
6093       : request.onreadystatechange = function() { request.readyState > 3 && respond(); };
6094
6095   function respond() {
6096     var status = request.status, result;
6097     if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
6098       try {
6099         result = response.call(xhr, request);
6100       } catch (e) {
6101         dispatch.error.call(xhr, e);
6102         return;
6103       }
6104       dispatch.load.call(xhr, result);
6105     } else {
6106       dispatch.error.call(xhr, request);
6107     }
6108   }
6109
6110   request.onprogress = function(event) {
6111     var o = d3.event;
6112     d3.event = event;
6113     try { dispatch.progress.call(xhr, request); }
6114     finally { d3.event = o; }
6115   };
6116
6117   xhr.header = function(name, value) {
6118     name = (name + "").toLowerCase();
6119     if (arguments.length < 2) return headers[name];
6120     if (value == null) delete headers[name];
6121     else headers[name] = value + "";
6122     return xhr;
6123   };
6124
6125   // If mimeType is non-null and no Accept header is set, a default is used.
6126   xhr.mimeType = function(value) {
6127     if (!arguments.length) return mimeType;
6128     mimeType = value == null ? null : value + "";
6129     return xhr;
6130   };
6131
6132   // Specifies what type the response value should take;
6133   // for instance, arraybuffer, blob, document, or text.
6134   xhr.responseType = function(value) {
6135     if (!arguments.length) return responseType;
6136     responseType = value;
6137     return xhr;
6138   };
6139
6140   // Specify how to convert the response content to a specific type;
6141   // changes the callback value on "load" events.
6142   xhr.response = function(value) {
6143     response = value;
6144     return xhr;
6145   };
6146
6147   // Convenience methods.
6148   ["get", "post"].forEach(function(method) {
6149     xhr[method] = function() {
6150       return xhr.send.apply(xhr, [method].concat(d3_array(arguments)));
6151     };
6152   });
6153
6154   // If callback is non-null, it will be used for error and load events.
6155   xhr.send = function(method, data, callback) {
6156     if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
6157     request.open(method, url, true);
6158     if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
6159     if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
6160     if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
6161     if (responseType != null) request.responseType = responseType;
6162     if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
6163     dispatch.beforesend.call(xhr, request);
6164     request.send(data == null ? null : data);
6165     return xhr;
6166   };
6167
6168   xhr.abort = function() {
6169     request.abort();
6170     return xhr;
6171   };
6172
6173   d3.rebind(xhr, dispatch, "on");
6174
6175   return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
6176 };
6177
6178 function d3_xhr_fixCallback(callback) {
6179   return callback.length === 1
6180       ? function(error, request) { callback(error == null ? request : null); }
6181       : callback;
6182 }
6183
6184 function d3_xhrHasResponse(request) {
6185   var type = request.responseType;
6186   return type && type !== "text"
6187       ? request.response // null on error
6188       : request.responseText; // "" on error
6189 }
6190
6191 d3.text = d3_xhrType(function(request) {
6192   return request.responseText;
6193 });
6194
6195 d3.json = function(url, callback) {
6196   return d3_xhr(url, "application/json", d3_json, callback);
6197 };
6198
6199 function d3_json(request) {
6200   return JSON.parse(request.responseText);
6201 }
6202
6203 d3.html = function(url, callback) {
6204   return d3_xhr(url, "text/html", d3_html, callback);
6205 };
6206
6207 function d3_html(request) {
6208   var range = d3_document.createRange();
6209   range.selectNode(d3_document.body);
6210   return range.createContextualFragment(request.responseText);
6211 }
6212
6213 d3.xml = d3_xhrType(function(request) {
6214   return request.responseXML;
6215 });
6216   if (typeof define === "function" && define.amd) define(d3);
6217   else if (typeof module === "object" && module.exports) module.exports = d3;
6218   this.d3 = d3;
6219 }();
6220 d3.combobox = function() {
6221     var event = d3.dispatch('accept'),
6222         data = [],
6223         suggestions = [],
6224         minItems = 2,
6225         caseSensitive = false;
6226
6227     var fetcher = function(val, cb) {
6228         cb(data.filter(function(d) {
6229             return d.value
6230                 .toString()
6231                 .toLowerCase()
6232                 .indexOf(val.toLowerCase()) !== -1;
6233         }));
6234     };
6235
6236     var combobox = function(input, attachTo) {
6237         var idx = -1,
6238             container = d3.select(document.body)
6239                 .selectAll('div.combobox')
6240                 .filter(function(d) { return d === input.node(); }),
6241             shown = !container.empty();
6242
6243         input
6244             .classed('combobox-input', true)
6245             .on('focus.typeahead', focus)
6246             .on('blur.typeahead', blur)
6247             .on('keydown.typeahead', keydown)
6248             .on('keyup.typeahead', keyup)
6249             .on('input.typeahead', change)
6250             .each(function() {
6251                 var parent = this.parentNode,
6252                     sibling = this.nextSibling;
6253
6254                 var caret = d3.select(parent).selectAll('.combobox-caret')
6255                     .filter(function(d) { return d === input.node(); })
6256                     .data([input.node()]);
6257
6258                 caret.enter().insert('div', function() { return sibling; })
6259                     .attr('class', 'combobox-caret');
6260
6261                 caret
6262                     .on('mousedown', function () {
6263                         // prevent the form element from blurring. it blurs
6264                         // on mousedown
6265                         d3.event.stopPropagation();
6266                         d3.event.preventDefault();
6267                         if (!shown) {
6268                             input.node().focus();
6269                             fetch('', render);
6270                         } else {
6271                             hide();
6272                         }
6273                     });
6274             });
6275
6276         function focus() {
6277             fetch(value(), render);
6278         }
6279
6280         function blur() {
6281             window.setTimeout(hide, 150);
6282         }
6283
6284         function show() {
6285             if (!shown) {
6286                 container = d3.select(document.body)
6287                     .insert('div', ':first-child')
6288                     .datum(input.node())
6289                     .attr('class', 'combobox')
6290                     .style({
6291                         position: 'absolute',
6292                         display: 'block',
6293                         left: '0px'
6294                     })
6295                     .on('mousedown', function () {
6296                         // prevent moving focus out of the text field
6297                         d3.event.preventDefault();
6298                     });
6299
6300                 d3.select(document.body)
6301                     .on('scroll.combobox', render, true);
6302
6303                 shown = true;
6304             }
6305         }
6306
6307         function hide() {
6308             if (shown) {
6309                 idx = -1;
6310                 container.remove();
6311
6312                 d3.select(document.body)
6313                     .on('scroll.combobox', null);
6314
6315                 shown = false;
6316             }
6317         }
6318
6319         function keydown() {
6320            switch (d3.event.keyCode) {
6321                // backspace, delete
6322                case 8:
6323                case 46:
6324                    input.on('input.typeahead', function() {
6325                        idx = -1;
6326                        render();
6327                        var start = input.property('selectionStart');
6328                        input.node().setSelectionRange(start, start);
6329                        input.on('input.typeahead', change);
6330                    });
6331                    break;
6332                // tab
6333                case 9:
6334                    container.selectAll('a.selected').each(event.accept);
6335                    break;
6336                // return
6337                case 13:
6338                    d3.event.preventDefault();
6339                    break;
6340                // up arrow
6341                case 38:
6342                    nav(-1);
6343                    d3.event.preventDefault();
6344                    break;
6345                // down arrow
6346                case 40:
6347                    nav(+1);
6348                    d3.event.preventDefault();
6349                    break;
6350            }
6351            d3.event.stopPropagation();
6352         }
6353
6354         function keyup() {
6355             switch (d3.event.keyCode) {
6356                 // escape
6357                 case 27:
6358                     hide();
6359                     break;
6360                 // return
6361                 case 13:
6362                     container.selectAll('a.selected').each(event.accept);
6363                     hide();
6364                     break;
6365             }
6366         }
6367
6368         function change() {
6369             fetch(value(), function() {
6370                 if (input.property('selectionEnd') === input.property('value').length) {
6371                     autocomplete();
6372                 }
6373                 render();
6374             });
6375         }
6376
6377         function nav(dir) {
6378             if (!suggestions.length) return;
6379             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6380             input.property('value', suggestions[idx].value);
6381             render();
6382             ensureVisible();
6383         }
6384
6385         function value() {
6386             var value = input.property('value'),
6387                 start = input.property('selectionStart'),
6388                 end = input.property('selectionEnd');
6389
6390             if (start && end) {
6391                 value = value.substring(0, start);
6392             }
6393
6394             return value;
6395         }
6396
6397         function fetch(v, cb) {
6398             fetcher.call(input, v, function(_) {
6399                 suggestions = _;
6400                 cb();
6401             });
6402         }
6403
6404         function autocomplete() {
6405             var v = caseSensitive ? value() : value().toLowerCase();
6406             idx = -1;
6407             if (!v) return;
6408
6409             for (var i = 0; i < suggestions.length; i++) {
6410                 var suggestion = suggestions[i].value,
6411                     compare = caseSensitive ? suggestion : suggestion.toLowerCase();
6412
6413                 if (compare.indexOf(v) === 0) {
6414                     idx = i;
6415                     input.property('value', suggestion);
6416                     input.node().setSelectionRange(v.length, suggestion.length);
6417                     return;
6418                 }
6419             }
6420         }
6421
6422         function render() {
6423             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6424                 show();
6425             } else {
6426                 hide();
6427                 return;
6428             }
6429
6430             var options = container
6431                 .selectAll('a.combobox-option')
6432                 .data(suggestions, function(d) { return d.value; });
6433
6434             options.enter().append('a')
6435                 .attr('class', 'combobox-option')
6436                 .text(function(d) { return d.value; });
6437
6438             options
6439                 .attr('title', function(d) { return d.title; })
6440                 .classed('selected', function(d, i) { return i == idx; })
6441                 .on('mouseover', select)
6442                 .on('click', accept)
6443                 .order();
6444
6445             options.exit()
6446                 .remove();
6447
6448             var node = attachTo ? attachTo.node() : input.node(),
6449                 rect = node.getBoundingClientRect();
6450
6451             container.style({
6452                 'left': rect.left + 'px',
6453                 'width': rect.width + 'px',
6454                 'top': rect.height + rect.top + 'px'
6455             });
6456         }
6457
6458         function select(d, i) {
6459             idx = i;
6460             render();
6461         }
6462
6463         function ensureVisible() {
6464             var node = container.selectAll('a.selected').node();
6465             if (node) node.scrollIntoView();
6466         }
6467
6468         function accept(d) {
6469             if (!shown) return;
6470             input
6471                 .property('value', d.value)
6472                 .trigger('change');
6473             event.accept(d);
6474             hide();
6475         }
6476     };
6477
6478     combobox.fetcher = function(_) {
6479         if (!arguments.length) return fetcher;
6480         fetcher = _;
6481         return combobox;
6482     };
6483
6484     combobox.data = function(_) {
6485         if (!arguments.length) return data;
6486         data = _;
6487         return combobox;
6488     };
6489
6490     combobox.minItems = function(_) {
6491         if (!arguments.length) return minItems;
6492         minItems = _;
6493         return combobox;
6494     };
6495
6496     combobox.caseSensitive = function(_) {
6497         if (!arguments.length) return caseSensitive;
6498         caseSensitive = _;
6499         return combobox;
6500     };
6501
6502     return d3.rebind(combobox, event, 'on');
6503 };
6504
6505 d3.combobox.off = function(input) {
6506     data = null;
6507     fetcher = null;
6508
6509     input
6510         .on('focus.typeahead', null)
6511         .on('blur.typeahead', null)
6512         .on('keydown.typeahead', null)
6513         .on('keyup.typeahead', null)
6514         .on('input.typeahead', null)
6515         .each(function() {
6516             d3.select(this.parentNode).selectAll('.combobox-caret')
6517                 .filter(function(d) { return d === input.node(); })
6518                 .on('mousedown', null);
6519         });
6520
6521     d3.select(document.body)
6522         .on('scroll.combobox', null);
6523 };
6524 d3.geo.tile = function() {
6525   var size = [960, 500],
6526       scale = 256,
6527       scaleExtent = [0, 20],
6528       translate = [size[0] / 2, size[1] / 2],
6529       zoomDelta = 0;
6530
6531   function bound(_) {
6532       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6533   }
6534
6535   function tile() {
6536     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6537         z0 = bound(Math.round(z + zoomDelta)),
6538         k = Math.pow(2, z - z0 + 8),
6539         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6540         tiles = [],
6541         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6542         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6543
6544     rows.forEach(function(y) {
6545       cols.forEach(function(x) {
6546         tiles.push([x, y, z0]);
6547       });
6548     });
6549
6550     tiles.translate = origin;
6551     tiles.scale = k;
6552
6553     return tiles;
6554   }
6555
6556   tile.scaleExtent = function(_) {
6557     if (!arguments.length) return scaleExtent;
6558     scaleExtent = _;
6559     return tile;
6560   };
6561
6562   tile.size = function(_) {
6563     if (!arguments.length) return size;
6564     size = _;
6565     return tile;
6566   };
6567
6568   tile.scale = function(_) {
6569     if (!arguments.length) return scale;
6570     scale = _;
6571     return tile;
6572   };
6573
6574   tile.translate = function(_) {
6575     if (!arguments.length) return translate;
6576     translate = _;
6577     return tile;
6578   };
6579
6580   tile.zoomDelta = function(_) {
6581     if (!arguments.length) return zoomDelta;
6582     zoomDelta = +_;
6583     return tile;
6584   };
6585
6586   return tile;
6587 };
6588 d3.jsonp = function (url, callback) {
6589   function rand() {
6590     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6591       c = '', i = -1;
6592     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6593     return c;
6594   }
6595
6596   function create(url) {
6597     var e = url.match(/callback=d3.jsonp.(\w+)/),
6598       c = e ? e[1] : rand();
6599     d3.jsonp[c] = function(data) {
6600       callback(data);
6601       delete d3.jsonp[c];
6602       script.remove();
6603     };
6604     return 'd3.jsonp.' + c;
6605   }
6606
6607   var cb = create(url),
6608     script = d3.select('head')
6609     .append('script')
6610     .attr('type', 'text/javascript')
6611     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6612 };
6613 /*
6614  * This code is licensed under the MIT license.
6615  *
6616  * Copyright © 2013, iD authors.
6617  *
6618  * Portions copyright © 2011, Keith Cirkel
6619  * See https://github.com/keithamus/jwerty
6620  *
6621  */
6622 d3.keybinding = function(namespace) {
6623     var bindings = [];
6624
6625     function matches(binding, event) {
6626         for (var p in binding.event) {
6627             if (event[p] != binding.event[p])
6628                 return false;
6629         }
6630         return true;
6631     }
6632
6633     function testBindings(isCapturing) {
6634         for (var i = 0; i < bindings.length; i++) {
6635             var binding = bindings[i];
6636
6637             if (!!binding.capture === isCapturing && matches(binding, d3.event)) {
6638                 binding.callback();
6639             }
6640         }
6641     }
6642
6643     function capture() {
6644         testBindings(true);
6645     }
6646
6647     function bubble() {
6648         var tagName = d3.select(d3.event.target).node().tagName;
6649         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6650             return;
6651         }
6652         testBindings(false);
6653     }
6654
6655     function keybinding(selection) {
6656         selection = selection || d3.select(document);
6657         selection.on('keydown.capture' + namespace, capture, true);
6658         selection.on('keydown.bubble' + namespace, bubble, false);
6659         return keybinding;
6660     }
6661
6662     keybinding.off = function(selection) {
6663         bindings = [];
6664         selection = selection || d3.select(document);
6665         selection.on('keydown.capture' + namespace, null);
6666         selection.on('keydown.bubble' + namespace, null);
6667         return keybinding;
6668     };
6669
6670     keybinding.on = function(code, callback, capture) {
6671         var binding = {
6672             event: {
6673                 keyCode: 0,
6674                 shiftKey: false,
6675                 ctrlKey: false,
6676                 altKey: false,
6677                 metaKey: false
6678             },
6679             capture: capture,
6680             callback: callback
6681         };
6682
6683         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6684
6685         for (var i = 0; i < code.length; i++) {
6686             // Normalise matching errors
6687             if (code[i] === '++') code[i] = '+';
6688
6689             if (code[i] in d3.keybinding.modifierCodes) {
6690                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6691             } else if (code[i] in d3.keybinding.keyCodes) {
6692                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6693             }
6694         }
6695
6696         bindings.push(binding);
6697
6698         return keybinding;
6699     };
6700
6701     return keybinding;
6702 };
6703
6704 (function () {
6705     d3.keybinding.modifierCodes = {
6706         // Shift key, ⇧
6707         '⇧': 16, shift: 16,
6708         // CTRL key, on Mac: ⌃
6709         '⌃': 17, ctrl: 17,
6710         // ALT key, on Mac: ⌥ (Alt)
6711         '⌥': 18, alt: 18, option: 18,
6712         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6713         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6714     };
6715
6716     d3.keybinding.modifierProperties = {
6717         16: 'shiftKey',
6718         17: 'ctrlKey',
6719         18: 'altKey',
6720         91: 'metaKey'
6721     };
6722
6723     d3.keybinding.keyCodes = {
6724         // Backspace key, on Mac: ⌫ (Backspace)
6725         '⌫': 8, backspace: 8,
6726         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6727         '⇥': 9, '⇆': 9, tab: 9,
6728         // Return key, ↩
6729         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6730         // Pause/Break key
6731         'pause': 19, 'pause-break': 19,
6732         // Caps Lock key, ⇪
6733         '⇪': 20, caps: 20, 'caps-lock': 20,
6734         // Escape key, on Mac: ⎋, on Windows: Esc
6735         '⎋': 27, escape: 27, esc: 27,
6736         // Space key
6737         space: 32,
6738         // Page-Up key, or pgup, on Mac: ↖
6739         '↖': 33, pgup: 33, 'page-up': 33,
6740         // Page-Down key, or pgdown, on Mac: ↘
6741         '↘': 34, pgdown: 34, 'page-down': 34,
6742         // END key, on Mac: ⇟
6743         '⇟': 35, end: 35,
6744         // HOME key, on Mac: ⇞
6745         '⇞': 36, home: 36,
6746         // Insert key, or ins
6747         ins: 45, insert: 45,
6748         // Delete key, on Mac: ⌦ (Delete)
6749         '⌦': 46, del: 46, 'delete': 46,
6750         // Left Arrow Key, or ←
6751         '←': 37, left: 37, 'arrow-left': 37,
6752         // Up Arrow Key, or ↑
6753         '↑': 38, up: 38, 'arrow-up': 38,
6754         // Right Arrow Key, or →
6755         '→': 39, right: 39, 'arrow-right': 39,
6756         // Up Arrow Key, or ↓
6757         '↓': 40, down: 40, 'arrow-down': 40,
6758         // odities, printing characters that come out wrong:
6759         // Firefox Equals
6760         'ffequals': 61,
6761         // Num-Multiply, or *
6762         '*': 106, star: 106, asterisk: 106, multiply: 106,
6763         // Num-Plus or +
6764         '+': 107, 'plus': 107,
6765         // Num-Subtract, or -
6766         '-': 109, subtract: 109,
6767         // Firefox Plus
6768         'ffplus': 171,
6769         // Firefox Minus
6770         'ffminus': 173,
6771         // Semicolon
6772         ';': 186, semicolon: 186,
6773         // = or equals
6774         '=': 187, 'equals': 187,
6775         // Comma, or ,
6776         ',': 188, comma: 188,
6777         'dash': 189, //???
6778         // Period, or ., or full-stop
6779         '.': 190, period: 190, 'full-stop': 190,
6780         // Slash, or /, or forward-slash
6781         '/': 191, slash: 191, 'forward-slash': 191,
6782         // Tick, or `, or back-quote
6783         '`': 192, tick: 192, 'back-quote': 192,
6784         // Open bracket, or [
6785         '[': 219, 'open-bracket': 219,
6786         // Back slash, or \
6787         '\\': 220, 'back-slash': 220,
6788         // Close backet, or ]
6789         ']': 221, 'close-bracket': 221,
6790         // Apostrophe, or Quote, or '
6791         '\'': 222, quote: 222, apostrophe: 222
6792     };
6793
6794     // NUMPAD 0-9
6795     var i = 95, n = 0;
6796     while (++i < 106) {
6797         d3.keybinding.keyCodes['num-' + n] = i;
6798         ++n;
6799     }
6800
6801     // 0-9
6802     i = 47; n = 0;
6803     while (++i < 58) {
6804         d3.keybinding.keyCodes[n] = i;
6805         ++n;
6806     }
6807
6808     // F1-F25
6809     i = 111; n = 1;
6810     while (++i < 136) {
6811         d3.keybinding.keyCodes['f' + n] = i;
6812         ++n;
6813     }
6814
6815     // a-z
6816     i = 64;
6817     while (++i < 91) {
6818         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6819     }
6820 })();
6821 d3.selection.prototype.one = function (type, listener, capture) {
6822     var target = this, typeOnce = type + ".once";
6823     function one() {
6824         target.on(typeOnce, null);
6825         listener.apply(this, arguments);
6826     }
6827     target.on(typeOnce, one, capture);
6828     return this;
6829 };
6830 d3.selection.prototype.dimensions = function (dimensions) {
6831     var refresh = (function(node) {
6832         var cr = node.getBoundingClientRect();
6833         prop = [cr.width, cr.height];
6834         this.property('__dimensions__', prop);
6835         return prop;
6836     }).bind(this);
6837
6838     var node = this.node();
6839
6840     if (!arguments.length) {
6841         if (!node) return [0,0];
6842         return this.property('__dimensions__') || refresh(node);
6843     }
6844     if (dimensions === null) {
6845         if (!node) return [0,0];
6846         return refresh(node);
6847     }
6848
6849     return this
6850         .property('__dimensions__', [dimensions[0], dimensions[1]])
6851         .attr({width: dimensions[0], height: dimensions[1]});
6852 };
6853 d3.selection.prototype.trigger = function (type) {
6854     this.each(function() {
6855         var evt = document.createEvent('HTMLEvents');
6856         evt.initEvent(type, true, true);
6857         this.dispatchEvent(evt);
6858     });
6859 };
6860 // Tooltips and svg mask used to highlight certain features
6861 d3.curtain = function() {
6862
6863     var event = d3.dispatch(),
6864         surface,
6865         tooltip,
6866         darkness;
6867
6868     function curtain(selection) {
6869
6870         surface = selection.append('svg')
6871             .attr('id', 'curtain')
6872             .style({
6873                 'z-index': 1000,
6874                 'pointer-events': 'none',
6875                 'position': 'absolute',
6876                 'top': 0,
6877                 'left': 0
6878             });
6879
6880         darkness = surface.append('path')
6881             .attr({
6882                 x: 0,
6883                 y: 0,
6884                 'class': 'curtain-darkness'
6885             });
6886
6887         d3.select(window).on('resize.curtain', resize);
6888
6889         tooltip = selection.append('div')
6890             .attr('class', 'tooltip')
6891             .style('z-index', 1002);
6892
6893         tooltip.append('div').attr('class', 'tooltip-arrow');
6894         tooltip.append('div').attr('class', 'tooltip-inner');
6895
6896         resize();
6897
6898         function resize() {
6899             surface.attr({
6900                 width: window.innerWidth,
6901                 height: window.innerHeight
6902             });
6903             curtain.cut(darkness.datum());
6904         }
6905     }
6906
6907     curtain.reveal = function(box, text, tooltipclass, duration) {
6908         if (typeof box === 'string') box = d3.select(box).node();
6909         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6910
6911         curtain.cut(box, duration);
6912
6913         if (text) {
6914             // pseudo markdown bold text hack
6915             var parts = text.split('**');
6916             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6917             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6918
6919             var dimensions = tooltip.classed('in', true)
6920                 .select('.tooltip-inner')
6921                     .html(html)
6922                     .dimensions();
6923
6924             var pos;
6925
6926             var w = window.innerWidth,
6927                 h = window.innerHeight;
6928
6929             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6930                 side = 'bottom';
6931                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6932
6933             } else if (box.left + box.width + 300 < window.innerWidth) {
6934                 side = 'right';
6935                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6936
6937             } else if (box.left > 300) {
6938                 side = 'left';
6939                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6940             } else {
6941                 side = 'bottom';
6942                 pos = [box.left, box.top + box.height];
6943             }
6944
6945             pos = [
6946                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6947                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6948             ];
6949
6950
6951             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6952
6953             tooltip
6954                 .style('top', pos[1] + 'px')
6955                 .style('left', pos[0] + 'px')
6956                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6957                 .select('.tooltip-inner')
6958                     .html(html);
6959
6960         } else {
6961             tooltip.call(iD.ui.Toggle(false));
6962         }
6963     };
6964
6965     curtain.cut = function(datum, duration) {
6966         darkness.datum(datum);
6967
6968         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6969             .attr('d', function(d) {
6970                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6971                     window.innerWidth + "," + window.innerHeight + "L" +
6972                     window.innerWidth + ",0 Z";
6973
6974                 if (!d) return string;
6975                 return string + 'M' +
6976                     d.left + ',' + d.top + 'L' +
6977                     d.left + ',' + (d.top + d.height) + 'L' +
6978                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6979                     (d.left + d.width) + ',' + (d.top) + 'Z';
6980
6981             });
6982     };
6983
6984     curtain.remove = function() {
6985         surface.remove();
6986         tooltip.remove();
6987     };
6988
6989     return d3.rebind(curtain, event, 'on');
6990 };
6991 // Like selection.property('value', ...), but avoids no-op value sets,
6992 // which can result in layout/repaint thrashing in some situations.
6993 d3.selection.prototype.value = function(value) {
6994     function d3_selection_value(value) {
6995       function valueNull() {
6996         delete this.value;
6997       }
6998
6999       function valueConstant() {
7000         if (this.value !== value) this.value = value;
7001       }
7002
7003       function valueFunction() {
7004         var x = value.apply(this, arguments);
7005         if (x == null) delete this.value;
7006         else if (this.value !== x) this.value = x;
7007       }
7008
7009       return value == null
7010           ? valueNull : (typeof value === "function"
7011           ? valueFunction : valueConstant);
7012     }
7013
7014     if (!arguments.length) return this.property('value');
7015     return this.each(d3_selection_value(value));
7016 };
7017 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7018 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7019 //
7020 // Permission is hereby granted, free of charge, to any person
7021 // obtaining a copy of this software and associated documentation files
7022 // (the "Software"), to deal in the Software without restriction,
7023 // including without limitation the rights to use, copy, modify, merge,
7024 // publish, distribute, sublicense, and/or sell copies of the Software,
7025 // and to permit persons to whom the Software is furnished to do so,
7026 // subject to the following conditions:
7027 //
7028 // The above copyright notice and this permission notice shall be
7029 // included in all copies or substantial portions of the Software.
7030 //
7031 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7032 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7033 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7034 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7035 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7036 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7037 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7038 // SOFTWARE.
7039
7040 // source:  https://bitbucket.org/lshift/synchrotron/src
7041
7042 Diff3 = (function() {
7043     'use strict';
7044
7045     var diff3 = {
7046         longest_common_subsequence: function(file1, file2) {
7047             /* Text diff algorithm following Hunt and McIlroy 1976.
7048              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7049              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7050              * http://www.cs.dartmouth.edu/~doug/
7051              *
7052              * Expects two arrays of strings.
7053              */
7054             var equivalenceClasses;
7055             var file2indices;
7056             var newCandidate;
7057             var candidates;
7058             var line;
7059             var c, i, j, jX, r, s;
7060
7061             equivalenceClasses = {};
7062             for (j = 0; j < file2.length; j++) {
7063                 line = file2[j];
7064                 if (equivalenceClasses[line]) {
7065                     equivalenceClasses[line].push(j);
7066                 } else {
7067                     equivalenceClasses[line] = [j];
7068                 }
7069             }
7070
7071             candidates = [{file1index: -1,
7072                            file2index: -1,
7073                            chain: null}];
7074
7075             for (i = 0; i < file1.length; i++) {
7076                 line = file1[i];
7077                 file2indices = equivalenceClasses[line] || [];
7078
7079                 r = 0;
7080                 c = candidates[0];
7081
7082                 for (jX = 0; jX < file2indices.length; jX++) {
7083                     j = file2indices[jX];
7084
7085                     for (s = 0; s < candidates.length; s++) {
7086                         if ((candidates[s].file2index < j) &&
7087                             ((s == candidates.length - 1) ||
7088                              (candidates[s + 1].file2index > j)))
7089                             break;
7090                     }
7091
7092                     if (s < candidates.length) {
7093                         newCandidate = {file1index: i,
7094                                         file2index: j,
7095                                         chain: candidates[s]};
7096                         if (r == candidates.length) {
7097                             candidates.push(c);
7098                         } else {
7099                             candidates[r] = c;
7100                         }
7101                         r = s + 1;
7102                         c = newCandidate;
7103                         if (r == candidates.length) {
7104                             break; // no point in examining further (j)s
7105                         }
7106                     }
7107                 }
7108
7109                 candidates[r] = c;
7110             }
7111
7112             // At this point, we know the LCS: it's in the reverse of the
7113             // linked-list through .chain of
7114             // candidates[candidates.length - 1].
7115
7116             return candidates[candidates.length - 1];
7117         },
7118
7119         diff_comm: function(file1, file2) {
7120             // We apply the LCS to build a "comm"-style picture of the
7121             // differences between file1 and file2.
7122
7123             var result = [];
7124             var tail1 = file1.length;
7125             var tail2 = file2.length;
7126             var common = {common: []};
7127
7128             function processCommon() {
7129                 if (common.common.length) {
7130                     common.common.reverse();
7131                     result.push(common);
7132                     common = {common: []};
7133                 }
7134             }
7135
7136             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7137                  candidate !== null;
7138                  candidate = candidate.chain)
7139             {
7140                 var different = {file1: [], file2: []};
7141
7142                 while (--tail1 > candidate.file1index) {
7143                     different.file1.push(file1[tail1]);
7144                 }
7145
7146                 while (--tail2 > candidate.file2index) {
7147                     different.file2.push(file2[tail2]);
7148                 }
7149
7150                 if (different.file1.length || different.file2.length) {
7151                     processCommon();
7152                     different.file1.reverse();
7153                     different.file2.reverse();
7154                     result.push(different);
7155                 }
7156
7157                 if (tail1 >= 0) {
7158                     common.common.push(file1[tail1]);
7159                 }
7160             }
7161
7162             processCommon();
7163
7164             result.reverse();
7165             return result;
7166         },
7167
7168         diff_patch: function(file1, file2) {
7169             // We apply the LCD to build a JSON representation of a
7170             // diff(1)-style patch.
7171
7172             var result = [];
7173             var tail1 = file1.length;
7174             var tail2 = file2.length;
7175
7176             function chunkDescription(file, offset, length) {
7177                 var chunk = [];
7178                 for (var i = 0; i < length; i++) {
7179                     chunk.push(file[offset + i]);
7180                 }
7181                 return {offset: offset,
7182                         length: length,
7183                         chunk: chunk};
7184             }
7185
7186             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7187                  candidate !== null;
7188                  candidate = candidate.chain)
7189             {
7190                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7191                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7192                 tail1 = candidate.file1index;
7193                 tail2 = candidate.file2index;
7194
7195                 if (mismatchLength1 || mismatchLength2) {
7196                     result.push({file1: chunkDescription(file1,
7197                                                          candidate.file1index + 1,
7198                                                          mismatchLength1),
7199                                  file2: chunkDescription(file2,
7200                                                          candidate.file2index + 1,
7201                                                          mismatchLength2)});
7202                 }
7203             }
7204
7205             result.reverse();
7206             return result;
7207         },
7208
7209         strip_patch: function(patch) {
7210         // Takes the output of Diff3.diff_patch(), and removes
7211         // information from it. It can still be used by patch(),
7212         // below, but can no longer be inverted.
7213         var newpatch = [];
7214         for (var i = 0; i < patch.length; i++) {
7215             var chunk = patch[i];
7216             newpatch.push({file1: {offset: chunk.file1.offset,
7217                        length: chunk.file1.length},
7218                    file2: {chunk: chunk.file2.chunk}});
7219         }
7220         return newpatch;
7221         },
7222
7223         invert_patch: function(patch) {
7224             // Takes the output of Diff3.diff_patch(), and inverts the
7225             // sense of it, so that it can be applied to file2 to give
7226             // file1 rather than the other way around.
7227
7228             for (var i = 0; i < patch.length; i++) {
7229                 var chunk = patch[i];
7230                 var tmp = chunk.file1;
7231                 chunk.file1 = chunk.file2;
7232                 chunk.file2 = tmp;
7233             }
7234         },
7235
7236         patch: function (file, patch) {
7237             // Applies a patch to a file.
7238             //
7239             // Given file1 and file2, Diff3.patch(file1,
7240             // Diff3.diff_patch(file1, file2)) should give file2.
7241
7242             var result = [];
7243             var commonOffset = 0;
7244
7245             function copyCommon(targetOffset) {
7246                 while (commonOffset < targetOffset) {
7247                     result.push(file[commonOffset]);
7248                     commonOffset++;
7249                 }
7250             }
7251
7252             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7253                 var chunk = patch[chunkIndex];
7254                 copyCommon(chunk.file1.offset);
7255                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7256                     result.push(chunk.file2.chunk[lineIndex]);
7257                 }
7258                 commonOffset += chunk.file1.length;
7259             }
7260
7261             copyCommon(file.length);
7262             return result;
7263         },
7264
7265         diff_indices: function(file1, file2) {
7266             // We apply the LCS to give a simple representation of the
7267             // offsets and lengths of mismatched chunks in the input
7268             // files. This is used by diff3_merge_indices below.
7269
7270             var result = [];
7271             var tail1 = file1.length;
7272             var tail2 = file2.length;
7273
7274             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7275                  candidate !== null;
7276                  candidate = candidate.chain)
7277             {
7278                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7279                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7280                 tail1 = candidate.file1index;
7281                 tail2 = candidate.file2index;
7282
7283                 if (mismatchLength1 || mismatchLength2) {
7284                     result.push({file1: [tail1 + 1, mismatchLength1],
7285                                  file2: [tail2 + 1, mismatchLength2]});
7286                 }
7287             }
7288
7289             result.reverse();
7290             return result;
7291         },
7292
7293         diff3_merge_indices: function (a, o, b) {
7294             // Given three files, A, O, and B, where both A and B are
7295             // independently derived from O, returns a fairly complicated
7296             // internal representation of merge decisions it's taken. The
7297             // interested reader may wish to consult
7298             //
7299             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7300             // Formal Investigation of Diff3." In Arvind and Prasad,
7301             // editors, Foundations of Software Technology and Theoretical
7302             // Computer Science (FSTTCS), December 2007.
7303             //
7304             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7305             var i;
7306
7307             var m1 = Diff3.diff_indices(o, a);
7308             var m2 = Diff3.diff_indices(o, b);
7309
7310             var hunks = [];
7311             function addHunk(h, side) {
7312                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7313             }
7314             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7315             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7316             hunks.sort();
7317
7318             var result = [];
7319             var commonOffset = 0;
7320             function copyCommon(targetOffset) {
7321                 if (targetOffset > commonOffset) {
7322                     result.push([1, commonOffset, targetOffset - commonOffset]);
7323                     commonOffset = targetOffset;
7324                 }
7325             }
7326
7327             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7328                 var firstHunkIndex = hunkIndex;
7329                 var hunk = hunks[hunkIndex];
7330                 var regionLhs = hunk[0];
7331                 var regionRhs = regionLhs + hunk[2];
7332                 while (hunkIndex < hunks.length - 1) {
7333                     var maybeOverlapping = hunks[hunkIndex + 1];
7334                     var maybeLhs = maybeOverlapping[0];
7335                     if (maybeLhs > regionRhs) break;
7336                     regionRhs = maybeLhs + maybeOverlapping[2];
7337                     hunkIndex++;
7338                 }
7339
7340                 copyCommon(regionLhs);
7341                 if (firstHunkIndex == hunkIndex) {
7342             // The "overlap" was only one hunk long, meaning that
7343             // there's no conflict here. Either a and o were the
7344             // same, or b and o were the same.
7345                     if (hunk[4] > 0) {
7346                         result.push([hunk[1], hunk[3], hunk[4]]);
7347                     }
7348                 } else {
7349             // A proper conflict. Determine the extents of the
7350             // regions involved from a, o and b. Effectively merge
7351             // all the hunks on the left into one giant hunk, and
7352             // do the same for the right; then, correct for skew
7353             // in the regions of o that each side changed, and
7354             // report appropriate spans for the three sides.
7355             var regions = {
7356                 0: [a.length, -1, o.length, -1],
7357                 2: [b.length, -1, o.length, -1]
7358             };
7359                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7360                 hunk = hunks[i];
7361                         var side = hunk[1];
7362                 var r = regions[side];
7363                 var oLhs = hunk[0];
7364                 var oRhs = oLhs + hunk[2];
7365                         var abLhs = hunk[3];
7366                         var abRhs = abLhs + hunk[4];
7367                 r[0] = Math.min(abLhs, r[0]);
7368                 r[1] = Math.max(abRhs, r[1]);
7369                 r[2] = Math.min(oLhs, r[2]);
7370                 r[3] = Math.max(oRhs, r[3]);
7371                     }
7372             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7373             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7374             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7375             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7376                     result.push([-1,
7377                      aLhs,      aRhs      - aLhs,
7378                      regionLhs, regionRhs - regionLhs,
7379                      bLhs,      bRhs      - bLhs]);
7380                 }
7381                 commonOffset = regionRhs;
7382             }
7383
7384             copyCommon(o.length);
7385             return result;
7386         },
7387
7388         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7389             // Applies the output of Diff3.diff3_merge_indices to actually
7390             // construct the merged file; the returned result alternates
7391             // between "ok" and "conflict" blocks.
7392
7393             var result = [];
7394             var files = [a, o, b];
7395             var indices = Diff3.diff3_merge_indices(a, o, b);
7396
7397             var okLines = [];
7398             function flushOk() {
7399                 if (okLines.length) {
7400                     result.push({ok: okLines});
7401                 }
7402                 okLines = [];
7403             }
7404             function pushOk(xs) {
7405                 for (var j = 0; j < xs.length; j++) {
7406                     okLines.push(xs[j]);
7407                 }
7408             }
7409
7410             function isTrueConflict(rec) {
7411                 if (rec[2] != rec[6]) return true;
7412                 var aoff = rec[1];
7413                 var boff = rec[5];
7414                 for (var j = 0; j < rec[2]; j++) {
7415                     if (a[j + aoff] != b[j + boff]) return true;
7416                 }
7417                 return false;
7418             }
7419
7420             for (var i = 0; i < indices.length; i++) {
7421                 var x = indices[i];
7422                 var side = x[0];
7423                 if (side == -1) {
7424                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7425                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7426                     } else {
7427                         flushOk();
7428                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7429                                                 aIndex: x[1],
7430                                                 o: o.slice(x[3], x[3] + x[4]),
7431                                                 oIndex: x[3],
7432                                                 b: b.slice(x[5], x[5] + x[6]),
7433                                                 bIndex: x[5]}});
7434                     }
7435                 } else {
7436                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7437                 }
7438             }
7439
7440             flushOk();
7441             return result;
7442         }
7443     };
7444     return diff3;
7445 })();
7446
7447 if (typeof module !== 'undefined') module.exports = Diff3;
7448 var JXON = new (function () {
7449   var
7450     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7451     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7452
7453   function parseText (sValue) {
7454     if (rIsNull.test(sValue)) { return null; }
7455     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7456     if (isFinite(sValue)) { return parseFloat(sValue); }
7457     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7458     return sValue;
7459   }
7460
7461   function EmptyTree () { }
7462   EmptyTree.prototype.toString = function () { return "null"; };
7463   EmptyTree.prototype.valueOf = function () { return null; };
7464
7465   function objectify (vValue) {
7466     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7467   }
7468
7469   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7470     var
7471       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7472       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7473
7474     var
7475       sProp, vContent, nLength = 0, sCollectedTxt = "",
7476       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7477
7478     if (bChildren) {
7479       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7480         oNode = oParentNode.childNodes.item(nItem);
7481         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7482         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7483         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7484       }
7485     }
7486
7487     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7488
7489     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7490
7491     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7492       sProp = aCache[nElId].nodeName.toLowerCase();
7493       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7494       if (vResult.hasOwnProperty(sProp)) {
7495         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7496         vResult[sProp].push(vContent);
7497       } else {
7498         vResult[sProp] = vContent;
7499         nLength++;
7500       }
7501     }
7502
7503     if (bAttributes) {
7504       var
7505         nAttrLen = oParentNode.attributes.length,
7506         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7507
7508       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7509         oAttrib = oParentNode.attributes.item(nAttrib);
7510         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7511       }
7512
7513       if (bNesteAttr) {
7514         if (bFreeze) { Object.freeze(oAttrParent); }
7515         vResult[sAttributesProp] = oAttrParent;
7516         nLength -= nAttrLen - 1;
7517       }
7518     }
7519
7520     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7521       vResult[sValueProp] = vBuiltVal;
7522     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7523       vResult = vBuiltVal;
7524     }
7525
7526     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7527
7528     aCache.length = nLevelStart;
7529
7530     return vResult;
7531   }
7532
7533   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7534     var vValue, oChild;
7535
7536     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7537       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7538     } else if (oParentObj.constructor === Date) {
7539       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7540     }
7541
7542     for (var sName in oParentObj) {
7543       vValue = oParentObj[sName];
7544       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7545       if (sName === sValueProp) {
7546         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7547       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7548         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7549       } else if (sName.charAt(0) === sAttrPref) {
7550         oParentEl.setAttribute(sName.slice(1), vValue);
7551       } else if (vValue.constructor === Array) {
7552         for (var nItem = 0; nItem < vValue.length; nItem++) {
7553           oChild = oXMLDoc.createElement(sName);
7554           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7555           oParentEl.appendChild(oChild);
7556         }
7557       } else {
7558         oChild = oXMLDoc.createElement(sName);
7559         if (vValue instanceof Object) {
7560           loadObjTree(oXMLDoc, oChild, vValue);
7561         } else if (vValue !== null && vValue !== true) {
7562           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7563         }
7564         oParentEl.appendChild(oChild);
7565      }
7566    }
7567   }
7568
7569   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7570     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7571     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7572   };
7573
7574   this.unbuild = function (oObjTree) {    
7575     var oNewDoc = document.implementation.createDocument("", "", null);
7576     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7577     return oNewDoc;
7578   };
7579
7580   this.stringify = function (oObjTree) {
7581     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7582   };
7583 })();
7584 // var myObject = JXON.build(doc);
7585 // we got our javascript object! try: alert(JSON.stringify(myObject));
7586
7587 // var newDoc = JXON.unbuild(myObject);
7588 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7589 /**
7590  * @license
7591  * lodash (Custom Build) <https://lodash.com/>
7592  * 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"`
7593  * Copyright jQuery Foundation and other contributors <https://jquery.org/>
7594  * Released under MIT license <https://lodash.com/license>
7595  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7596  * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7597  */
7598 ;(function() {
7599
7600   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7601   var undefined;
7602
7603   /** Used as the semantic version number. */
7604   var VERSION = '4.12.0';
7605
7606   /** Used as the size to enable large array optimizations. */
7607   var LARGE_ARRAY_SIZE = 200;
7608
7609   /** Used as the `TypeError` message for "Functions" methods. */
7610   var FUNC_ERROR_TEXT = 'Expected a function';
7611
7612   /** Used to stand-in for `undefined` hash values. */
7613   var HASH_UNDEFINED = '__lodash_hash_undefined__';
7614
7615   /** Used as the internal argument placeholder. */
7616   var PLACEHOLDER = '__lodash_placeholder__';
7617
7618   /** Used to compose bitmasks for wrapper metadata. */
7619   var BIND_FLAG = 1,
7620       BIND_KEY_FLAG = 2,
7621       CURRY_BOUND_FLAG = 4,
7622       CURRY_FLAG = 8,
7623       CURRY_RIGHT_FLAG = 16,
7624       PARTIAL_FLAG = 32,
7625       PARTIAL_RIGHT_FLAG = 64,
7626       ARY_FLAG = 128,
7627       REARG_FLAG = 256,
7628       FLIP_FLAG = 512;
7629
7630   /** Used to compose bitmasks for comparison styles. */
7631   var UNORDERED_COMPARE_FLAG = 1,
7632       PARTIAL_COMPARE_FLAG = 2;
7633
7634   /** Used to detect hot functions by number of calls within a span of milliseconds. */
7635   var HOT_COUNT = 150,
7636       HOT_SPAN = 16;
7637
7638   /** Used to indicate the type of lazy iteratees. */
7639   var LAZY_FILTER_FLAG = 1,
7640       LAZY_MAP_FLAG = 2,
7641       LAZY_WHILE_FLAG = 3;
7642
7643   /** Used as references for various `Number` constants. */
7644   var INFINITY = 1 / 0,
7645       MAX_SAFE_INTEGER = 9007199254740991,
7646       MAX_INTEGER = 1.7976931348623157e+308,
7647       NAN = 0 / 0;
7648
7649   /** Used as references for the maximum length and index of an array. */
7650   var MAX_ARRAY_LENGTH = 4294967295;
7651
7652   /** `Object#toString` result references. */
7653   var argsTag = '[object Arguments]',
7654       arrayTag = '[object Array]',
7655       boolTag = '[object Boolean]',
7656       dateTag = '[object Date]',
7657       errorTag = '[object Error]',
7658       funcTag = '[object Function]',
7659       genTag = '[object GeneratorFunction]',
7660       mapTag = '[object Map]',
7661       numberTag = '[object Number]',
7662       objectTag = '[object Object]',
7663       promiseTag = '[object Promise]',
7664       regexpTag = '[object RegExp]',
7665       setTag = '[object Set]',
7666       stringTag = '[object String]',
7667       symbolTag = '[object Symbol]',
7668       weakMapTag = '[object WeakMap]';
7669
7670   var arrayBufferTag = '[object ArrayBuffer]',
7671       dataViewTag = '[object DataView]',
7672       float32Tag = '[object Float32Array]',
7673       float64Tag = '[object Float64Array]',
7674       int8Tag = '[object Int8Array]',
7675       int16Tag = '[object Int16Array]',
7676       int32Tag = '[object Int32Array]',
7677       uint8Tag = '[object Uint8Array]',
7678       uint8ClampedTag = '[object Uint8ClampedArray]',
7679       uint16Tag = '[object Uint16Array]',
7680       uint32Tag = '[object Uint32Array]';
7681
7682   /** Used to match property names within property paths. */
7683   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
7684       reIsPlainProp = /^\w*$/,
7685       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g;
7686
7687   /**
7688    * Used to match `RegExp`
7689    * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns).
7690    */
7691   var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
7692
7693   /** Used to match leading and trailing whitespace. */
7694   var reTrim = /^\s+|\s+$/g;
7695
7696   /** Used to match backslashes in property paths. */
7697   var reEscapeChar = /\\(\\)?/g;
7698
7699   /** Used to match `RegExp` flags from their coerced string values. */
7700   var reFlags = /\w*$/;
7701
7702   /** Used to detect bad signed hexadecimal string values. */
7703   var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
7704
7705   /** Used to detect binary string values. */
7706   var reIsBinary = /^0b[01]+$/i;
7707
7708   /** Used to detect host constructors (Safari). */
7709   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7710
7711   /** Used to detect octal string values. */
7712   var reIsOctal = /^0o[0-7]+$/i;
7713
7714   /** Used to detect unsigned integer values. */
7715   var reIsUint = /^(?:0|[1-9]\d*)$/;
7716
7717   /** Used to compose unicode character classes. */
7718   var rsAstralRange = '\\ud800-\\udfff',
7719       rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
7720       rsComboSymbolsRange = '\\u20d0-\\u20f0',
7721       rsVarRange = '\\ufe0e\\ufe0f';
7722
7723   /** Used to compose unicode capture groups. */
7724   var rsAstral = '[' + rsAstralRange + ']',
7725       rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
7726       rsFitz = '\\ud83c[\\udffb-\\udfff]',
7727       rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
7728       rsNonAstral = '[^' + rsAstralRange + ']',
7729       rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
7730       rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
7731       rsZWJ = '\\u200d';
7732
7733   /** Used to compose unicode regexes. */
7734   var reOptMod = rsModifier + '?',
7735       rsOptVar = '[' + rsVarRange + ']?',
7736       rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
7737       rsSeq = rsOptVar + reOptMod + rsOptJoin,
7738       rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
7739
7740   /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
7741   var reComplexSymbol = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
7742
7743   /** Used to identify `toStringTag` values of typed arrays. */
7744   var typedArrayTags = {};
7745   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7746   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7747   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7748   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7749   typedArrayTags[uint32Tag] = true;
7750   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7751   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7752   typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
7753   typedArrayTags[errorTag] = typedArrayTags[funcTag] =
7754   typedArrayTags[mapTag] = typedArrayTags[numberTag] =
7755   typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
7756   typedArrayTags[setTag] = typedArrayTags[stringTag] =
7757   typedArrayTags[weakMapTag] = false;
7758
7759   /** Used to identify `toStringTag` values supported by `_.clone`. */
7760   var cloneableTags = {};
7761   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7762   cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
7763   cloneableTags[boolTag] = cloneableTags[dateTag] =
7764   cloneableTags[float32Tag] = cloneableTags[float64Tag] =
7765   cloneableTags[int8Tag] = cloneableTags[int16Tag] =
7766   cloneableTags[int32Tag] = cloneableTags[mapTag] =
7767   cloneableTags[numberTag] = cloneableTags[objectTag] =
7768   cloneableTags[regexpTag] = cloneableTags[setTag] =
7769   cloneableTags[stringTag] = cloneableTags[symbolTag] =
7770   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7771   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7772   cloneableTags[errorTag] = cloneableTags[funcTag] =
7773   cloneableTags[weakMapTag] = false;
7774
7775   /** Used to determine if values are of the language type `Object`. */
7776   var objectTypes = {
7777     'function': true,
7778     'object': true
7779   };
7780
7781   /** Built-in method references without a dependency on `root`. */
7782   var freeParseInt = parseInt;
7783
7784   /** Detect free variable `exports`. */
7785   var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)
7786     ? exports
7787     : undefined;
7788
7789   /** Detect free variable `module`. */
7790   var freeModule = (objectTypes[typeof module] && module && !module.nodeType)
7791     ? module
7792     : undefined;
7793
7794   /** Detect the popular CommonJS extension `module.exports`. */
7795   var moduleExports = (freeModule && freeModule.exports === freeExports)
7796     ? freeExports
7797     : undefined;
7798
7799   /** Detect free variable `global` from Node.js. */
7800   var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);
7801
7802   /** Detect free variable `self`. */
7803   var freeSelf = checkGlobal(objectTypes[typeof self] && self);
7804
7805   /** Detect free variable `window`. */
7806   var freeWindow = checkGlobal(objectTypes[typeof window] && window);
7807
7808   /** Detect `this` as the global object. */
7809   var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
7810
7811   /**
7812    * Used as a reference to the global object.
7813    *
7814    * The `this` value is used if it's the global object to avoid Greasemonkey's
7815    * restricted `window` object, otherwise the `window` object is used.
7816    */
7817   var root = freeGlobal ||
7818     ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||
7819       freeSelf || thisGlobal || Function('return this')();
7820
7821   /*--------------------------------------------------------------------------*/
7822
7823   /**
7824    * Adds the key-value `pair` to `map`.
7825    *
7826    * @private
7827    * @param {Object} map The map to modify.
7828    * @param {Array} pair The key-value pair to add.
7829    * @returns {Object} Returns `map`.
7830    */
7831   function addMapEntry(map, pair) {
7832     // Don't return `Map#set` because it doesn't return the map instance in IE 11.
7833     map.set(pair[0], pair[1]);
7834     return map;
7835   }
7836
7837   /**
7838    * Adds `value` to `set`.
7839    *
7840    * @private
7841    * @param {Object} set The set to modify.
7842    * @param {*} value The value to add.
7843    * @returns {Object} Returns `set`.
7844    */
7845   function addSetEntry(set, value) {
7846     set.add(value);
7847     return set;
7848   }
7849
7850   /**
7851    * A faster alternative to `Function#apply`, this function invokes `func`
7852    * with the `this` binding of `thisArg` and the arguments of `args`.
7853    *
7854    * @private
7855    * @param {Function} func The function to invoke.
7856    * @param {*} thisArg The `this` binding of `func`.
7857    * @param {Array} args The arguments to invoke `func` with.
7858    * @returns {*} Returns the result of `func`.
7859    */
7860   function apply(func, thisArg, args) {
7861     var length = args.length;
7862     switch (length) {
7863       case 0: return func.call(thisArg);
7864       case 1: return func.call(thisArg, args[0]);
7865       case 2: return func.call(thisArg, args[0], args[1]);
7866       case 3: return func.call(thisArg, args[0], args[1], args[2]);
7867     }
7868     return func.apply(thisArg, args);
7869   }
7870
7871   /**
7872    * A specialized version of `baseAggregator` for arrays.
7873    *
7874    * @private
7875    * @param {Array} array The array to iterate over.
7876    * @param {Function} setter The function to set `accumulator` values.
7877    * @param {Function} iteratee The iteratee to transform keys.
7878    * @param {Object} accumulator The initial aggregated object.
7879    * @returns {Function} Returns `accumulator`.
7880    */
7881   function arrayAggregator(array, setter, iteratee, accumulator) {
7882     var index = -1,
7883         length = array.length;
7884
7885     while (++index < length) {
7886       var value = array[index];
7887       setter(accumulator, value, iteratee(value), array);
7888     }
7889     return accumulator;
7890   }
7891
7892   /**
7893    * A specialized version of `_.forEach` for arrays without support for
7894    * iteratee shorthands.
7895    *
7896    * @private
7897    * @param {Array} array The array to iterate over.
7898    * @param {Function} iteratee The function invoked per iteration.
7899    * @returns {Array} Returns `array`.
7900    */
7901   function arrayEach(array, iteratee) {
7902     var index = -1,
7903         length = array.length;
7904
7905     while (++index < length) {
7906       if (iteratee(array[index], index, array) === false) {
7907         break;
7908       }
7909     }
7910     return array;
7911   }
7912
7913   /**
7914    * A specialized version of `_.every` for arrays without support for
7915    * iteratee shorthands.
7916    *
7917    * @private
7918    * @param {Array} array The array to iterate over.
7919    * @param {Function} predicate The function invoked per iteration.
7920    * @returns {boolean} Returns `true` if all elements pass the predicate check,
7921    *  else `false`.
7922    */
7923   function arrayEvery(array, predicate) {
7924     var index = -1,
7925         length = array.length;
7926
7927     while (++index < length) {
7928       if (!predicate(array[index], index, array)) {
7929         return false;
7930       }
7931     }
7932     return true;
7933   }
7934
7935   /**
7936    * A specialized version of `_.filter` for arrays without support for
7937    * iteratee shorthands.
7938    *
7939    * @private
7940    * @param {Array} array The array to iterate over.
7941    * @param {Function} predicate The function invoked per iteration.
7942    * @returns {Array} Returns the new filtered array.
7943    */
7944   function arrayFilter(array, predicate) {
7945     var index = -1,
7946         length = array.length,
7947         resIndex = 0,
7948         result = [];
7949
7950     while (++index < length) {
7951       var value = array[index];
7952       if (predicate(value, index, array)) {
7953         result[resIndex++] = value;
7954       }
7955     }
7956     return result;
7957   }
7958
7959   /**
7960    * A specialized version of `_.includes` for arrays without support for
7961    * specifying an index to search from.
7962    *
7963    * @private
7964    * @param {Array} array The array to search.
7965    * @param {*} target The value to search for.
7966    * @returns {boolean} Returns `true` if `target` is found, else `false`.
7967    */
7968   function arrayIncludes(array, value) {
7969     return !!array.length && baseIndexOf(array, value, 0) > -1;
7970   }
7971
7972   /**
7973    * This function is like `arrayIncludes` except that it accepts a comparator.
7974    *
7975    * @private
7976    * @param {Array} array The array to search.
7977    * @param {*} target The value to search for.
7978    * @param {Function} comparator The comparator invoked per element.
7979    * @returns {boolean} Returns `true` if `target` is found, else `false`.
7980    */
7981   function arrayIncludesWith(array, value, comparator) {
7982     var index = -1,
7983         length = array.length;
7984
7985     while (++index < length) {
7986       if (comparator(value, array[index])) {
7987         return true;
7988       }
7989     }
7990     return false;
7991   }
7992
7993   /**
7994    * A specialized version of `_.map` for arrays without support for iteratee
7995    * shorthands.
7996    *
7997    * @private
7998    * @param {Array} array The array to iterate over.
7999    * @param {Function} iteratee The function invoked per iteration.
8000    * @returns {Array} Returns the new mapped array.
8001    */
8002   function arrayMap(array, iteratee) {
8003     var index = -1,
8004         length = array.length,
8005         result = Array(length);
8006
8007     while (++index < length) {
8008       result[index] = iteratee(array[index], index, array);
8009     }
8010     return result;
8011   }
8012
8013   /**
8014    * Appends the elements of `values` to `array`.
8015    *
8016    * @private
8017    * @param {Array} array The array to modify.
8018    * @param {Array} values The values to append.
8019    * @returns {Array} Returns `array`.
8020    */
8021   function arrayPush(array, values) {
8022     var index = -1,
8023         length = values.length,
8024         offset = array.length;
8025
8026     while (++index < length) {
8027       array[offset + index] = values[index];
8028     }
8029     return array;
8030   }
8031
8032   /**
8033    * A specialized version of `_.reduce` for arrays without support for
8034    * iteratee shorthands.
8035    *
8036    * @private
8037    * @param {Array} array The array to iterate over.
8038    * @param {Function} iteratee The function invoked per iteration.
8039    * @param {*} [accumulator] The initial value.
8040    * @param {boolean} [initAccum] Specify using the first element of `array` as
8041    *  the initial value.
8042    * @returns {*} Returns the accumulated value.
8043    */
8044   function arrayReduce(array, iteratee, accumulator, initAccum) {
8045     var index = -1,
8046         length = array.length;
8047
8048     if (initAccum && length) {
8049       accumulator = array[++index];
8050     }
8051     while (++index < length) {
8052       accumulator = iteratee(accumulator, array[index], index, array);
8053     }
8054     return accumulator;
8055   }
8056
8057   /**
8058    * A specialized version of `_.some` for arrays without support for iteratee
8059    * shorthands.
8060    *
8061    * @private
8062    * @param {Array} array The array to iterate over.
8063    * @param {Function} predicate The function invoked per iteration.
8064    * @returns {boolean} Returns `true` if any element passes the predicate check,
8065    *  else `false`.
8066    */
8067   function arraySome(array, predicate) {
8068     var index = -1,
8069         length = array.length;
8070
8071     while (++index < length) {
8072       if (predicate(array[index], index, array)) {
8073         return true;
8074       }
8075     }
8076     return false;
8077   }
8078
8079   /**
8080    * The base implementation of methods like `_.find` and `_.findKey`, without
8081    * support for iteratee shorthands, which iterates over `collection` using
8082    * `eachFunc`.
8083    *
8084    * @private
8085    * @param {Array|Object} collection The collection to search.
8086    * @param {Function} predicate The function invoked per iteration.
8087    * @param {Function} eachFunc The function to iterate over `collection`.
8088    * @param {boolean} [retKey] Specify returning the key of the found element
8089    *  instead of the element itself.
8090    * @returns {*} Returns the found element or its key, else `undefined`.
8091    */
8092   function baseFind(collection, predicate, eachFunc, retKey) {
8093     var result;
8094     eachFunc(collection, function(value, key, collection) {
8095       if (predicate(value, key, collection)) {
8096         result = retKey ? key : value;
8097         return false;
8098       }
8099     });
8100     return result;
8101   }
8102
8103   /**
8104    * The base implementation of `_.findIndex` and `_.findLastIndex` without
8105    * support for iteratee shorthands.
8106    *
8107    * @private
8108    * @param {Array} array The array to search.
8109    * @param {Function} predicate The function invoked per iteration.
8110    * @param {boolean} [fromRight] Specify iterating from right to left.
8111    * @returns {number} Returns the index of the matched value, else `-1`.
8112    */
8113   function baseFindIndex(array, predicate, fromRight) {
8114     var length = array.length,
8115         index = fromRight ? length : -1;
8116
8117     while ((fromRight ? index-- : ++index < length)) {
8118       if (predicate(array[index], index, array)) {
8119         return index;
8120       }
8121     }
8122     return -1;
8123   }
8124
8125   /**
8126    * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
8127    *
8128    * @private
8129    * @param {Array} array The array to search.
8130    * @param {*} value The value to search for.
8131    * @param {number} fromIndex The index to search from.
8132    * @returns {number} Returns the index of the matched value, else `-1`.
8133    */
8134   function baseIndexOf(array, value, fromIndex) {
8135     if (value !== value) {
8136       return indexOfNaN(array, fromIndex);
8137     }
8138     var index = fromIndex - 1,
8139         length = array.length;
8140
8141     while (++index < length) {
8142       if (array[index] === value) {
8143         return index;
8144       }
8145     }
8146     return -1;
8147   }
8148
8149   /**
8150    * The base implementation of `_.reduce` and `_.reduceRight`, without support
8151    * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
8152    *
8153    * @private
8154    * @param {Array|Object} collection The collection to iterate over.
8155    * @param {Function} iteratee The function invoked per iteration.
8156    * @param {*} accumulator The initial value.
8157    * @param {boolean} initAccum Specify using the first or last element of
8158    *  `collection` as the initial value.
8159    * @param {Function} eachFunc The function to iterate over `collection`.
8160    * @returns {*} Returns the accumulated value.
8161    */
8162   function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
8163     eachFunc(collection, function(value, index, collection) {
8164       accumulator = initAccum
8165         ? (initAccum = false, value)
8166         : iteratee(accumulator, value, index, collection);
8167     });
8168     return accumulator;
8169   }
8170
8171   /**
8172    * The base implementation of `_.times` without support for iteratee shorthands
8173    * or max array length checks.
8174    *
8175    * @private
8176    * @param {number} n The number of times to invoke `iteratee`.
8177    * @param {Function} iteratee The function invoked per iteration.
8178    * @returns {Array} Returns the array of results.
8179    */
8180   function baseTimes(n, iteratee) {
8181     var index = -1,
8182         result = Array(n);
8183
8184     while (++index < n) {
8185       result[index] = iteratee(index);
8186     }
8187     return result;
8188   }
8189
8190   /**
8191    * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
8192    * of key-value pairs for `object` corresponding to the property names of `props`.
8193    *
8194    * @private
8195    * @param {Object} object The object to query.
8196    * @param {Array} props The property names to get values for.
8197    * @returns {Object} Returns the key-value pairs.
8198    */
8199   function baseToPairs(object, props) {
8200     return arrayMap(props, function(key) {
8201       return [key, object[key]];
8202     });
8203   }
8204
8205   /**
8206    * The base implementation of `_.unary` without support for storing wrapper metadata.
8207    *
8208    * @private
8209    * @param {Function} func The function to cap arguments for.
8210    * @returns {Function} Returns the new capped function.
8211    */
8212   function baseUnary(func) {
8213     return function(value) {
8214       return func(value);
8215     };
8216   }
8217
8218   /**
8219    * The base implementation of `_.values` and `_.valuesIn` which creates an
8220    * array of `object` property values corresponding to the property names
8221    * of `props`.
8222    *
8223    * @private
8224    * @param {Object} object The object to query.
8225    * @param {Array} props The property names to get values for.
8226    * @returns {Object} Returns the array of property values.
8227    */
8228   function baseValues(object, props) {
8229     return arrayMap(props, function(key) {
8230       return object[key];
8231     });
8232   }
8233
8234   /**
8235    * Checks if a cache value for `key` exists.
8236    *
8237    * @private
8238    * @param {Object} cache The cache to query.
8239    * @param {string} key The key of the entry to check.
8240    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8241    */
8242   function cacheHas(cache, key) {
8243     return cache.has(key);
8244   }
8245
8246   /**
8247    * Checks if `value` is a global object.
8248    *
8249    * @private
8250    * @param {*} value The value to check.
8251    * @returns {null|Object} Returns `value` if it's a global object, else `null`.
8252    */
8253   function checkGlobal(value) {
8254     return (value && value.Object === Object) ? value : null;
8255   }
8256
8257   /**
8258    * Gets the number of `placeholder` occurrences in `array`.
8259    *
8260    * @private
8261    * @param {Array} array The array to inspect.
8262    * @param {*} placeholder The placeholder to search for.
8263    * @returns {number} Returns the placeholder count.
8264    */
8265   function countHolders(array, placeholder) {
8266     var length = array.length,
8267         result = 0;
8268
8269     while (length--) {
8270       if (array[length] === placeholder) {
8271         result++;
8272       }
8273     }
8274     return result;
8275   }
8276
8277   /**
8278    * Gets the index at which the first occurrence of `NaN` is found in `array`.
8279    *
8280    * @private
8281    * @param {Array} array The array to search.
8282    * @param {number} fromIndex The index to search from.
8283    * @param {boolean} [fromRight] Specify iterating from right to left.
8284    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
8285    */
8286   function indexOfNaN(array, fromIndex, fromRight) {
8287     var length = array.length,
8288         index = fromIndex + (fromRight ? 0 : -1);
8289
8290     while ((fromRight ? index-- : ++index < length)) {
8291       var other = array[index];
8292       if (other !== other) {
8293         return index;
8294       }
8295     }
8296     return -1;
8297   }
8298
8299   /**
8300    * Checks if `value` is a host object in IE < 9.
8301    *
8302    * @private
8303    * @param {*} value The value to check.
8304    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
8305    */
8306   function isHostObject(value) {
8307     // Many host objects are `Object` objects that can coerce to strings
8308     // despite having improperly defined `toString` methods.
8309     var result = false;
8310     if (value != null && typeof value.toString != 'function') {
8311       try {
8312         result = !!(value + '');
8313       } catch (e) {}
8314     }
8315     return result;
8316   }
8317
8318   /**
8319    * Converts `iterator` to an array.
8320    *
8321    * @private
8322    * @param {Object} iterator The iterator to convert.
8323    * @returns {Array} Returns the converted array.
8324    */
8325   function iteratorToArray(iterator) {
8326     var data,
8327         result = [];
8328
8329     while (!(data = iterator.next()).done) {
8330       result.push(data.value);
8331     }
8332     return result;
8333   }
8334
8335   /**
8336    * Converts `map` to its key-value pairs.
8337    *
8338    * @private
8339    * @param {Object} map The map to convert.
8340    * @returns {Array} Returns the key-value pairs.
8341    */
8342   function mapToArray(map) {
8343     var index = -1,
8344         result = Array(map.size);
8345
8346     map.forEach(function(value, key) {
8347       result[++index] = [key, value];
8348     });
8349     return result;
8350   }
8351
8352   /**
8353    * Replaces all `placeholder` elements in `array` with an internal placeholder
8354    * and returns an array of their indexes.
8355    *
8356    * @private
8357    * @param {Array} array The array to modify.
8358    * @param {*} placeholder The placeholder to replace.
8359    * @returns {Array} Returns the new array of placeholder indexes.
8360    */
8361   function replaceHolders(array, placeholder) {
8362     var index = -1,
8363         length = array.length,
8364         resIndex = 0,
8365         result = [];
8366
8367     while (++index < length) {
8368       var value = array[index];
8369       if (value === placeholder || value === PLACEHOLDER) {
8370         array[index] = PLACEHOLDER;
8371         result[resIndex++] = index;
8372       }
8373     }
8374     return result;
8375   }
8376
8377   /**
8378    * Converts `set` to an array of its values.
8379    *
8380    * @private
8381    * @param {Object} set The set to convert.
8382    * @returns {Array} Returns the values.
8383    */
8384   function setToArray(set) {
8385     var index = -1,
8386         result = Array(set.size);
8387
8388     set.forEach(function(value) {
8389       result[++index] = value;
8390     });
8391     return result;
8392   }
8393
8394   /**
8395    * Converts `set` to its value-value pairs.
8396    *
8397    * @private
8398    * @param {Object} set The set to convert.
8399    * @returns {Array} Returns the value-value pairs.
8400    */
8401   function setToPairs(set) {
8402     var index = -1,
8403         result = Array(set.size);
8404
8405     set.forEach(function(value) {
8406       result[++index] = [value, value];
8407     });
8408     return result;
8409   }
8410
8411   /**
8412    * Converts `string` to an array.
8413    *
8414    * @private
8415    * @param {string} string The string to convert.
8416    * @returns {Array} Returns the converted array.
8417    */
8418   function stringToArray(string) {
8419     return string.match(reComplexSymbol);
8420   }
8421
8422   /*--------------------------------------------------------------------------*/
8423
8424   /** Used for built-in method references. */
8425   var arrayProto = Array.prototype,
8426       objectProto = Object.prototype;
8427
8428   /** Used to resolve the decompiled source of functions. */
8429   var funcToString = Function.prototype.toString;
8430
8431   /** Used to check objects for own properties. */
8432   var hasOwnProperty = objectProto.hasOwnProperty;
8433
8434   /** Used to infer the `Object` constructor. */
8435   var objectCtorString = funcToString.call(Object);
8436
8437   /**
8438    * Used to resolve the
8439    * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
8440    * of values.
8441    */
8442   var objectToString = objectProto.toString;
8443
8444   /** Used to detect if a method is native. */
8445   var reIsNative = RegExp('^' +
8446     funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
8447     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
8448   );
8449
8450   /** Built-in value references. */
8451   var Buffer = moduleExports ? root.Buffer : undefined,
8452       Reflect = root.Reflect,
8453       Symbol = root.Symbol,
8454       Uint8Array = root.Uint8Array,
8455       enumerate = Reflect ? Reflect.enumerate : undefined,
8456       getOwnPropertySymbols = Object.getOwnPropertySymbols,
8457       iteratorSymbol = typeof (iteratorSymbol = Symbol && Symbol.iterator) == 'symbol' ? iteratorSymbol : undefined,
8458       objectCreate = Object.create,
8459       propertyIsEnumerable = objectProto.propertyIsEnumerable,
8460       splice = arrayProto.splice;
8461
8462   /* Built-in method references for those with the same name as other `lodash` methods. */
8463   var nativeCeil = Math.ceil,
8464       nativeGetPrototype = Object.getPrototypeOf,
8465       nativeKeys = Object.keys,
8466       nativeMax = Math.max,
8467       nativeMin = Math.min,
8468       nativeReverse = arrayProto.reverse;
8469
8470   /* Built-in method references that are verified to be native. */
8471   var DataView = getNative(root, 'DataView'),
8472       Map = getNative(root, 'Map'),
8473       Promise = getNative(root, 'Promise'),
8474       Set = getNative(root, 'Set'),
8475       WeakMap = getNative(root, 'WeakMap'),
8476       nativeCreate = getNative(Object, 'create');
8477
8478   /** Used to store function metadata. */
8479   var metaMap = WeakMap && new WeakMap;
8480
8481   /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */
8482   var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');
8483
8484   /** Used to lookup unminified function names. */
8485   var realNames = {};
8486
8487   /** Used to detect maps, sets, and weakmaps. */
8488   var dataViewCtorString = toSource(DataView),
8489       mapCtorString = toSource(Map),
8490       promiseCtorString = toSource(Promise),
8491       setCtorString = toSource(Set),
8492       weakMapCtorString = toSource(WeakMap);
8493
8494   /** Used to convert symbols to primitives and strings. */
8495   var symbolProto = Symbol ? Symbol.prototype : undefined,
8496       symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
8497       symbolToString = symbolProto ? symbolProto.toString : undefined;
8498
8499   /*------------------------------------------------------------------------*/
8500
8501   /**
8502    * Creates a `lodash` object which wraps `value` to enable implicit method
8503    * chain sequences. Methods that operate on and return arrays, collections,
8504    * and functions can be chained together. Methods that retrieve a single value
8505    * or may return a primitive value will automatically end the chain sequence
8506    * and return the unwrapped value. Otherwise, the value must be unwrapped
8507    * with `_#value`.
8508    *
8509    * Explicit chain sequences, which must be unwrapped with `_#value`, may be
8510    * enabled using `_.chain`.
8511    *
8512    * The execution of chained methods is lazy, that is, it's deferred until
8513    * `_#value` is implicitly or explicitly called.
8514    *
8515    * Lazy evaluation allows several methods to support shortcut fusion.
8516    * Shortcut fusion is an optimization to merge iteratee calls; this avoids
8517    * the creation of intermediate arrays and can greatly reduce the number of
8518    * iteratee executions. Sections of a chain sequence qualify for shortcut
8519    * fusion if the section is applied to an array of at least `200` elements
8520    * and any iteratees accept only one argument. The heuristic for whether a
8521    * section qualifies for shortcut fusion is subject to change.
8522    *
8523    * Chaining is supported in custom builds as long as the `_#value` method is
8524    * directly or indirectly included in the build.
8525    *
8526    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8527    *
8528    * The wrapper `Array` methods are:
8529    * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
8530    *
8531    * The wrapper `String` methods are:
8532    * `replace` and `split`
8533    *
8534    * The wrapper methods that support shortcut fusion are:
8535    * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
8536    * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
8537    * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
8538    *
8539    * The chainable wrapper methods are:
8540    * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
8541    * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
8542    * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
8543    * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
8544    * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
8545    * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
8546    * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
8547    * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
8548    * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
8549    * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
8550    * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8551    * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
8552    * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
8553    * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
8554    * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
8555    * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
8556    * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
8557    * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
8558    * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
8559    * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
8560    * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
8561    * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
8562    * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
8563    * `zipObject`, `zipObjectDeep`, and `zipWith`
8564    *
8565    * The wrapper methods that are **not** chainable by default are:
8566    * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
8567    * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `deburr`, `divide`, `each`,
8568    * `eachRight`, `endsWith`, `eq`, `escape`, `escapeRegExp`, `every`, `find`,
8569    * `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `first`,
8570    * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
8571    * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,
8572    * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`,
8573    * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`,
8574    * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`,
8575    * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`,
8576    * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,
8577    * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,
8578    * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,
8579    * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`,
8580    * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`,
8581    * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`,
8582    * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,
8583    * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8584    * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`,
8585    * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`,
8586    * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`,
8587    * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,
8588    * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`
8589    *
8590    * @name _
8591    * @constructor
8592    * @category Seq
8593    * @param {*} value The value to wrap in a `lodash` instance.
8594    * @returns {Object} Returns the new `lodash` wrapper instance.
8595    * @example
8596    *
8597    * function square(n) {
8598    *   return n * n;
8599    * }
8600    *
8601    * var wrapped = _([1, 2, 3]);
8602    *
8603    * // Returns an unwrapped value.
8604    * wrapped.reduce(_.add);
8605    * // => 6
8606    *
8607    * // Returns a wrapped value.
8608    * var squares = wrapped.map(square);
8609    *
8610    * _.isArray(squares);
8611    * // => false
8612    *
8613    * _.isArray(squares.value());
8614    * // => true
8615    */
8616   function lodash(value) {
8617     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8618       if (value instanceof LodashWrapper) {
8619         return value;
8620       }
8621       if (hasOwnProperty.call(value, '__wrapped__')) {
8622         return wrapperClone(value);
8623       }
8624     }
8625     return new LodashWrapper(value);
8626   }
8627
8628   /**
8629    * The function whose prototype chain sequence wrappers inherit from.
8630    *
8631    * @private
8632    */
8633   function baseLodash() {
8634     // No operation performed.
8635   }
8636
8637   /**
8638    * The base constructor for creating `lodash` wrapper objects.
8639    *
8640    * @private
8641    * @param {*} value The value to wrap.
8642    * @param {boolean} [chainAll] Enable explicit method chain sequences.
8643    */
8644   function LodashWrapper(value, chainAll) {
8645     this.__wrapped__ = value;
8646     this.__actions__ = [];
8647     this.__chain__ = !!chainAll;
8648     this.__index__ = 0;
8649     this.__values__ = undefined;
8650   }
8651
8652   // Ensure wrappers are instances of `baseLodash`.
8653   lodash.prototype = baseLodash.prototype;
8654   lodash.prototype.constructor = lodash;
8655
8656   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
8657   LodashWrapper.prototype.constructor = LodashWrapper;
8658
8659   /*------------------------------------------------------------------------*/
8660
8661   /**
8662    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8663    *
8664    * @private
8665    * @constructor
8666    * @param {*} value The value to wrap.
8667    */
8668   function LazyWrapper(value) {
8669     this.__wrapped__ = value;
8670     this.__actions__ = [];
8671     this.__dir__ = 1;
8672     this.__filtered__ = false;
8673     this.__iteratees__ = [];
8674     this.__takeCount__ = MAX_ARRAY_LENGTH;
8675     this.__views__ = [];
8676   }
8677
8678   /**
8679    * Creates a clone of the lazy wrapper object.
8680    *
8681    * @private
8682    * @name clone
8683    * @memberOf LazyWrapper
8684    * @returns {Object} Returns the cloned `LazyWrapper` object.
8685    */
8686   function lazyClone() {
8687     var result = new LazyWrapper(this.__wrapped__);
8688     result.__actions__ = copyArray(this.__actions__);
8689     result.__dir__ = this.__dir__;
8690     result.__filtered__ = this.__filtered__;
8691     result.__iteratees__ = copyArray(this.__iteratees__);
8692     result.__takeCount__ = this.__takeCount__;
8693     result.__views__ = copyArray(this.__views__);
8694     return result;
8695   }
8696
8697   /**
8698    * Reverses the direction of lazy iteration.
8699    *
8700    * @private
8701    * @name reverse
8702    * @memberOf LazyWrapper
8703    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8704    */
8705   function lazyReverse() {
8706     if (this.__filtered__) {
8707       var result = new LazyWrapper(this);
8708       result.__dir__ = -1;
8709       result.__filtered__ = true;
8710     } else {
8711       result = this.clone();
8712       result.__dir__ *= -1;
8713     }
8714     return result;
8715   }
8716
8717   /**
8718    * Extracts the unwrapped value from its lazy wrapper.
8719    *
8720    * @private
8721    * @name value
8722    * @memberOf LazyWrapper
8723    * @returns {*} Returns the unwrapped value.
8724    */
8725   function lazyValue() {
8726     var array = this.__wrapped__.value(),
8727         dir = this.__dir__,
8728         isArr = isArray(array),
8729         isRight = dir < 0,
8730         arrLength = isArr ? array.length : 0,
8731         view = getView(0, arrLength, this.__views__),
8732         start = view.start,
8733         end = view.end,
8734         length = end - start,
8735         index = isRight ? end : (start - 1),
8736         iteratees = this.__iteratees__,
8737         iterLength = iteratees.length,
8738         resIndex = 0,
8739         takeCount = nativeMin(length, this.__takeCount__);
8740
8741     if (!isArr || arrLength < LARGE_ARRAY_SIZE ||
8742         (arrLength == length && takeCount == length)) {
8743       return baseWrapperValue(array, this.__actions__);
8744     }
8745     var result = [];
8746
8747     outer:
8748     while (length-- && resIndex < takeCount) {
8749       index += dir;
8750
8751       var iterIndex = -1,
8752           value = array[index];
8753
8754       while (++iterIndex < iterLength) {
8755         var data = iteratees[iterIndex],
8756             iteratee = data.iteratee,
8757             type = data.type,
8758             computed = iteratee(value);
8759
8760         if (type == LAZY_MAP_FLAG) {
8761           value = computed;
8762         } else if (!computed) {
8763           if (type == LAZY_FILTER_FLAG) {
8764             continue outer;
8765           } else {
8766             break outer;
8767           }
8768         }
8769       }
8770       result[resIndex++] = value;
8771     }
8772     return result;
8773   }
8774
8775   // Ensure `LazyWrapper` is an instance of `baseLodash`.
8776   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
8777   LazyWrapper.prototype.constructor = LazyWrapper;
8778
8779   /*------------------------------------------------------------------------*/
8780
8781   /**
8782    * Creates a hash object.
8783    *
8784    * @private
8785    * @constructor
8786    * @param {Array} [entries] The key-value pairs to cache.
8787    */
8788   function Hash(entries) {
8789     var index = -1,
8790         length = entries ? entries.length : 0;
8791
8792     this.clear();
8793     while (++index < length) {
8794       var entry = entries[index];
8795       this.set(entry[0], entry[1]);
8796     }
8797   }
8798
8799   /**
8800    * Removes all key-value entries from the hash.
8801    *
8802    * @private
8803    * @name clear
8804    * @memberOf Hash
8805    */
8806   function hashClear() {
8807     this.__data__ = nativeCreate ? nativeCreate(null) : {};
8808   }
8809
8810   /**
8811    * Removes `key` and its value from the hash.
8812    *
8813    * @private
8814    * @name delete
8815    * @memberOf Hash
8816    * @param {Object} hash The hash to modify.
8817    * @param {string} key The key of the value to remove.
8818    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8819    */
8820   function hashDelete(key) {
8821     return this.has(key) && delete this.__data__[key];
8822   }
8823
8824   /**
8825    * Gets the hash value for `key`.
8826    *
8827    * @private
8828    * @name get
8829    * @memberOf Hash
8830    * @param {string} key The key of the value to get.
8831    * @returns {*} Returns the entry value.
8832    */
8833   function hashGet(key) {
8834     var data = this.__data__;
8835     if (nativeCreate) {
8836       var result = data[key];
8837       return result === HASH_UNDEFINED ? undefined : result;
8838     }
8839     return hasOwnProperty.call(data, key) ? data[key] : undefined;
8840   }
8841
8842   /**
8843    * Checks if a hash value for `key` exists.
8844    *
8845    * @private
8846    * @name has
8847    * @memberOf Hash
8848    * @param {string} key The key of the entry to check.
8849    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8850    */
8851   function hashHas(key) {
8852     var data = this.__data__;
8853     return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
8854   }
8855
8856   /**
8857    * Sets the hash `key` to `value`.
8858    *
8859    * @private
8860    * @name set
8861    * @memberOf Hash
8862    * @param {string} key The key of the value to set.
8863    * @param {*} value The value to set.
8864    * @returns {Object} Returns the hash instance.
8865    */
8866   function hashSet(key, value) {
8867     var data = this.__data__;
8868     data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
8869     return this;
8870   }
8871
8872   // Add methods to `Hash`.
8873   Hash.prototype.clear = hashClear;
8874   Hash.prototype['delete'] = hashDelete;
8875   Hash.prototype.get = hashGet;
8876   Hash.prototype.has = hashHas;
8877   Hash.prototype.set = hashSet;
8878
8879   /*------------------------------------------------------------------------*/
8880
8881   /**
8882    * Creates an list cache object.
8883    *
8884    * @private
8885    * @constructor
8886    * @param {Array} [entries] The key-value pairs to cache.
8887    */
8888   function ListCache(entries) {
8889     var index = -1,
8890         length = entries ? entries.length : 0;
8891
8892     this.clear();
8893     while (++index < length) {
8894       var entry = entries[index];
8895       this.set(entry[0], entry[1]);
8896     }
8897   }
8898
8899   /**
8900    * Removes all key-value entries from the list cache.
8901    *
8902    * @private
8903    * @name clear
8904    * @memberOf ListCache
8905    */
8906   function listCacheClear() {
8907     this.__data__ = [];
8908   }
8909
8910   /**
8911    * Removes `key` and its value from the list cache.
8912    *
8913    * @private
8914    * @name delete
8915    * @memberOf ListCache
8916    * @param {string} key The key of the value to remove.
8917    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8918    */
8919   function listCacheDelete(key) {
8920     var data = this.__data__,
8921         index = assocIndexOf(data, key);
8922
8923     if (index < 0) {
8924       return false;
8925     }
8926     var lastIndex = data.length - 1;
8927     if (index == lastIndex) {
8928       data.pop();
8929     } else {
8930       splice.call(data, index, 1);
8931     }
8932     return true;
8933   }
8934
8935   /**
8936    * Gets the list cache value for `key`.
8937    *
8938    * @private
8939    * @name get
8940    * @memberOf ListCache
8941    * @param {string} key The key of the value to get.
8942    * @returns {*} Returns the entry value.
8943    */
8944   function listCacheGet(key) {
8945     var data = this.__data__,
8946         index = assocIndexOf(data, key);
8947
8948     return index < 0 ? undefined : data[index][1];
8949   }
8950
8951   /**
8952    * Checks if a list cache value for `key` exists.
8953    *
8954    * @private
8955    * @name has
8956    * @memberOf ListCache
8957    * @param {string} key The key of the entry to check.
8958    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8959    */
8960   function listCacheHas(key) {
8961     return assocIndexOf(this.__data__, key) > -1;
8962   }
8963
8964   /**
8965    * Sets the list cache `key` to `value`.
8966    *
8967    * @private
8968    * @name set
8969    * @memberOf ListCache
8970    * @param {string} key The key of the value to set.
8971    * @param {*} value The value to set.
8972    * @returns {Object} Returns the list cache instance.
8973    */
8974   function listCacheSet(key, value) {
8975     var data = this.__data__,
8976         index = assocIndexOf(data, key);
8977
8978     if (index < 0) {
8979       data.push([key, value]);
8980     } else {
8981       data[index][1] = value;
8982     }
8983     return this;
8984   }
8985
8986   // Add methods to `ListCache`.
8987   ListCache.prototype.clear = listCacheClear;
8988   ListCache.prototype['delete'] = listCacheDelete;
8989   ListCache.prototype.get = listCacheGet;
8990   ListCache.prototype.has = listCacheHas;
8991   ListCache.prototype.set = listCacheSet;
8992
8993   /*------------------------------------------------------------------------*/
8994
8995   /**
8996    * Creates a map cache object to store key-value pairs.
8997    *
8998    * @private
8999    * @constructor
9000    * @param {Array} [entries] The key-value pairs to cache.
9001    */
9002   function MapCache(entries) {
9003     var index = -1,
9004         length = entries ? entries.length : 0;
9005
9006     this.clear();
9007     while (++index < length) {
9008       var entry = entries[index];
9009       this.set(entry[0], entry[1]);
9010     }
9011   }
9012
9013   /**
9014    * Removes all key-value entries from the map.
9015    *
9016    * @private
9017    * @name clear
9018    * @memberOf MapCache
9019    */
9020   function mapCacheClear() {
9021     this.__data__ = {
9022       'hash': new Hash,
9023       'map': new (Map || ListCache),
9024       'string': new Hash
9025     };
9026   }
9027
9028   /**
9029    * Removes `key` and its value from the map.
9030    *
9031    * @private
9032    * @name delete
9033    * @memberOf MapCache
9034    * @param {string} key The key of the value to remove.
9035    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
9036    */
9037   function mapCacheDelete(key) {
9038     return getMapData(this, key)['delete'](key);
9039   }
9040
9041   /**
9042    * Gets the map value for `key`.
9043    *
9044    * @private
9045    * @name get
9046    * @memberOf MapCache
9047    * @param {string} key The key of the value to get.
9048    * @returns {*} Returns the entry value.
9049    */
9050   function mapCacheGet(key) {
9051     return getMapData(this, key).get(key);
9052   }
9053
9054   /**
9055    * Checks if a map value for `key` exists.
9056    *
9057    * @private
9058    * @name has
9059    * @memberOf MapCache
9060    * @param {string} key The key of the entry to check.
9061    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
9062    */
9063   function mapCacheHas(key) {
9064     return getMapData(this, key).has(key);
9065   }
9066
9067   /**
9068    * Sets the map `key` to `value`.
9069    *
9070    * @private
9071    * @name set
9072    * @memberOf MapCache
9073    * @param {string} key The key of the value to set.
9074    * @param {*} value The value to set.
9075    * @returns {Object} Returns the map cache instance.
9076    */
9077   function mapCacheSet(key, value) {
9078     getMapData(this, key).set(key, value);
9079     return this;
9080   }
9081
9082   // Add methods to `MapCache`.
9083   MapCache.prototype.clear = mapCacheClear;
9084   MapCache.prototype['delete'] = mapCacheDelete;
9085   MapCache.prototype.get = mapCacheGet;
9086   MapCache.prototype.has = mapCacheHas;
9087   MapCache.prototype.set = mapCacheSet;
9088
9089   /*------------------------------------------------------------------------*/
9090
9091   /**
9092    *
9093    * Creates an array cache object to store unique values.
9094    *
9095    * @private
9096    * @constructor
9097    * @param {Array} [values] The values to cache.
9098    */
9099   function SetCache(values) {
9100     var index = -1,
9101         length = values ? values.length : 0;
9102
9103     this.__data__ = new MapCache;
9104     while (++index < length) {
9105       this.add(values[index]);
9106     }
9107   }
9108
9109   /**
9110    * Adds `value` to the array cache.
9111    *
9112    * @private
9113    * @name add
9114    * @memberOf SetCache
9115    * @alias push
9116    * @param {*} value The value to cache.
9117    * @returns {Object} Returns the cache instance.
9118    */
9119   function setCacheAdd(value) {
9120     this.__data__.set(value, HASH_UNDEFINED);
9121     return this;
9122   }
9123
9124   /**
9125    * Checks if `value` is in the array cache.
9126    *
9127    * @private
9128    * @name has
9129    * @memberOf SetCache
9130    * @param {*} value The value to search for.
9131    * @returns {number} Returns `true` if `value` is found, else `false`.
9132    */
9133   function setCacheHas(value) {
9134     return this.__data__.has(value);
9135   }
9136
9137   // Add methods to `SetCache`.
9138   SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
9139   SetCache.prototype.has = setCacheHas;
9140
9141   /*------------------------------------------------------------------------*/
9142
9143   /**
9144    * Creates a stack cache object to store key-value pairs.
9145    *
9146    * @private
9147    * @constructor
9148    * @param {Array} [entries] The key-value pairs to cache.
9149    */
9150   function Stack(entries) {
9151     this.__data__ = new ListCache(entries);
9152   }
9153
9154   /**
9155    * Removes all key-value entries from the stack.
9156    *
9157    * @private
9158    * @name clear
9159    * @memberOf Stack
9160    */
9161   function stackClear() {
9162     this.__data__ = new ListCache;
9163   }
9164
9165   /**
9166    * Removes `key` and its value from the stack.
9167    *
9168    * @private
9169    * @name delete
9170    * @memberOf Stack
9171    * @param {string} key The key of the value to remove.
9172    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
9173    */
9174   function stackDelete(key) {
9175     return this.__data__['delete'](key);
9176   }
9177
9178   /**
9179    * Gets the stack value for `key`.
9180    *
9181    * @private
9182    * @name get
9183    * @memberOf Stack
9184    * @param {string} key The key of the value to get.
9185    * @returns {*} Returns the entry value.
9186    */
9187   function stackGet(key) {
9188     return this.__data__.get(key);
9189   }
9190
9191   /**
9192    * Checks if a stack value for `key` exists.
9193    *
9194    * @private
9195    * @name has
9196    * @memberOf Stack
9197    * @param {string} key The key of the entry to check.
9198    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
9199    */
9200   function stackHas(key) {
9201     return this.__data__.has(key);
9202   }
9203
9204   /**
9205    * Sets the stack `key` to `value`.
9206    *
9207    * @private
9208    * @name set
9209    * @memberOf Stack
9210    * @param {string} key The key of the value to set.
9211    * @param {*} value The value to set.
9212    * @returns {Object} Returns the stack cache instance.
9213    */
9214   function stackSet(key, value) {
9215     var cache = this.__data__;
9216     if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) {
9217       cache = this.__data__ = new MapCache(cache.__data__);
9218     }
9219     cache.set(key, value);
9220     return this;
9221   }
9222
9223   // Add methods to `Stack`.
9224   Stack.prototype.clear = stackClear;
9225   Stack.prototype['delete'] = stackDelete;
9226   Stack.prototype.get = stackGet;
9227   Stack.prototype.has = stackHas;
9228   Stack.prototype.set = stackSet;
9229
9230   /*------------------------------------------------------------------------*/
9231
9232   /**
9233    * This function is like `assignValue` except that it doesn't assign
9234    * `undefined` values.
9235    *
9236    * @private
9237    * @param {Object} object The object to modify.
9238    * @param {string} key The key of the property to assign.
9239    * @param {*} value The value to assign.
9240    */
9241   function assignMergeValue(object, key, value) {
9242     if ((value !== undefined && !eq(object[key], value)) ||
9243         (typeof key == 'number' && value === undefined && !(key in object))) {
9244       object[key] = value;
9245     }
9246   }
9247
9248   /**
9249    * Assigns `value` to `key` of `object` if the existing value is not equivalent
9250    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
9251    * for equality comparisons.
9252    *
9253    * @private
9254    * @param {Object} object The object to modify.
9255    * @param {string} key The key of the property to assign.
9256    * @param {*} value The value to assign.
9257    */
9258   function assignValue(object, key, value) {
9259     var objValue = object[key];
9260     if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
9261         (value === undefined && !(key in object))) {
9262       object[key] = value;
9263     }
9264   }
9265
9266   /**
9267    * Gets the index at which the `key` is found in `array` of key-value pairs.
9268    *
9269    * @private
9270    * @param {Array} array The array to search.
9271    * @param {*} key The key to search for.
9272    * @returns {number} Returns the index of the matched value, else `-1`.
9273    */
9274   function assocIndexOf(array, key) {
9275     var length = array.length;
9276     while (length--) {
9277       if (eq(array[length][0], key)) {
9278         return length;
9279       }
9280     }
9281     return -1;
9282   }
9283
9284   /**
9285    * Aggregates elements of `collection` on `accumulator` with keys transformed
9286    * by `iteratee` and values set by `setter`.
9287    *
9288    * @private
9289    * @param {Array|Object} collection The collection to iterate over.
9290    * @param {Function} setter The function to set `accumulator` values.
9291    * @param {Function} iteratee The iteratee to transform keys.
9292    * @param {Object} accumulator The initial aggregated object.
9293    * @returns {Function} Returns `accumulator`.
9294    */
9295   function baseAggregator(collection, setter, iteratee, accumulator) {
9296     baseEach(collection, function(value, key, collection) {
9297       setter(accumulator, value, iteratee(value), collection);
9298     });
9299     return accumulator;
9300   }
9301
9302   /**
9303    * The base implementation of `_.assign` without support for multiple sources
9304    * or `customizer` functions.
9305    *
9306    * @private
9307    * @param {Object} object The destination object.
9308    * @param {Object} source The source object.
9309    * @returns {Object} Returns `object`.
9310    */
9311   function baseAssign(object, source) {
9312     return object && copyObject(source, keys(source), object);
9313   }
9314
9315   /**
9316    * The base implementation of `_.at` without support for individual paths.
9317    *
9318    * @private
9319    * @param {Object} object The object to iterate over.
9320    * @param {string[]} paths The property paths of elements to pick.
9321    * @returns {Array} Returns the picked elements.
9322    */
9323   function baseAt(object, paths) {
9324     var index = -1,
9325         isNil = object == null,
9326         length = paths.length,
9327         result = Array(length);
9328
9329     while (++index < length) {
9330       result[index] = isNil ? undefined : get(object, paths[index]);
9331     }
9332     return result;
9333   }
9334
9335   /**
9336    * The base implementation of `_.clone` and `_.cloneDeep` which tracks
9337    * traversed objects.
9338    *
9339    * @private
9340    * @param {*} value The value to clone.
9341    * @param {boolean} [isDeep] Specify a deep clone.
9342    * @param {boolean} [isFull] Specify a clone including symbols.
9343    * @param {Function} [customizer] The function to customize cloning.
9344    * @param {string} [key] The key of `value`.
9345    * @param {Object} [object] The parent object of `value`.
9346    * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
9347    * @returns {*} Returns the cloned value.
9348    */
9349   function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
9350     var result;
9351     if (customizer) {
9352       result = object ? customizer(value, key, object, stack) : customizer(value);
9353     }
9354     if (result !== undefined) {
9355       return result;
9356     }
9357     if (!isObject(value)) {
9358       return value;
9359     }
9360     var isArr = isArray(value);
9361     if (isArr) {
9362       result = initCloneArray(value);
9363       if (!isDeep) {
9364         return copyArray(value, result);
9365       }
9366     } else {
9367       var tag = getTag(value),
9368           isFunc = tag == funcTag || tag == genTag;
9369
9370       if (isBuffer(value)) {
9371         return cloneBuffer(value, isDeep);
9372       }
9373       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
9374         if (isHostObject(value)) {
9375           return object ? value : {};
9376         }
9377         result = initCloneObject(isFunc ? {} : value);
9378         if (!isDeep) {
9379           return copySymbols(value, baseAssign(result, value));
9380         }
9381       } else {
9382         if (!cloneableTags[tag]) {
9383           return object ? value : {};
9384         }
9385         result = initCloneByTag(value, tag, baseClone, isDeep);
9386       }
9387     }
9388     // Check for circular references and return its corresponding clone.
9389     stack || (stack = new Stack);
9390     var stacked = stack.get(value);
9391     if (stacked) {
9392       return stacked;
9393     }
9394     stack.set(value, result);
9395
9396     if (!isArr) {
9397       var props = isFull ? getAllKeys(value) : keys(value);
9398     }
9399     // Recursively populate clone (susceptible to call stack limits).
9400     arrayEach(props || value, function(subValue, key) {
9401       if (props) {
9402         key = subValue;
9403         subValue = value[key];
9404       }
9405       assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
9406     });
9407     return result;
9408   }
9409
9410   /**
9411    * The base implementation of `_.create` without support for assigning
9412    * properties to the created object.
9413    *
9414    * @private
9415    * @param {Object} prototype The object to inherit from.
9416    * @returns {Object} Returns the new object.
9417    */
9418   function baseCreate(proto) {
9419     return isObject(proto) ? objectCreate(proto) : {};
9420   }
9421
9422   /**
9423    * The base implementation of methods like `_.difference` without support
9424    * for excluding multiple arrays or iteratee shorthands.
9425    *
9426    * @private
9427    * @param {Array} array The array to inspect.
9428    * @param {Array} values The values to exclude.
9429    * @param {Function} [iteratee] The iteratee invoked per element.
9430    * @param {Function} [comparator] The comparator invoked per element.
9431    * @returns {Array} Returns the new array of filtered values.
9432    */
9433   function baseDifference(array, values, iteratee, comparator) {
9434     var index = -1,
9435         includes = arrayIncludes,
9436         isCommon = true,
9437         length = array.length,
9438         result = [],
9439         valuesLength = values.length;
9440
9441     if (!length) {
9442       return result;
9443     }
9444     if (iteratee) {
9445       values = arrayMap(values, baseUnary(iteratee));
9446     }
9447     if (comparator) {
9448       includes = arrayIncludesWith;
9449       isCommon = false;
9450     }
9451     else if (values.length >= LARGE_ARRAY_SIZE) {
9452       includes = cacheHas;
9453       isCommon = false;
9454       values = new SetCache(values);
9455     }
9456     outer:
9457     while (++index < length) {
9458       var value = array[index],
9459           computed = iteratee ? iteratee(value) : value;
9460
9461       value = (comparator || value !== 0) ? value : 0;
9462       if (isCommon && computed === computed) {
9463         var valuesIndex = valuesLength;
9464         while (valuesIndex--) {
9465           if (values[valuesIndex] === computed) {
9466             continue outer;
9467           }
9468         }
9469         result.push(value);
9470       }
9471       else if (!includes(values, computed, comparator)) {
9472         result.push(value);
9473       }
9474     }
9475     return result;
9476   }
9477
9478   /**
9479    * The base implementation of `_.forEach` without support for iteratee shorthands.
9480    *
9481    * @private
9482    * @param {Array|Object} collection The collection to iterate over.
9483    * @param {Function} iteratee The function invoked per iteration.
9484    * @returns {Array|Object} Returns `collection`.
9485    */
9486   var baseEach = createBaseEach(baseForOwn);
9487
9488   /**
9489    * The base implementation of `_.every` without support for iteratee shorthands.
9490    *
9491    * @private
9492    * @param {Array|Object} collection The collection to iterate over.
9493    * @param {Function} predicate The function invoked per iteration.
9494    * @returns {boolean} Returns `true` if all elements pass the predicate check,
9495    *  else `false`
9496    */
9497   function baseEvery(collection, predicate) {
9498     var result = true;
9499     baseEach(collection, function(value, index, collection) {
9500       result = !!predicate(value, index, collection);
9501       return result;
9502     });
9503     return result;
9504   }
9505
9506   /**
9507    * The base implementation of `_.filter` without support for iteratee shorthands.
9508    *
9509    * @private
9510    * @param {Array|Object} collection The collection to iterate over.
9511    * @param {Function} predicate The function invoked per iteration.
9512    * @returns {Array} Returns the new filtered array.
9513    */
9514   function baseFilter(collection, predicate) {
9515     var result = [];
9516     baseEach(collection, function(value, index, collection) {
9517       if (predicate(value, index, collection)) {
9518         result.push(value);
9519       }
9520     });
9521     return result;
9522   }
9523
9524   /**
9525    * The base implementation of `_.flatten` with support for restricting flattening.
9526    *
9527    * @private
9528    * @param {Array} array The array to flatten.
9529    * @param {number} depth The maximum recursion depth.
9530    * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
9531    * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
9532    * @param {Array} [result=[]] The initial result value.
9533    * @returns {Array} Returns the new flattened array.
9534    */
9535   function baseFlatten(array, depth, predicate, isStrict, result) {
9536     var index = -1,
9537         length = array.length;
9538
9539     predicate || (predicate = isFlattenable);
9540     result || (result = []);
9541
9542     while (++index < length) {
9543       var value = array[index];
9544       if (depth > 0 && predicate(value)) {
9545         if (depth > 1) {
9546           // Recursively flatten arrays (susceptible to call stack limits).
9547           baseFlatten(value, depth - 1, predicate, isStrict, result);
9548         } else {
9549           arrayPush(result, value);
9550         }
9551       } else if (!isStrict) {
9552         result[result.length] = value;
9553       }
9554     }
9555     return result;
9556   }
9557
9558   /**
9559    * The base implementation of `baseForOwn` which iterates over `object`
9560    * properties returned by `keysFunc` and invokes `iteratee` for each property.
9561    * Iteratee functions may exit iteration early by explicitly returning `false`.
9562    *
9563    * @private
9564    * @param {Object} object The object to iterate over.
9565    * @param {Function} iteratee The function invoked per iteration.
9566    * @param {Function} keysFunc The function to get the keys of `object`.
9567    * @returns {Object} Returns `object`.
9568    */
9569   var baseFor = createBaseFor();
9570
9571   /**
9572    * The base implementation of `_.forOwn` without support for iteratee shorthands.
9573    *
9574    * @private
9575    * @param {Object} object The object to iterate over.
9576    * @param {Function} iteratee The function invoked per iteration.
9577    * @returns {Object} Returns `object`.
9578    */
9579   function baseForOwn(object, iteratee) {
9580     return object && baseFor(object, iteratee, keys);
9581   }
9582
9583   /**
9584    * The base implementation of `_.functions` which creates an array of
9585    * `object` function property names filtered from `props`.
9586    *
9587    * @private
9588    * @param {Object} object The object to inspect.
9589    * @param {Array} props The property names to filter.
9590    * @returns {Array} Returns the function names.
9591    */
9592   function baseFunctions(object, props) {
9593     return arrayFilter(props, function(key) {
9594       return isFunction(object[key]);
9595     });
9596   }
9597
9598   /**
9599    * The base implementation of `_.get` without support for default values.
9600    *
9601    * @private
9602    * @param {Object} object The object to query.
9603    * @param {Array|string} path The path of the property to get.
9604    * @returns {*} Returns the resolved value.
9605    */
9606   function baseGet(object, path) {
9607     path = isKey(path, object) ? [path] : castPath(path);
9608
9609     var index = 0,
9610         length = path.length;
9611
9612     while (object != null && index < length) {
9613       object = object[toKey(path[index++])];
9614     }
9615     return (index && index == length) ? object : undefined;
9616   }
9617
9618   /**
9619    * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
9620    * `keysFunc` and `symbolsFunc` to get the enumerable property names and
9621    * symbols of `object`.
9622    *
9623    * @private
9624    * @param {Object} object The object to query.
9625    * @param {Function} keysFunc The function to get the keys of `object`.
9626    * @param {Function} symbolsFunc The function to get the symbols of `object`.
9627    * @returns {Array} Returns the array of property names and symbols.
9628    */
9629   function baseGetAllKeys(object, keysFunc, symbolsFunc) {
9630     var result = keysFunc(object);
9631     return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
9632   }
9633
9634   /**
9635    * The base implementation of `_.has` without support for deep paths.
9636    *
9637    * @private
9638    * @param {Object} object The object to query.
9639    * @param {Array|string} key The key to check.
9640    * @returns {boolean} Returns `true` if `key` exists, else `false`.
9641    */
9642   function baseHas(object, key) {
9643     // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`,
9644     // that are composed entirely of index properties, return `false` for
9645     // `hasOwnProperty` checks of them.
9646     return hasOwnProperty.call(object, key) ||
9647       (typeof object == 'object' && key in object && getPrototype(object) === null);
9648   }
9649
9650   /**
9651    * The base implementation of `_.hasIn` without support for deep paths.
9652    *
9653    * @private
9654    * @param {Object} object The object to query.
9655    * @param {Array|string} key The key to check.
9656    * @returns {boolean} Returns `true` if `key` exists, else `false`.
9657    */
9658   function baseHasIn(object, key) {
9659     return key in Object(object);
9660   }
9661
9662   /**
9663    * The base implementation of methods like `_.intersection`, without support
9664    * for iteratee shorthands, that accepts an array of arrays to inspect.
9665    *
9666    * @private
9667    * @param {Array} arrays The arrays to inspect.
9668    * @param {Function} [iteratee] The iteratee invoked per element.
9669    * @param {Function} [comparator] The comparator invoked per element.
9670    * @returns {Array} Returns the new array of shared values.
9671    */
9672   function baseIntersection(arrays, iteratee, comparator) {
9673     var includes = comparator ? arrayIncludesWith : arrayIncludes,
9674         length = arrays[0].length,
9675         othLength = arrays.length,
9676         othIndex = othLength,
9677         caches = Array(othLength),
9678         maxLength = Infinity,
9679         result = [];
9680
9681     while (othIndex--) {
9682       var array = arrays[othIndex];
9683       if (othIndex && iteratee) {
9684         array = arrayMap(array, baseUnary(iteratee));
9685       }
9686       maxLength = nativeMin(array.length, maxLength);
9687       caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
9688         ? new SetCache(othIndex && array)
9689         : undefined;
9690     }
9691     array = arrays[0];
9692
9693     var index = -1,
9694         seen = caches[0];
9695
9696     outer:
9697     while (++index < length && result.length < maxLength) {
9698       var value = array[index],
9699           computed = iteratee ? iteratee(value) : value;
9700
9701       value = (comparator || value !== 0) ? value : 0;
9702       if (!(seen
9703             ? cacheHas(seen, computed)
9704             : includes(result, computed, comparator)
9705           )) {
9706         othIndex = othLength;
9707         while (--othIndex) {
9708           var cache = caches[othIndex];
9709           if (!(cache
9710                 ? cacheHas(cache, computed)
9711                 : includes(arrays[othIndex], computed, comparator))
9712               ) {
9713             continue outer;
9714           }
9715         }
9716         if (seen) {
9717           seen.push(computed);
9718         }
9719         result.push(value);
9720       }
9721     }
9722     return result;
9723   }
9724
9725   /**
9726    * The base implementation of `_.invoke` without support for individual
9727    * method arguments.
9728    *
9729    * @private
9730    * @param {Object} object The object to query.
9731    * @param {Array|string} path The path of the method to invoke.
9732    * @param {Array} args The arguments to invoke the method with.
9733    * @returns {*} Returns the result of the invoked method.
9734    */
9735   function baseInvoke(object, path, args) {
9736     if (!isKey(path, object)) {
9737       path = castPath(path);
9738       object = parent(object, path);
9739       path = last(path);
9740     }
9741     var func = object == null ? object : object[toKey(path)];
9742     return func == null ? undefined : apply(func, object, args);
9743   }
9744
9745   /**
9746    * The base implementation of `_.isEqual` which supports partial comparisons
9747    * and tracks traversed objects.
9748    *
9749    * @private
9750    * @param {*} value The value to compare.
9751    * @param {*} other The other value to compare.
9752    * @param {Function} [customizer] The function to customize comparisons.
9753    * @param {boolean} [bitmask] The bitmask of comparison flags.
9754    *  The bitmask may be composed of the following flags:
9755    *     1 - Unordered comparison
9756    *     2 - Partial comparison
9757    * @param {Object} [stack] Tracks traversed `value` and `other` objects.
9758    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9759    */
9760   function baseIsEqual(value, other, customizer, bitmask, stack) {
9761     if (value === other) {
9762       return true;
9763     }
9764     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9765       return value !== value && other !== other;
9766     }
9767     return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);
9768   }
9769
9770   /**
9771    * A specialized version of `baseIsEqual` for arrays and objects which performs
9772    * deep comparisons and tracks traversed objects enabling objects with circular
9773    * references to be compared.
9774    *
9775    * @private
9776    * @param {Object} object The object to compare.
9777    * @param {Object} other The other object to compare.
9778    * @param {Function} equalFunc The function to determine equivalents of values.
9779    * @param {Function} [customizer] The function to customize comparisons.
9780    * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`
9781    *  for more details.
9782    * @param {Object} [stack] Tracks traversed `object` and `other` objects.
9783    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9784    */
9785   function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {
9786     var objIsArr = isArray(object),
9787         othIsArr = isArray(other),
9788         objTag = arrayTag,
9789         othTag = arrayTag;
9790
9791     if (!objIsArr) {
9792       objTag = getTag(object);
9793       objTag = objTag == argsTag ? objectTag : objTag;
9794     }
9795     if (!othIsArr) {
9796       othTag = getTag(other);
9797       othTag = othTag == argsTag ? objectTag : othTag;
9798     }
9799     var objIsObj = objTag == objectTag && !isHostObject(object),
9800         othIsObj = othTag == objectTag && !isHostObject(other),
9801         isSameTag = objTag == othTag;
9802
9803     if (isSameTag && !objIsObj) {
9804       stack || (stack = new Stack);
9805       return (objIsArr || isTypedArray(object))
9806         ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)
9807         : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);
9808     }
9809     if (!(bitmask & PARTIAL_COMPARE_FLAG)) {
9810       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9811           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9812
9813       if (objIsWrapped || othIsWrapped) {
9814         var objUnwrapped = objIsWrapped ? object.value() : object,
9815             othUnwrapped = othIsWrapped ? other.value() : other;
9816
9817         stack || (stack = new Stack);
9818         return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);
9819       }
9820     }
9821     if (!isSameTag) {
9822       return false;
9823     }
9824     stack || (stack = new Stack);
9825     return equalObjects(object, other, equalFunc, customizer, bitmask, stack);
9826   }
9827
9828   /**
9829    * The base implementation of `_.isMatch` without support for iteratee shorthands.
9830    *
9831    * @private
9832    * @param {Object} object The object to inspect.
9833    * @param {Object} source The object of property values to match.
9834    * @param {Array} matchData The property names, values, and compare flags to match.
9835    * @param {Function} [customizer] The function to customize comparisons.
9836    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9837    */
9838   function baseIsMatch(object, source, matchData, customizer) {
9839     var index = matchData.length,
9840         length = index,
9841         noCustomizer = !customizer;
9842
9843     if (object == null) {
9844       return !length;
9845     }
9846     object = Object(object);
9847     while (index--) {
9848       var data = matchData[index];
9849       if ((noCustomizer && data[2])
9850             ? data[1] !== object[data[0]]
9851             : !(data[0] in object)
9852           ) {
9853         return false;
9854       }
9855     }
9856     while (++index < length) {
9857       data = matchData[index];
9858       var key = data[0],
9859           objValue = object[key],
9860           srcValue = data[1];
9861
9862       if (noCustomizer && data[2]) {
9863         if (objValue === undefined && !(key in object)) {
9864           return false;
9865         }
9866       } else {
9867         var stack = new Stack;
9868         if (customizer) {
9869           var result = customizer(objValue, srcValue, key, object, source, stack);
9870         }
9871         if (!(result === undefined
9872               ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)
9873               : result
9874             )) {
9875           return false;
9876         }
9877       }
9878     }
9879     return true;
9880   }
9881
9882   /**
9883    * The base implementation of `_.iteratee`.
9884    *
9885    * @private
9886    * @param {*} [value=_.identity] The value to convert to an iteratee.
9887    * @returns {Function} Returns the iteratee.
9888    */
9889   function baseIteratee(value) {
9890     // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
9891     // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
9892     if (typeof value == 'function') {
9893       return value;
9894     }
9895     if (value == null) {
9896       return identity;
9897     }
9898     if (typeof value == 'object') {
9899       return isArray(value)
9900         ? baseMatchesProperty(value[0], value[1])
9901         : baseMatches(value);
9902     }
9903     return property(value);
9904   }
9905
9906   /**
9907    * The base implementation of `_.keys` which doesn't skip the constructor
9908    * property of prototypes or treat sparse arrays as dense.
9909    *
9910    * @private
9911    * @param {Object} object The object to query.
9912    * @returns {Array} Returns the array of property names.
9913    */
9914   function baseKeys(object) {
9915     return nativeKeys(Object(object));
9916   }
9917
9918   /**
9919    * The base implementation of `_.keysIn` which doesn't skip the constructor
9920    * property of prototypes or treat sparse arrays as dense.
9921    *
9922    * @private
9923    * @param {Object} object The object to query.
9924    * @returns {Array} Returns the array of property names.
9925    */
9926   function baseKeysIn(object) {
9927     object = object == null ? object : Object(object);
9928
9929     var result = [];
9930     for (var key in object) {
9931       result.push(key);
9932     }
9933     return result;
9934   }
9935
9936   // Fallback for IE < 9 with es6-shim.
9937   if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
9938     baseKeysIn = function(object) {
9939       return iteratorToArray(enumerate(object));
9940     };
9941   }
9942
9943   /**
9944    * The base implementation of `_.map` without support for iteratee shorthands.
9945    *
9946    * @private
9947    * @param {Array|Object} collection The collection to iterate over.
9948    * @param {Function} iteratee The function invoked per iteration.
9949    * @returns {Array} Returns the new mapped array.
9950    */
9951   function baseMap(collection, iteratee) {
9952     var index = -1,
9953         result = isArrayLike(collection) ? Array(collection.length) : [];
9954
9955     baseEach(collection, function(value, key, collection) {
9956       result[++index] = iteratee(value, key, collection);
9957     });
9958     return result;
9959   }
9960
9961   /**
9962    * The base implementation of `_.matches` which doesn't clone `source`.
9963    *
9964    * @private
9965    * @param {Object} source The object of property values to match.
9966    * @returns {Function} Returns the new spec function.
9967    */
9968   function baseMatches(source) {
9969     var matchData = getMatchData(source);
9970     if (matchData.length == 1 && matchData[0][2]) {
9971       return matchesStrictComparable(matchData[0][0], matchData[0][1]);
9972     }
9973     return function(object) {
9974       return object === source || baseIsMatch(object, source, matchData);
9975     };
9976   }
9977
9978   /**
9979    * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
9980    *
9981    * @private
9982    * @param {string} path The path of the property to get.
9983    * @param {*} srcValue The value to match.
9984    * @returns {Function} Returns the new spec function.
9985    */
9986   function baseMatchesProperty(path, srcValue) {
9987     if (isKey(path) && isStrictComparable(srcValue)) {
9988       return matchesStrictComparable(toKey(path), srcValue);
9989     }
9990     return function(object) {
9991       var objValue = get(object, path);
9992       return (objValue === undefined && objValue === srcValue)
9993         ? hasIn(object, path)
9994         : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
9995     };
9996   }
9997
9998   /**
9999    * The base implementation of `_.merge` without support for multiple sources.
10000    *
10001    * @private
10002    * @param {Object} object The destination object.
10003    * @param {Object} source The source object.
10004    * @param {number} srcIndex The index of `source`.
10005    * @param {Function} [customizer] The function to customize merged values.
10006    * @param {Object} [stack] Tracks traversed source values and their merged
10007    *  counterparts.
10008    */
10009   function baseMerge(object, source, srcIndex, customizer, stack) {
10010     if (object === source) {
10011       return;
10012     }
10013     if (!(isArray(source) || isTypedArray(source))) {
10014       var props = keysIn(source);
10015     }
10016     arrayEach(props || source, function(srcValue, key) {
10017       if (props) {
10018         key = srcValue;
10019         srcValue = source[key];
10020       }
10021       if (isObject(srcValue)) {
10022         stack || (stack = new Stack);
10023         baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
10024       }
10025       else {
10026         var newValue = customizer
10027           ? customizer(object[key], srcValue, (key + ''), object, source, stack)
10028           : undefined;
10029
10030         if (newValue === undefined) {
10031           newValue = srcValue;
10032         }
10033         assignMergeValue(object, key, newValue);
10034       }
10035     });
10036   }
10037
10038   /**
10039    * A specialized version of `baseMerge` for arrays and objects which performs
10040    * deep merges and tracks traversed objects enabling objects with circular
10041    * references to be merged.
10042    *
10043    * @private
10044    * @param {Object} object The destination object.
10045    * @param {Object} source The source object.
10046    * @param {string} key The key of the value to merge.
10047    * @param {number} srcIndex The index of `source`.
10048    * @param {Function} mergeFunc The function to merge values.
10049    * @param {Function} [customizer] The function to customize assigned values.
10050    * @param {Object} [stack] Tracks traversed source values and their merged
10051    *  counterparts.
10052    */
10053   function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
10054     var objValue = object[key],
10055         srcValue = source[key],
10056         stacked = stack.get(srcValue);
10057
10058     if (stacked) {
10059       assignMergeValue(object, key, stacked);
10060       return;
10061     }
10062     var newValue = customizer
10063       ? customizer(objValue, srcValue, (key + ''), object, source, stack)
10064       : undefined;
10065
10066     var isCommon = newValue === undefined;
10067
10068     if (isCommon) {
10069       newValue = srcValue;
10070       if (isArray(srcValue) || isTypedArray(srcValue)) {
10071         if (isArray(objValue)) {
10072           newValue = objValue;
10073         }
10074         else if (isArrayLikeObject(objValue)) {
10075           newValue = copyArray(objValue);
10076         }
10077         else {
10078           isCommon = false;
10079           newValue = baseClone(srcValue, true);
10080         }
10081       }
10082       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
10083         if (isArguments(objValue)) {
10084           newValue = toPlainObject(objValue);
10085         }
10086         else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
10087           isCommon = false;
10088           newValue = baseClone(srcValue, true);
10089         }
10090         else {
10091           newValue = objValue;
10092         }
10093       }
10094       else {
10095         isCommon = false;
10096       }
10097     }
10098     stack.set(srcValue, newValue);
10099
10100     if (isCommon) {
10101       // Recursively merge objects and arrays (susceptible to call stack limits).
10102       mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
10103     }
10104     stack['delete'](srcValue);
10105     assignMergeValue(object, key, newValue);
10106   }
10107
10108   /**
10109    * The base implementation of `_.pick` without support for individual
10110    * property identifiers.
10111    *
10112    * @private
10113    * @param {Object} object The source object.
10114    * @param {string[]} props The property identifiers to pick.
10115    * @returns {Object} Returns the new object.
10116    */
10117   function basePick(object, props) {
10118     object = Object(object);
10119     return arrayReduce(props, function(result, key) {
10120       if (key in object) {
10121         result[key] = object[key];
10122       }
10123       return result;
10124     }, {});
10125   }
10126
10127   /**
10128    * The base implementation of `_.property` without support for deep paths.
10129    *
10130    * @private
10131    * @param {string} key The key of the property to get.
10132    * @returns {Function} Returns the new accessor function.
10133    */
10134   function baseProperty(key) {
10135     return function(object) {
10136       return object == null ? undefined : object[key];
10137     };
10138   }
10139
10140   /**
10141    * A specialized version of `baseProperty` which supports deep paths.
10142    *
10143    * @private
10144    * @param {Array|string} path The path of the property to get.
10145    * @returns {Function} Returns the new accessor function.
10146    */
10147   function basePropertyDeep(path) {
10148     return function(object) {
10149       return baseGet(object, path);
10150     };
10151   }
10152
10153   /**
10154    * The base implementation of `setData` without support for hot loop detection.
10155    *
10156    * @private
10157    * @param {Function} func The function to associate metadata with.
10158    * @param {*} data The metadata.
10159    * @returns {Function} Returns `func`.
10160    */
10161   var baseSetData = !metaMap ? identity : function(func, data) {
10162     metaMap.set(func, data);
10163     return func;
10164   };
10165
10166   /**
10167    * The base implementation of `_.slice` without an iteratee call guard.
10168    *
10169    * @private
10170    * @param {Array} array The array to slice.
10171    * @param {number} [start=0] The start position.
10172    * @param {number} [end=array.length] The end position.
10173    * @returns {Array} Returns the slice of `array`.
10174    */
10175   function baseSlice(array, start, end) {
10176     var index = -1,
10177         length = array.length;
10178
10179     if (start < 0) {
10180       start = -start > length ? 0 : (length + start);
10181     }
10182     end = end > length ? length : end;
10183     if (end < 0) {
10184       end += length;
10185     }
10186     length = start > end ? 0 : ((end - start) >>> 0);
10187     start >>>= 0;
10188
10189     var result = Array(length);
10190     while (++index < length) {
10191       result[index] = array[index + start];
10192     }
10193     return result;
10194   }
10195
10196   /**
10197    * The base implementation of `_.some` without support for iteratee shorthands.
10198    *
10199    * @private
10200    * @param {Array|Object} collection The collection to iterate over.
10201    * @param {Function} predicate The function invoked per iteration.
10202    * @returns {boolean} Returns `true` if any element passes the predicate check,
10203    *  else `false`.
10204    */
10205   function baseSome(collection, predicate) {
10206     var result;
10207
10208     baseEach(collection, function(value, index, collection) {
10209       result = predicate(value, index, collection);
10210       return !result;
10211     });
10212     return !!result;
10213   }
10214
10215   /**
10216    * The base implementation of `_.toString` which doesn't convert nullish
10217    * values to empty strings.
10218    *
10219    * @private
10220    * @param {*} value The value to process.
10221    * @returns {string} Returns the string.
10222    */
10223   function baseToString(value) {
10224     // Exit early for strings to avoid a performance hit in some environments.
10225     if (typeof value == 'string') {
10226       return value;
10227     }
10228     if (isSymbol(value)) {
10229       return symbolToString ? symbolToString.call(value) : '';
10230     }
10231     var result = (value + '');
10232     return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
10233   }
10234
10235   /**
10236    * The base implementation of `_.uniqBy` without support for iteratee shorthands.
10237    *
10238    * @private
10239    * @param {Array} array The array to inspect.
10240    * @param {Function} [iteratee] The iteratee invoked per element.
10241    * @param {Function} [comparator] The comparator invoked per element.
10242    * @returns {Array} Returns the new duplicate free array.
10243    */
10244   function baseUniq(array, iteratee, comparator) {
10245     var index = -1,
10246         includes = arrayIncludes,
10247         length = array.length,
10248         isCommon = true,
10249         result = [],
10250         seen = result;
10251
10252     if (comparator) {
10253       isCommon = false;
10254       includes = arrayIncludesWith;
10255     }
10256     else if (length >= LARGE_ARRAY_SIZE) {
10257       var set = iteratee ? null : createSet(array);
10258       if (set) {
10259         return setToArray(set);
10260       }
10261       isCommon = false;
10262       includes = cacheHas;
10263       seen = new SetCache;
10264     }
10265     else {
10266       seen = iteratee ? [] : result;
10267     }
10268     outer:
10269     while (++index < length) {
10270       var value = array[index],
10271           computed = iteratee ? iteratee(value) : value;
10272
10273       value = (comparator || value !== 0) ? value : 0;
10274       if (isCommon && computed === computed) {
10275         var seenIndex = seen.length;
10276         while (seenIndex--) {
10277           if (seen[seenIndex] === computed) {
10278             continue outer;
10279           }
10280         }
10281         if (iteratee) {
10282           seen.push(computed);
10283         }
10284         result.push(value);
10285       }
10286       else if (!includes(seen, computed, comparator)) {
10287         if (seen !== result) {
10288           seen.push(computed);
10289         }
10290         result.push(value);
10291       }
10292     }
10293     return result;
10294   }
10295
10296   /**
10297    * The base implementation of `wrapperValue` which returns the result of
10298    * performing a sequence of actions on the unwrapped `value`, where each
10299    * successive action is supplied the return value of the previous.
10300    *
10301    * @private
10302    * @param {*} value The unwrapped value.
10303    * @param {Array} actions Actions to perform to resolve the unwrapped value.
10304    * @returns {*} Returns the resolved value.
10305    */
10306   function baseWrapperValue(value, actions) {
10307     var result = value;
10308     if (result instanceof LazyWrapper) {
10309       result = result.value();
10310     }
10311     return arrayReduce(actions, function(result, action) {
10312       return action.func.apply(action.thisArg, arrayPush([result], action.args));
10313     }, result);
10314   }
10315
10316   /**
10317    * Casts `value` to an empty array if it's not an array like object.
10318    *
10319    * @private
10320    * @param {*} value The value to inspect.
10321    * @returns {Array|Object} Returns the cast array-like object.
10322    */
10323   function castArrayLikeObject(value) {
10324     return isArrayLikeObject(value) ? value : [];
10325   }
10326
10327   /**
10328    * Casts `value` to a path array if it's not one.
10329    *
10330    * @private
10331    * @param {*} value The value to inspect.
10332    * @returns {Array} Returns the cast property path array.
10333    */
10334   function castPath(value) {
10335     return isArray(value) ? value : stringToPath(value);
10336   }
10337
10338   /**
10339    * Creates a clone of  `buffer`.
10340    *
10341    * @private
10342    * @param {Buffer} buffer The buffer to clone.
10343    * @param {boolean} [isDeep] Specify a deep clone.
10344    * @returns {Buffer} Returns the cloned buffer.
10345    */
10346   function cloneBuffer(buffer, isDeep) {
10347     if (isDeep) {
10348       return buffer.slice();
10349     }
10350     var result = new buffer.constructor(buffer.length);
10351     buffer.copy(result);
10352     return result;
10353   }
10354
10355   /**
10356    * Creates a clone of `arrayBuffer`.
10357    *
10358    * @private
10359    * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
10360    * @returns {ArrayBuffer} Returns the cloned array buffer.
10361    */
10362   function cloneArrayBuffer(arrayBuffer) {
10363     var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
10364     new Uint8Array(result).set(new Uint8Array(arrayBuffer));
10365     return result;
10366   }
10367
10368   /**
10369    * Creates a clone of `dataView`.
10370    *
10371    * @private
10372    * @param {Object} dataView The data view to clone.
10373    * @param {boolean} [isDeep] Specify a deep clone.
10374    * @returns {Object} Returns the cloned data view.
10375    */
10376   function cloneDataView(dataView, isDeep) {
10377     var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
10378     return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
10379   }
10380
10381   /**
10382    * Creates a clone of `map`.
10383    *
10384    * @private
10385    * @param {Object} map The map to clone.
10386    * @param {Function} cloneFunc The function to clone values.
10387    * @param {boolean} [isDeep] Specify a deep clone.
10388    * @returns {Object} Returns the cloned map.
10389    */
10390   function cloneMap(map, isDeep, cloneFunc) {
10391     var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
10392     return arrayReduce(array, addMapEntry, new map.constructor);
10393   }
10394
10395   /**
10396    * Creates a clone of `regexp`.
10397    *
10398    * @private
10399    * @param {Object} regexp The regexp to clone.
10400    * @returns {Object} Returns the cloned regexp.
10401    */
10402   function cloneRegExp(regexp) {
10403     var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
10404     result.lastIndex = regexp.lastIndex;
10405     return result;
10406   }
10407
10408   /**
10409    * Creates a clone of `set`.
10410    *
10411    * @private
10412    * @param {Object} set The set to clone.
10413    * @param {Function} cloneFunc The function to clone values.
10414    * @param {boolean} [isDeep] Specify a deep clone.
10415    * @returns {Object} Returns the cloned set.
10416    */
10417   function cloneSet(set, isDeep, cloneFunc) {
10418     var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
10419     return arrayReduce(array, addSetEntry, new set.constructor);
10420   }
10421
10422   /**
10423    * Creates a clone of the `symbol` object.
10424    *
10425    * @private
10426    * @param {Object} symbol The symbol object to clone.
10427    * @returns {Object} Returns the cloned symbol object.
10428    */
10429   function cloneSymbol(symbol) {
10430     return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
10431   }
10432
10433   /**
10434    * Creates a clone of `typedArray`.
10435    *
10436    * @private
10437    * @param {Object} typedArray The typed array to clone.
10438    * @param {boolean} [isDeep] Specify a deep clone.
10439    * @returns {Object} Returns the cloned typed array.
10440    */
10441   function cloneTypedArray(typedArray, isDeep) {
10442     var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
10443     return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
10444   }
10445
10446   /**
10447    * Creates an array that is the composition of partially applied arguments,
10448    * placeholders, and provided arguments into a single array of arguments.
10449    *
10450    * @private
10451    * @param {Array} args The provided arguments.
10452    * @param {Array} partials The arguments to prepend to those provided.
10453    * @param {Array} holders The `partials` placeholder indexes.
10454    * @params {boolean} [isCurried] Specify composing for a curried function.
10455    * @returns {Array} Returns the new array of composed arguments.
10456    */
10457   function composeArgs(args, partials, holders, isCurried) {
10458     var argsIndex = -1,
10459         argsLength = args.length,
10460         holdersLength = holders.length,
10461         leftIndex = -1,
10462         leftLength = partials.length,
10463         rangeLength = nativeMax(argsLength - holdersLength, 0),
10464         result = Array(leftLength + rangeLength),
10465         isUncurried = !isCurried;
10466
10467     while (++leftIndex < leftLength) {
10468       result[leftIndex] = partials[leftIndex];
10469     }
10470     while (++argsIndex < holdersLength) {
10471       if (isUncurried || argsIndex < argsLength) {
10472         result[holders[argsIndex]] = args[argsIndex];
10473       }
10474     }
10475     while (rangeLength--) {
10476       result[leftIndex++] = args[argsIndex++];
10477     }
10478     return result;
10479   }
10480
10481   /**
10482    * This function is like `composeArgs` except that the arguments composition
10483    * is tailored for `_.partialRight`.
10484    *
10485    * @private
10486    * @param {Array} args The provided arguments.
10487    * @param {Array} partials The arguments to append to those provided.
10488    * @param {Array} holders The `partials` placeholder indexes.
10489    * @params {boolean} [isCurried] Specify composing for a curried function.
10490    * @returns {Array} Returns the new array of composed arguments.
10491    */
10492   function composeArgsRight(args, partials, holders, isCurried) {
10493     var argsIndex = -1,
10494         argsLength = args.length,
10495         holdersIndex = -1,
10496         holdersLength = holders.length,
10497         rightIndex = -1,
10498         rightLength = partials.length,
10499         rangeLength = nativeMax(argsLength - holdersLength, 0),
10500         result = Array(rangeLength + rightLength),
10501         isUncurried = !isCurried;
10502
10503     while (++argsIndex < rangeLength) {
10504       result[argsIndex] = args[argsIndex];
10505     }
10506     var offset = argsIndex;
10507     while (++rightIndex < rightLength) {
10508       result[offset + rightIndex] = partials[rightIndex];
10509     }
10510     while (++holdersIndex < holdersLength) {
10511       if (isUncurried || argsIndex < argsLength) {
10512         result[offset + holders[holdersIndex]] = args[argsIndex++];
10513       }
10514     }
10515     return result;
10516   }
10517
10518   /**
10519    * Copies the values of `source` to `array`.
10520    *
10521    * @private
10522    * @param {Array} source The array to copy values from.
10523    * @param {Array} [array=[]] The array to copy values to.
10524    * @returns {Array} Returns `array`.
10525    */
10526   function copyArray(source, array) {
10527     var index = -1,
10528         length = source.length;
10529
10530     array || (array = Array(length));
10531     while (++index < length) {
10532       array[index] = source[index];
10533     }
10534     return array;
10535   }
10536
10537   /**
10538    * Copies properties of `source` to `object`.
10539    *
10540    * @private
10541    * @param {Object} source The object to copy properties from.
10542    * @param {Array} props The property identifiers to copy.
10543    * @param {Object} [object={}] The object to copy properties to.
10544    * @param {Function} [customizer] The function to customize copied values.
10545    * @returns {Object} Returns `object`.
10546    */
10547   function copyObject(source, props, object, customizer) {
10548     object || (object = {});
10549
10550     var index = -1,
10551         length = props.length;
10552
10553     while (++index < length) {
10554       var key = props[index];
10555
10556       var newValue = customizer
10557         ? customizer(object[key], source[key], key, object, source)
10558         : source[key];
10559
10560       assignValue(object, key, newValue);
10561     }
10562     return object;
10563   }
10564
10565   /**
10566    * Copies own symbol properties of `source` to `object`.
10567    *
10568    * @private
10569    * @param {Object} source The object to copy symbols from.
10570    * @param {Object} [object={}] The object to copy symbols to.
10571    * @returns {Object} Returns `object`.
10572    */
10573   function copySymbols(source, object) {
10574     return copyObject(source, getSymbols(source), object);
10575   }
10576
10577   /**
10578    * Creates a function like `_.groupBy`.
10579    *
10580    * @private
10581    * @param {Function} setter The function to set accumulator values.
10582    * @param {Function} [initializer] The accumulator object initializer.
10583    * @returns {Function} Returns the new aggregator function.
10584    */
10585   function createAggregator(setter, initializer) {
10586     return function(collection, iteratee) {
10587       var func = isArray(collection) ? arrayAggregator : baseAggregator,
10588           accumulator = initializer ? initializer() : {};
10589
10590       return func(collection, setter, getIteratee(iteratee), accumulator);
10591     };
10592   }
10593
10594   /**
10595    * Creates a function like `_.assign`.
10596    *
10597    * @private
10598    * @param {Function} assigner The function to assign values.
10599    * @returns {Function} Returns the new assigner function.
10600    */
10601   function createAssigner(assigner) {
10602     return rest(function(object, sources) {
10603       var index = -1,
10604           length = sources.length,
10605           customizer = length > 1 ? sources[length - 1] : undefined,
10606           guard = length > 2 ? sources[2] : undefined;
10607
10608       customizer = (assigner.length > 3 && typeof customizer == 'function')
10609         ? (length--, customizer)
10610         : undefined;
10611
10612       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
10613         customizer = length < 3 ? undefined : customizer;
10614         length = 1;
10615       }
10616       object = Object(object);
10617       while (++index < length) {
10618         var source = sources[index];
10619         if (source) {
10620           assigner(object, source, index, customizer);
10621         }
10622       }
10623       return object;
10624     });
10625   }
10626
10627   /**
10628    * Creates a `baseEach` or `baseEachRight` function.
10629    *
10630    * @private
10631    * @param {Function} eachFunc The function to iterate over a collection.
10632    * @param {boolean} [fromRight] Specify iterating from right to left.
10633    * @returns {Function} Returns the new base function.
10634    */
10635   function createBaseEach(eachFunc, fromRight) {
10636     return function(collection, iteratee) {
10637       if (collection == null) {
10638         return collection;
10639       }
10640       if (!isArrayLike(collection)) {
10641         return eachFunc(collection, iteratee);
10642       }
10643       var length = collection.length,
10644           index = fromRight ? length : -1,
10645           iterable = Object(collection);
10646
10647       while ((fromRight ? index-- : ++index < length)) {
10648         if (iteratee(iterable[index], index, iterable) === false) {
10649           break;
10650         }
10651       }
10652       return collection;
10653     };
10654   }
10655
10656   /**
10657    * Creates a base function for methods like `_.forIn` and `_.forOwn`.
10658    *
10659    * @private
10660    * @param {boolean} [fromRight] Specify iterating from right to left.
10661    * @returns {Function} Returns the new base function.
10662    */
10663   function createBaseFor(fromRight) {
10664     return function(object, iteratee, keysFunc) {
10665       var index = -1,
10666           iterable = Object(object),
10667           props = keysFunc(object),
10668           length = props.length;
10669
10670       while (length--) {
10671         var key = props[fromRight ? length : ++index];
10672         if (iteratee(iterable[key], key, iterable) === false) {
10673           break;
10674         }
10675       }
10676       return object;
10677     };
10678   }
10679
10680   /**
10681    * Creates a function that wraps `func` to invoke it with the optional `this`
10682    * binding of `thisArg`.
10683    *
10684    * @private
10685    * @param {Function} func The function to wrap.
10686    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10687    *  for more details.
10688    * @param {*} [thisArg] The `this` binding of `func`.
10689    * @returns {Function} Returns the new wrapped function.
10690    */
10691   function createBaseWrapper(func, bitmask, thisArg) {
10692     var isBind = bitmask & BIND_FLAG,
10693         Ctor = createCtorWrapper(func);
10694
10695     function wrapper() {
10696       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10697       return fn.apply(isBind ? thisArg : this, arguments);
10698     }
10699     return wrapper;
10700   }
10701
10702   /**
10703    * Creates a function that produces an instance of `Ctor` regardless of
10704    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
10705    *
10706    * @private
10707    * @param {Function} Ctor The constructor to wrap.
10708    * @returns {Function} Returns the new wrapped function.
10709    */
10710   function createCtorWrapper(Ctor) {
10711     return function() {
10712       // Use a `switch` statement to work with class constructors. See
10713       // http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
10714       // for more details.
10715       var args = arguments;
10716       switch (args.length) {
10717         case 0: return new Ctor;
10718         case 1: return new Ctor(args[0]);
10719         case 2: return new Ctor(args[0], args[1]);
10720         case 3: return new Ctor(args[0], args[1], args[2]);
10721         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
10722         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
10723         case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
10724         case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
10725       }
10726       var thisBinding = baseCreate(Ctor.prototype),
10727           result = Ctor.apply(thisBinding, args);
10728
10729       // Mimic the constructor's `return` behavior.
10730       // See https://es5.github.io/#x13.2.2 for more details.
10731       return isObject(result) ? result : thisBinding;
10732     };
10733   }
10734
10735   /**
10736    * Creates a function that wraps `func` to enable currying.
10737    *
10738    * @private
10739    * @param {Function} func The function to wrap.
10740    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10741    *  for more details.
10742    * @param {number} arity The arity of `func`.
10743    * @returns {Function} Returns the new wrapped function.
10744    */
10745   function createCurryWrapper(func, bitmask, arity) {
10746     var Ctor = createCtorWrapper(func);
10747
10748     function wrapper() {
10749       var length = arguments.length,
10750           args = Array(length),
10751           index = length,
10752           placeholder = getHolder(wrapper);
10753
10754       while (index--) {
10755         args[index] = arguments[index];
10756       }
10757       var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
10758         ? []
10759         : replaceHolders(args, placeholder);
10760
10761       length -= holders.length;
10762       if (length < arity) {
10763         return createRecurryWrapper(
10764           func, bitmask, createHybridWrapper, wrapper.placeholder, undefined,
10765           args, holders, undefined, undefined, arity - length);
10766       }
10767       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10768       return apply(fn, this, args);
10769     }
10770     return wrapper;
10771   }
10772
10773   /**
10774    * Creates a function that wraps `func` to invoke it with optional `this`
10775    * binding of `thisArg`, partial application, and currying.
10776    *
10777    * @private
10778    * @param {Function|string} func The function or method name to wrap.
10779    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10780    *  for more details.
10781    * @param {*} [thisArg] The `this` binding of `func`.
10782    * @param {Array} [partials] The arguments to prepend to those provided to
10783    *  the new function.
10784    * @param {Array} [holders] The `partials` placeholder indexes.
10785    * @param {Array} [partialsRight] The arguments to append to those provided
10786    *  to the new function.
10787    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10788    * @param {Array} [argPos] The argument positions of the new function.
10789    * @param {number} [ary] The arity cap of `func`.
10790    * @param {number} [arity] The arity of `func`.
10791    * @returns {Function} Returns the new wrapped function.
10792    */
10793   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10794     var isAry = bitmask & ARY_FLAG,
10795         isBind = bitmask & BIND_FLAG,
10796         isBindKey = bitmask & BIND_KEY_FLAG,
10797         isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),
10798         isFlip = bitmask & FLIP_FLAG,
10799         Ctor = isBindKey ? undefined : createCtorWrapper(func);
10800
10801     function wrapper() {
10802       var length = arguments.length,
10803           args = Array(length),
10804           index = length;
10805
10806       while (index--) {
10807         args[index] = arguments[index];
10808       }
10809       if (isCurried) {
10810         var placeholder = getHolder(wrapper),
10811             holdersCount = countHolders(args, placeholder);
10812       }
10813       if (partials) {
10814         args = composeArgs(args, partials, holders, isCurried);
10815       }
10816       if (partialsRight) {
10817         args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
10818       }
10819       length -= holdersCount;
10820       if (isCurried && length < arity) {
10821         var newHolders = replaceHolders(args, placeholder);
10822         return createRecurryWrapper(
10823           func, bitmask, createHybridWrapper, wrapper.placeholder, thisArg,
10824           args, newHolders, argPos, ary, arity - length
10825         );
10826       }
10827       var thisBinding = isBind ? thisArg : this,
10828           fn = isBindKey ? thisBinding[func] : func;
10829
10830       length = args.length;
10831       if (argPos) {
10832         args = reorder(args, argPos);
10833       } else if (isFlip && length > 1) {
10834         args.reverse();
10835       }
10836       if (isAry && ary < length) {
10837         args.length = ary;
10838       }
10839       if (this && this !== root && this instanceof wrapper) {
10840         fn = Ctor || createCtorWrapper(fn);
10841       }
10842       return fn.apply(thisBinding, args);
10843     }
10844     return wrapper;
10845   }
10846
10847   /**
10848    * Creates a function that wraps `func` to invoke it with the `this` binding
10849    * of `thisArg` and `partials` prepended to the arguments it receives.
10850    *
10851    * @private
10852    * @param {Function} func The function to wrap.
10853    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10854    *  for more details.
10855    * @param {*} thisArg The `this` binding of `func`.
10856    * @param {Array} partials The arguments to prepend to those provided to
10857    *  the new function.
10858    * @returns {Function} Returns the new wrapped function.
10859    */
10860   function createPartialWrapper(func, bitmask, thisArg, partials) {
10861     var isBind = bitmask & BIND_FLAG,
10862         Ctor = createCtorWrapper(func);
10863
10864     function wrapper() {
10865       var argsIndex = -1,
10866           argsLength = arguments.length,
10867           leftIndex = -1,
10868           leftLength = partials.length,
10869           args = Array(leftLength + argsLength),
10870           fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10871
10872       while (++leftIndex < leftLength) {
10873         args[leftIndex] = partials[leftIndex];
10874       }
10875       while (argsLength--) {
10876         args[leftIndex++] = arguments[++argsIndex];
10877       }
10878       return apply(fn, isBind ? thisArg : this, args);
10879     }
10880     return wrapper;
10881   }
10882
10883   /**
10884    * Creates a function that wraps `func` to continue currying.
10885    *
10886    * @private
10887    * @param {Function} func The function to wrap.
10888    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10889    *  for more details.
10890    * @param {Function} wrapFunc The function to create the `func` wrapper.
10891    * @param {*} placeholder The placeholder value.
10892    * @param {*} [thisArg] The `this` binding of `func`.
10893    * @param {Array} [partials] The arguments to prepend to those provided to
10894    *  the new function.
10895    * @param {Array} [holders] The `partials` placeholder indexes.
10896    * @param {Array} [argPos] The argument positions of the new function.
10897    * @param {number} [ary] The arity cap of `func`.
10898    * @param {number} [arity] The arity of `func`.
10899    * @returns {Function} Returns the new wrapped function.
10900    */
10901   function createRecurryWrapper(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
10902     var isCurry = bitmask & CURRY_FLAG,
10903         newHolders = isCurry ? holders : undefined,
10904         newHoldersRight = isCurry ? undefined : holders,
10905         newPartials = isCurry ? partials : undefined,
10906         newPartialsRight = isCurry ? undefined : partials;
10907
10908     bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10909     bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10910
10911     if (!(bitmask & CURRY_BOUND_FLAG)) {
10912       bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10913     }
10914     var newData = [
10915       func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
10916       newHoldersRight, argPos, ary, arity
10917     ];
10918
10919     var result = wrapFunc.apply(undefined, newData);
10920     if (isLaziable(func)) {
10921       setData(result, newData);
10922     }
10923     result.placeholder = placeholder;
10924     return result;
10925   }
10926
10927   /**
10928    * Creates a set of `values`.
10929    *
10930    * @private
10931    * @param {Array} values The values to add to the set.
10932    * @returns {Object} Returns the new set.
10933    */
10934   var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
10935     return new Set(values);
10936   };
10937
10938   /**
10939    * Creates a `_.toPairs` or `_.toPairsIn` function.
10940    *
10941    * @private
10942    * @param {Function} keysFunc The function to get the keys of a given object.
10943    * @returns {Function} Returns the new pairs function.
10944    */
10945   function createToPairs(keysFunc) {
10946     return function(object) {
10947       var tag = getTag(object);
10948       if (tag == mapTag) {
10949         return mapToArray(object);
10950       }
10951       if (tag == setTag) {
10952         return setToPairs(object);
10953       }
10954       return baseToPairs(object, keysFunc(object));
10955     };
10956   }
10957
10958   /**
10959    * Creates a function that either curries or invokes `func` with optional
10960    * `this` binding and partially applied arguments.
10961    *
10962    * @private
10963    * @param {Function|string} func The function or method name to wrap.
10964    * @param {number} bitmask The bitmask of wrapper flags.
10965    *  The bitmask may be composed of the following flags:
10966    *     1 - `_.bind`
10967    *     2 - `_.bindKey`
10968    *     4 - `_.curry` or `_.curryRight` of a bound function
10969    *     8 - `_.curry`
10970    *    16 - `_.curryRight`
10971    *    32 - `_.partial`
10972    *    64 - `_.partialRight`
10973    *   128 - `_.rearg`
10974    *   256 - `_.ary`
10975    *   512 - `_.flip`
10976    * @param {*} [thisArg] The `this` binding of `func`.
10977    * @param {Array} [partials] The arguments to be partially applied.
10978    * @param {Array} [holders] The `partials` placeholder indexes.
10979    * @param {Array} [argPos] The argument positions of the new function.
10980    * @param {number} [ary] The arity cap of `func`.
10981    * @param {number} [arity] The arity of `func`.
10982    * @returns {Function} Returns the new wrapped function.
10983    */
10984   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10985     var isBindKey = bitmask & BIND_KEY_FLAG;
10986     if (!isBindKey && typeof func != 'function') {
10987       throw new TypeError(FUNC_ERROR_TEXT);
10988     }
10989     var length = partials ? partials.length : 0;
10990     if (!length) {
10991       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10992       partials = holders = undefined;
10993     }
10994     ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
10995     arity = arity === undefined ? arity : toInteger(arity);
10996     length -= holders ? holders.length : 0;
10997
10998     if (bitmask & PARTIAL_RIGHT_FLAG) {
10999       var partialsRight = partials,
11000           holdersRight = holders;
11001
11002       partials = holders = undefined;
11003     }
11004     var data = isBindKey ? undefined : getData(func);
11005
11006     var newData = [
11007       func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
11008       argPos, ary, arity
11009     ];
11010
11011     if (data) {
11012       mergeData(newData, data);
11013     }
11014     func = newData[0];
11015     bitmask = newData[1];
11016     thisArg = newData[2];
11017     partials = newData[3];
11018     holders = newData[4];
11019     arity = newData[9] = newData[9] == null
11020       ? (isBindKey ? 0 : func.length)
11021       : nativeMax(newData[9] - length, 0);
11022
11023     if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {
11024       bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);
11025     }
11026     if (!bitmask || bitmask == BIND_FLAG) {
11027       var result = createBaseWrapper(func, bitmask, thisArg);
11028     } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {
11029       result = createCurryWrapper(func, bitmask, arity);
11030     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {
11031       result = createPartialWrapper(func, bitmask, thisArg, partials);
11032     } else {
11033       result = createHybridWrapper.apply(undefined, newData);
11034     }
11035     var setter = data ? baseSetData : setData;
11036     return setter(result, newData);
11037   }
11038
11039   /**
11040    * A specialized version of `baseIsEqualDeep` for arrays with support for
11041    * partial deep comparisons.
11042    *
11043    * @private
11044    * @param {Array} array The array to compare.
11045    * @param {Array} other The other array to compare.
11046    * @param {Function} equalFunc The function to determine equivalents of values.
11047    * @param {Function} customizer The function to customize comparisons.
11048    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11049    *  for more details.
11050    * @param {Object} stack Tracks traversed `array` and `other` objects.
11051    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
11052    */
11053   function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
11054     var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
11055         arrLength = array.length,
11056         othLength = other.length;
11057
11058     if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
11059       return false;
11060     }
11061     // Assume cyclic values are equal.
11062     var stacked = stack.get(array);
11063     if (stacked) {
11064       return stacked == other;
11065     }
11066     var index = -1,
11067         result = true,
11068         seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;
11069
11070     stack.set(array, other);
11071
11072     // Ignore non-index properties.
11073     while (++index < arrLength) {
11074       var arrValue = array[index],
11075           othValue = other[index];
11076
11077       if (customizer) {
11078         var compared = isPartial
11079           ? customizer(othValue, arrValue, index, other, array, stack)
11080           : customizer(arrValue, othValue, index, array, other, stack);
11081       }
11082       if (compared !== undefined) {
11083         if (compared) {
11084           continue;
11085         }
11086         result = false;
11087         break;
11088       }
11089       // Recursively compare arrays (susceptible to call stack limits).
11090       if (seen) {
11091         if (!arraySome(other, function(othValue, othIndex) {
11092               if (!seen.has(othIndex) &&
11093                   (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {
11094                 return seen.add(othIndex);
11095               }
11096             })) {
11097           result = false;
11098           break;
11099         }
11100       } else if (!(
11101             arrValue === othValue ||
11102               equalFunc(arrValue, othValue, customizer, bitmask, stack)
11103           )) {
11104         result = false;
11105         break;
11106       }
11107     }
11108     stack['delete'](array);
11109     return result;
11110   }
11111
11112   /**
11113    * A specialized version of `baseIsEqualDeep` for comparing objects of
11114    * the same `toStringTag`.
11115    *
11116    * **Note:** This function only supports comparing values with tags of
11117    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
11118    *
11119    * @private
11120    * @param {Object} object The object to compare.
11121    * @param {Object} other The other object to compare.
11122    * @param {string} tag The `toStringTag` of the objects to compare.
11123    * @param {Function} equalFunc The function to determine equivalents of values.
11124    * @param {Function} customizer The function to customize comparisons.
11125    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11126    *  for more details.
11127    * @param {Object} stack Tracks traversed `object` and `other` objects.
11128    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
11129    */
11130   function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {
11131     switch (tag) {
11132       case dataViewTag:
11133         if ((object.byteLength != other.byteLength) ||
11134             (object.byteOffset != other.byteOffset)) {
11135           return false;
11136         }
11137         object = object.buffer;
11138         other = other.buffer;
11139
11140       case arrayBufferTag:
11141         if ((object.byteLength != other.byteLength) ||
11142             !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
11143           return false;
11144         }
11145         return true;
11146
11147       case boolTag:
11148       case dateTag:
11149         // Coerce dates and booleans to numbers, dates to milliseconds and
11150         // booleans to `1` or `0` treating invalid dates coerced to `NaN` as
11151         // not equal.
11152         return +object == +other;
11153
11154       case errorTag:
11155         return object.name == other.name && object.message == other.message;
11156
11157       case numberTag:
11158         // Treat `NaN` vs. `NaN` as equal.
11159         return (object != +object) ? other != +other : object == +other;
11160
11161       case regexpTag:
11162       case stringTag:
11163         // Coerce regexes to strings and treat strings, primitives and objects,
11164         // as equal. See http://www.ecma-international.org/ecma-262/6.0/#sec-regexp.prototype.tostring
11165         // for more details.
11166         return object == (other + '');
11167
11168       case mapTag:
11169         var convert = mapToArray;
11170
11171       case setTag:
11172         var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
11173         convert || (convert = setToArray);
11174
11175         if (object.size != other.size && !isPartial) {
11176           return false;
11177         }
11178         // Assume cyclic values are equal.
11179         var stacked = stack.get(object);
11180         if (stacked) {
11181           return stacked == other;
11182         }
11183         bitmask |= UNORDERED_COMPARE_FLAG;
11184         stack.set(object, other);
11185
11186         // Recursively compare objects (susceptible to call stack limits).
11187         return equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);
11188
11189       case symbolTag:
11190         if (symbolValueOf) {
11191           return symbolValueOf.call(object) == symbolValueOf.call(other);
11192         }
11193     }
11194     return false;
11195   }
11196
11197   /**
11198    * A specialized version of `baseIsEqualDeep` for objects with support for
11199    * partial deep comparisons.
11200    *
11201    * @private
11202    * @param {Object} object The object to compare.
11203    * @param {Object} other The other object to compare.
11204    * @param {Function} equalFunc The function to determine equivalents of values.
11205    * @param {Function} customizer The function to customize comparisons.
11206    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11207    *  for more details.
11208    * @param {Object} stack Tracks traversed `object` and `other` objects.
11209    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
11210    */
11211   function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {
11212     var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
11213         objProps = keys(object),
11214         objLength = objProps.length,
11215         othProps = keys(other),
11216         othLength = othProps.length;
11217
11218     if (objLength != othLength && !isPartial) {
11219       return false;
11220     }
11221     var index = objLength;
11222     while (index--) {
11223       var key = objProps[index];
11224       if (!(isPartial ? key in other : baseHas(other, key))) {
11225         return false;
11226       }
11227     }
11228     // Assume cyclic values are equal.
11229     var stacked = stack.get(object);
11230     if (stacked) {
11231       return stacked == other;
11232     }
11233     var result = true;
11234     stack.set(object, other);
11235
11236     var skipCtor = isPartial;
11237     while (++index < objLength) {
11238       key = objProps[index];
11239       var objValue = object[key],
11240           othValue = other[key];
11241
11242       if (customizer) {
11243         var compared = isPartial
11244           ? customizer(othValue, objValue, key, other, object, stack)
11245           : customizer(objValue, othValue, key, object, other, stack);
11246       }
11247       // Recursively compare objects (susceptible to call stack limits).
11248       if (!(compared === undefined
11249             ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))
11250             : compared
11251           )) {
11252         result = false;
11253         break;
11254       }
11255       skipCtor || (skipCtor = key == 'constructor');
11256     }
11257     if (result && !skipCtor) {
11258       var objCtor = object.constructor,
11259           othCtor = other.constructor;
11260
11261       // Non `Object` object instances with different constructors are not equal.
11262       if (objCtor != othCtor &&
11263           ('constructor' in object && 'constructor' in other) &&
11264           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
11265             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
11266         result = false;
11267       }
11268     }
11269     stack['delete'](object);
11270     return result;
11271   }
11272
11273   /**
11274    * Creates an array of own enumerable property names and symbols of `object`.
11275    *
11276    * @private
11277    * @param {Object} object The object to query.
11278    * @returns {Array} Returns the array of property names and symbols.
11279    */
11280   function getAllKeys(object) {
11281     return baseGetAllKeys(object, keys, getSymbols);
11282   }
11283
11284   /**
11285    * Creates an array of own and inherited enumerable property names and
11286    * symbols of `object`.
11287    *
11288    * @private
11289    * @param {Object} object The object to query.
11290    * @returns {Array} Returns the array of property names and symbols.
11291    */
11292   function getAllKeysIn(object) {
11293     return baseGetAllKeys(object, keysIn, getSymbolsIn);
11294   }
11295
11296   /**
11297    * Gets metadata for `func`.
11298    *
11299    * @private
11300    * @param {Function} func The function to query.
11301    * @returns {*} Returns the metadata for `func`.
11302    */
11303   var getData = !metaMap ? noop : function(func) {
11304     return metaMap.get(func);
11305   };
11306
11307   /**
11308    * Gets the name of `func`.
11309    *
11310    * @private
11311    * @param {Function} func The function to query.
11312    * @returns {string} Returns the function name.
11313    */
11314   function getFuncName(func) {
11315     var result = (func.name + ''),
11316         array = realNames[result],
11317         length = hasOwnProperty.call(realNames, result) ? array.length : 0;
11318
11319     while (length--) {
11320       var data = array[length],
11321           otherFunc = data.func;
11322       if (otherFunc == null || otherFunc == func) {
11323         return data.name;
11324       }
11325     }
11326     return result;
11327   }
11328
11329   /**
11330    * Gets the argument placeholder value for `func`.
11331    *
11332    * @private
11333    * @param {Function} func The function to inspect.
11334    * @returns {*} Returns the placeholder value.
11335    */
11336   function getHolder(func) {
11337     var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
11338     return object.placeholder;
11339   }
11340
11341   /**
11342    * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
11343    * this function returns the custom method, otherwise it returns `baseIteratee`.
11344    * If arguments are provided, the chosen function is invoked with them and
11345    * its result is returned.
11346    *
11347    * @private
11348    * @param {*} [value] The value to convert to an iteratee.
11349    * @param {number} [arity] The arity of the created iteratee.
11350    * @returns {Function} Returns the chosen function or its result.
11351    */
11352   function getIteratee() {
11353     var result = lodash.iteratee || iteratee;
11354     result = result === iteratee ? baseIteratee : result;
11355     return arguments.length ? result(arguments[0], arguments[1]) : result;
11356   }
11357
11358   /**
11359    * Gets the "length" property value of `object`.
11360    *
11361    * **Note:** This function is used to avoid a
11362    * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects
11363    * Safari on at least iOS 8.1-8.3 ARM64.
11364    *
11365    * @private
11366    * @param {Object} object The object to query.
11367    * @returns {*} Returns the "length" value.
11368    */
11369   var getLength = baseProperty('length');
11370
11371   /**
11372    * Gets the data for `map`.
11373    *
11374    * @private
11375    * @param {Object} map The map to query.
11376    * @param {string} key The reference key.
11377    * @returns {*} Returns the map data.
11378    */
11379   function getMapData(map, key) {
11380     var data = map.__data__;
11381     return isKeyable(key)
11382       ? data[typeof key == 'string' ? 'string' : 'hash']
11383       : data.map;
11384   }
11385
11386   /**
11387    * Gets the property names, values, and compare flags of `object`.
11388    *
11389    * @private
11390    * @param {Object} object The object to query.
11391    * @returns {Array} Returns the match data of `object`.
11392    */
11393   function getMatchData(object) {
11394     var result = toPairs(object),
11395         length = result.length;
11396
11397     while (length--) {
11398       result[length][2] = isStrictComparable(result[length][1]);
11399     }
11400     return result;
11401   }
11402
11403   /**
11404    * Gets the native function at `key` of `object`.
11405    *
11406    * @private
11407    * @param {Object} object The object to query.
11408    * @param {string} key The key of the method to get.
11409    * @returns {*} Returns the function if it's native, else `undefined`.
11410    */
11411   function getNative(object, key) {
11412     var value = object[key];
11413     return isNative(value) ? value : undefined;
11414   }
11415
11416   /**
11417    * Gets the `[[Prototype]]` of `value`.
11418    *
11419    * @private
11420    * @param {*} value The value to query.
11421    * @returns {null|Object} Returns the `[[Prototype]]`.
11422    */
11423   function getPrototype(value) {
11424     return nativeGetPrototype(Object(value));
11425   }
11426
11427   /**
11428    * Creates an array of the own enumerable symbol properties of `object`.
11429    *
11430    * @private
11431    * @param {Object} object The object to query.
11432    * @returns {Array} Returns the array of symbols.
11433    */
11434   function getSymbols(object) {
11435     // Coerce `object` to an object to avoid non-object errors in V8.
11436     // See https://bugs.chromium.org/p/v8/issues/detail?id=3443 for more details.
11437     return getOwnPropertySymbols(Object(object));
11438   }
11439
11440   // Fallback for IE < 11.
11441   if (!getOwnPropertySymbols) {
11442     getSymbols = function() {
11443       return [];
11444     };
11445   }
11446
11447   /**
11448    * Creates an array of the own and inherited enumerable symbol properties
11449    * of `object`.
11450    *
11451    * @private
11452    * @param {Object} object The object to query.
11453    * @returns {Array} Returns the array of symbols.
11454    */
11455   var getSymbolsIn = !getOwnPropertySymbols ? getSymbols : function(object) {
11456     var result = [];
11457     while (object) {
11458       arrayPush(result, getSymbols(object));
11459       object = getPrototype(object);
11460     }
11461     return result;
11462   };
11463
11464   /**
11465    * Gets the `toStringTag` of `value`.
11466    *
11467    * @private
11468    * @param {*} value The value to query.
11469    * @returns {string} Returns the `toStringTag`.
11470    */
11471   function getTag(value) {
11472     return objectToString.call(value);
11473   }
11474
11475   // Fallback for data views, maps, sets, and weak maps in IE 11,
11476   // for data views in Edge, and promises in Node.js.
11477   if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
11478       (Map && getTag(new Map) != mapTag) ||
11479       (Promise && getTag(Promise.resolve()) != promiseTag) ||
11480       (Set && getTag(new Set) != setTag) ||
11481       (WeakMap && getTag(new WeakMap) != weakMapTag)) {
11482     getTag = function(value) {
11483       var result = objectToString.call(value),
11484           Ctor = result == objectTag ? value.constructor : undefined,
11485           ctorString = Ctor ? toSource(Ctor) : undefined;
11486
11487       if (ctorString) {
11488         switch (ctorString) {
11489           case dataViewCtorString: return dataViewTag;
11490           case mapCtorString: return mapTag;
11491           case promiseCtorString: return promiseTag;
11492           case setCtorString: return setTag;
11493           case weakMapCtorString: return weakMapTag;
11494         }
11495       }
11496       return result;
11497     };
11498   }
11499
11500   /**
11501    * Gets the view, applying any `transforms` to the `start` and `end` positions.
11502    *
11503    * @private
11504    * @param {number} start The start of the view.
11505    * @param {number} end The end of the view.
11506    * @param {Array} transforms The transformations to apply to the view.
11507    * @returns {Object} Returns an object containing the `start` and `end`
11508    *  positions of the view.
11509    */
11510   function getView(start, end, transforms) {
11511     var index = -1,
11512         length = transforms.length;
11513
11514     while (++index < length) {
11515       var data = transforms[index],
11516           size = data.size;
11517
11518       switch (data.type) {
11519         case 'drop':      start += size; break;
11520         case 'dropRight': end -= size; break;
11521         case 'take':      end = nativeMin(end, start + size); break;
11522         case 'takeRight': start = nativeMax(start, end - size); break;
11523       }
11524     }
11525     return { 'start': start, 'end': end };
11526   }
11527
11528   /**
11529    * Checks if `path` exists on `object`.
11530    *
11531    * @private
11532    * @param {Object} object The object to query.
11533    * @param {Array|string} path The path to check.
11534    * @param {Function} hasFunc The function to check properties.
11535    * @returns {boolean} Returns `true` if `path` exists, else `false`.
11536    */
11537   function hasPath(object, path, hasFunc) {
11538     path = isKey(path, object) ? [path] : castPath(path);
11539
11540     var result,
11541         index = -1,
11542         length = path.length;
11543
11544     while (++index < length) {
11545       var key = toKey(path[index]);
11546       if (!(result = object != null && hasFunc(object, key))) {
11547         break;
11548       }
11549       object = object[key];
11550     }
11551     if (result) {
11552       return result;
11553     }
11554     var length = object ? object.length : 0;
11555     return !!length && isLength(length) && isIndex(key, length) &&
11556       (isArray(object) || isString(object) || isArguments(object));
11557   }
11558
11559   /**
11560    * Initializes an array clone.
11561    *
11562    * @private
11563    * @param {Array} array The array to clone.
11564    * @returns {Array} Returns the initialized clone.
11565    */
11566   function initCloneArray(array) {
11567     var length = array.length,
11568         result = array.constructor(length);
11569
11570     // Add properties assigned by `RegExp#exec`.
11571     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
11572       result.index = array.index;
11573       result.input = array.input;
11574     }
11575     return result;
11576   }
11577
11578   /**
11579    * Initializes an object clone.
11580    *
11581    * @private
11582    * @param {Object} object The object to clone.
11583    * @returns {Object} Returns the initialized clone.
11584    */
11585   function initCloneObject(object) {
11586     return (typeof object.constructor == 'function' && !isPrototype(object))
11587       ? baseCreate(getPrototype(object))
11588       : {};
11589   }
11590
11591   /**
11592    * Initializes an object clone based on its `toStringTag`.
11593    *
11594    * **Note:** This function only supports cloning values with tags of
11595    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
11596    *
11597    * @private
11598    * @param {Object} object The object to clone.
11599    * @param {string} tag The `toStringTag` of the object to clone.
11600    * @param {Function} cloneFunc The function to clone values.
11601    * @param {boolean} [isDeep] Specify a deep clone.
11602    * @returns {Object} Returns the initialized clone.
11603    */
11604   function initCloneByTag(object, tag, cloneFunc, isDeep) {
11605     var Ctor = object.constructor;
11606     switch (tag) {
11607       case arrayBufferTag:
11608         return cloneArrayBuffer(object);
11609
11610       case boolTag:
11611       case dateTag:
11612         return new Ctor(+object);
11613
11614       case dataViewTag:
11615         return cloneDataView(object, isDeep);
11616
11617       case float32Tag: case float64Tag:
11618       case int8Tag: case int16Tag: case int32Tag:
11619       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
11620         return cloneTypedArray(object, isDeep);
11621
11622       case mapTag:
11623         return cloneMap(object, isDeep, cloneFunc);
11624
11625       case numberTag:
11626       case stringTag:
11627         return new Ctor(object);
11628
11629       case regexpTag:
11630         return cloneRegExp(object);
11631
11632       case setTag:
11633         return cloneSet(object, isDeep, cloneFunc);
11634
11635       case symbolTag:
11636         return cloneSymbol(object);
11637     }
11638   }
11639
11640   /**
11641    * Creates an array of index keys for `object` values of arrays,
11642    * `arguments` objects, and strings, otherwise `null` is returned.
11643    *
11644    * @private
11645    * @param {Object} object The object to query.
11646    * @returns {Array|null} Returns index keys, else `null`.
11647    */
11648   function indexKeys(object) {
11649     var length = object ? object.length : undefined;
11650     if (isLength(length) &&
11651         (isArray(object) || isString(object) || isArguments(object))) {
11652       return baseTimes(length, String);
11653     }
11654     return null;
11655   }
11656
11657   /**
11658    * Checks if `value` is a flattenable `arguments` object or array.
11659    *
11660    * @private
11661    * @param {*} value The value to check.
11662    * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
11663    */
11664   function isFlattenable(value) {
11665     return isArray(value) || isArguments(value);
11666   }
11667
11668   /**
11669    * Checks if `value` is a valid array-like index.
11670    *
11671    * @private
11672    * @param {*} value The value to check.
11673    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
11674    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
11675    */
11676   function isIndex(value, length) {
11677     length = length == null ? MAX_SAFE_INTEGER : length;
11678     return !!length &&
11679       (typeof value == 'number' || reIsUint.test(value)) &&
11680       (value > -1 && value % 1 == 0 && value < length);
11681   }
11682
11683   /**
11684    * Checks if the given arguments are from an iteratee call.
11685    *
11686    * @private
11687    * @param {*} value The potential iteratee value argument.
11688    * @param {*} index The potential iteratee index or key argument.
11689    * @param {*} object The potential iteratee object argument.
11690    * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
11691    *  else `false`.
11692    */
11693   function isIterateeCall(value, index, object) {
11694     if (!isObject(object)) {
11695       return false;
11696     }
11697     var type = typeof index;
11698     if (type == 'number'
11699           ? (isArrayLike(object) && isIndex(index, object.length))
11700           : (type == 'string' && index in object)
11701         ) {
11702       return eq(object[index], value);
11703     }
11704     return false;
11705   }
11706
11707   /**
11708    * Checks if `value` is a property name and not a property path.
11709    *
11710    * @private
11711    * @param {*} value The value to check.
11712    * @param {Object} [object] The object to query keys on.
11713    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
11714    */
11715   function isKey(value, object) {
11716     if (isArray(value)) {
11717       return false;
11718     }
11719     var type = typeof value;
11720     if (type == 'number' || type == 'symbol' || type == 'boolean' ||
11721         value == null || isSymbol(value)) {
11722       return true;
11723     }
11724     return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
11725       (object != null && value in Object(object));
11726   }
11727
11728   /**
11729    * Checks if `value` is suitable for use as unique object key.
11730    *
11731    * @private
11732    * @param {*} value The value to check.
11733    * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
11734    */
11735   function isKeyable(value) {
11736     var type = typeof value;
11737     return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
11738       ? (value !== '__proto__')
11739       : (value === null);
11740   }
11741
11742   /**
11743    * Checks if `func` has a lazy counterpart.
11744    *
11745    * @private
11746    * @param {Function} func The function to check.
11747    * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
11748    *  else `false`.
11749    */
11750   function isLaziable(func) {
11751     var funcName = getFuncName(func),
11752         other = lodash[funcName];
11753
11754     if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
11755       return false;
11756     }
11757     if (func === other) {
11758       return true;
11759     }
11760     var data = getData(other);
11761     return !!data && func === data[0];
11762   }
11763
11764   /**
11765    * Checks if `value` is likely a prototype object.
11766    *
11767    * @private
11768    * @param {*} value The value to check.
11769    * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
11770    */
11771   function isPrototype(value) {
11772     var Ctor = value && value.constructor,
11773         proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
11774
11775     return value === proto;
11776   }
11777
11778   /**
11779    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
11780    *
11781    * @private
11782    * @param {*} value The value to check.
11783    * @returns {boolean} Returns `true` if `value` if suitable for strict
11784    *  equality comparisons, else `false`.
11785    */
11786   function isStrictComparable(value) {
11787     return value === value && !isObject(value);
11788   }
11789
11790   /**
11791    * A specialized version of `matchesProperty` for source values suitable
11792    * for strict equality comparisons, i.e. `===`.
11793    *
11794    * @private
11795    * @param {string} key The key of the property to get.
11796    * @param {*} srcValue The value to match.
11797    * @returns {Function} Returns the new spec function.
11798    */
11799   function matchesStrictComparable(key, srcValue) {
11800     return function(object) {
11801       if (object == null) {
11802         return false;
11803       }
11804       return object[key] === srcValue &&
11805         (srcValue !== undefined || (key in Object(object)));
11806     };
11807   }
11808
11809   /**
11810    * Merges the function metadata of `source` into `data`.
11811    *
11812    * Merging metadata reduces the number of wrappers used to invoke a function.
11813    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
11814    * may be applied regardless of execution order. Methods like `_.ary` and
11815    * `_.rearg` modify function arguments, making the order in which they are
11816    * executed important, preventing the merging of metadata. However, we make
11817    * an exception for a safe combined case where curried functions have `_.ary`
11818    * and or `_.rearg` applied.
11819    *
11820    * @private
11821    * @param {Array} data The destination metadata.
11822    * @param {Array} source The source metadata.
11823    * @returns {Array} Returns `data`.
11824    */
11825   function mergeData(data, source) {
11826     var bitmask = data[1],
11827         srcBitmask = source[1],
11828         newBitmask = bitmask | srcBitmask,
11829         isCommon = newBitmask < (BIND_FLAG | BIND_KEY_FLAG | ARY_FLAG);
11830
11831     var isCombo =
11832       ((srcBitmask == ARY_FLAG) && (bitmask == CURRY_FLAG)) ||
11833       ((srcBitmask == ARY_FLAG) && (bitmask == REARG_FLAG) && (data[7].length <= source[8])) ||
11834       ((srcBitmask == (ARY_FLAG | REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == CURRY_FLAG));
11835
11836     // Exit early if metadata can't be merged.
11837     if (!(isCommon || isCombo)) {
11838       return data;
11839     }
11840     // Use source `thisArg` if available.
11841     if (srcBitmask & BIND_FLAG) {
11842       data[2] = source[2];
11843       // Set when currying a bound function.
11844       newBitmask |= bitmask & BIND_FLAG ? 0 : CURRY_BOUND_FLAG;
11845     }
11846     // Compose partial arguments.
11847     var value = source[3];
11848     if (value) {
11849       var partials = data[3];
11850       data[3] = partials ? composeArgs(partials, value, source[4]) : value;
11851       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
11852     }
11853     // Compose partial right arguments.
11854     value = source[5];
11855     if (value) {
11856       partials = data[5];
11857       data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
11858       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
11859     }
11860     // Use source `argPos` if available.
11861     value = source[7];
11862     if (value) {
11863       data[7] = value;
11864     }
11865     // Use source `ary` if it's smaller.
11866     if (srcBitmask & ARY_FLAG) {
11867       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
11868     }
11869     // Use source `arity` if one is not provided.
11870     if (data[9] == null) {
11871       data[9] = source[9];
11872     }
11873     // Use source `func` and merge bitmasks.
11874     data[0] = source[0];
11875     data[1] = newBitmask;
11876
11877     return data;
11878   }
11879
11880   /**
11881    * Gets the parent value at `path` of `object`.
11882    *
11883    * @private
11884    * @param {Object} object The object to query.
11885    * @param {Array} path The path to get the parent value of.
11886    * @returns {*} Returns the parent value.
11887    */
11888   function parent(object, path) {
11889     return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
11890   }
11891
11892   /**
11893    * Reorder `array` according to the specified indexes where the element at
11894    * the first index is assigned as the first element, the element at
11895    * the second index is assigned as the second element, and so on.
11896    *
11897    * @private
11898    * @param {Array} array The array to reorder.
11899    * @param {Array} indexes The arranged array indexes.
11900    * @returns {Array} Returns `array`.
11901    */
11902   function reorder(array, indexes) {
11903     var arrLength = array.length,
11904         length = nativeMin(indexes.length, arrLength),
11905         oldArray = copyArray(array);
11906
11907     while (length--) {
11908       var index = indexes[length];
11909       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
11910     }
11911     return array;
11912   }
11913
11914   /**
11915    * Sets metadata for `func`.
11916    *
11917    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
11918    * period of time, it will trip its breaker and transition to an identity
11919    * function to avoid garbage collection pauses in V8. See
11920    * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
11921    * for more details.
11922    *
11923    * @private
11924    * @param {Function} func The function to associate metadata with.
11925    * @param {*} data The metadata.
11926    * @returns {Function} Returns `func`.
11927    */
11928   var setData = (function() {
11929     var count = 0,
11930         lastCalled = 0;
11931
11932     return function(key, value) {
11933       var stamp = now(),
11934           remaining = HOT_SPAN - (stamp - lastCalled);
11935
11936       lastCalled = stamp;
11937       if (remaining > 0) {
11938         if (++count >= HOT_COUNT) {
11939           return key;
11940         }
11941       } else {
11942         count = 0;
11943       }
11944       return baseSetData(key, value);
11945     };
11946   }());
11947
11948   /**
11949    * Converts `string` to a property path array.
11950    *
11951    * @private
11952    * @param {string} string The string to convert.
11953    * @returns {Array} Returns the property path array.
11954    */
11955   var stringToPath = memoize(function(string) {
11956     var result = [];
11957     toString(string).replace(rePropName, function(match, number, quote, string) {
11958       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
11959     });
11960     return result;
11961   });
11962
11963   /**
11964    * Converts `value` to a string key if it's not a string or symbol.
11965    *
11966    * @private
11967    * @param {*} value The value to inspect.
11968    * @returns {string|symbol} Returns the key.
11969    */
11970   function toKey(value) {
11971     if (typeof value == 'string' || isSymbol(value)) {
11972       return value;
11973     }
11974     var result = (value + '');
11975     return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
11976   }
11977
11978   /**
11979    * Converts `func` to its source code.
11980    *
11981    * @private
11982    * @param {Function} func The function to process.
11983    * @returns {string} Returns the source code.
11984    */
11985   function toSource(func) {
11986     if (func != null) {
11987       try {
11988         return funcToString.call(func);
11989       } catch (e) {}
11990       try {
11991         return (func + '');
11992       } catch (e) {}
11993     }
11994     return '';
11995   }
11996
11997   /**
11998    * Creates a clone of `wrapper`.
11999    *
12000    * @private
12001    * @param {Object} wrapper The wrapper to clone.
12002    * @returns {Object} Returns the cloned wrapper.
12003    */
12004   function wrapperClone(wrapper) {
12005     if (wrapper instanceof LazyWrapper) {
12006       return wrapper.clone();
12007     }
12008     var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
12009     result.__actions__ = copyArray(wrapper.__actions__);
12010     result.__index__  = wrapper.__index__;
12011     result.__values__ = wrapper.__values__;
12012     return result;
12013   }
12014
12015   /*------------------------------------------------------------------------*/
12016
12017   /**
12018    * Creates an array of elements split into groups the length of `size`.
12019    * If `array` can't be split evenly, the final chunk will be the remaining
12020    * elements.
12021    *
12022    * @static
12023    * @memberOf _
12024    * @since 3.0.0
12025    * @category Array
12026    * @param {Array} array The array to process.
12027    * @param {number} [size=1] The length of each chunk
12028    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
12029    * @returns {Array} Returns the new array of chunks.
12030    * @example
12031    *
12032    * _.chunk(['a', 'b', 'c', 'd'], 2);
12033    * // => [['a', 'b'], ['c', 'd']]
12034    *
12035    * _.chunk(['a', 'b', 'c', 'd'], 3);
12036    * // => [['a', 'b', 'c'], ['d']]
12037    */
12038   function chunk(array, size, guard) {
12039     if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
12040       size = 1;
12041     } else {
12042       size = nativeMax(toInteger(size), 0);
12043     }
12044     var length = array ? array.length : 0;
12045     if (!length || size < 1) {
12046       return [];
12047     }
12048     var index = 0,
12049         resIndex = 0,
12050         result = Array(nativeCeil(length / size));
12051
12052     while (index < length) {
12053       result[resIndex++] = baseSlice(array, index, (index += size));
12054     }
12055     return result;
12056   }
12057
12058   /**
12059    * Creates an array with all falsey values removed. The values `false`, `null`,
12060    * `0`, `""`, `undefined`, and `NaN` are falsey.
12061    *
12062    * @static
12063    * @memberOf _
12064    * @since 0.1.0
12065    * @category Array
12066    * @param {Array} array The array to compact.
12067    * @returns {Array} Returns the new array of filtered values.
12068    * @example
12069    *
12070    * _.compact([0, 1, false, 2, '', 3]);
12071    * // => [1, 2, 3]
12072    */
12073   function compact(array) {
12074     var index = -1,
12075         length = array ? array.length : 0,
12076         resIndex = 0,
12077         result = [];
12078
12079     while (++index < length) {
12080       var value = array[index];
12081       if (value) {
12082         result[resIndex++] = value;
12083       }
12084     }
12085     return result;
12086   }
12087
12088   /**
12089    * Creates an array of unique `array` values not included in the other given
12090    * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12091    * for equality comparisons. The order of result values is determined by the
12092    * order they occur in the first array.
12093    *
12094    * @static
12095    * @memberOf _
12096    * @since 0.1.0
12097    * @category Array
12098    * @param {Array} array The array to inspect.
12099    * @param {...Array} [values] The values to exclude.
12100    * @returns {Array} Returns the new array of filtered values.
12101    * @see _.without, _.xor
12102    * @example
12103    *
12104    * _.difference([3, 2, 1], [4, 2]);
12105    * // => [3, 1]
12106    */
12107   var difference = rest(function(array, values) {
12108     return isArrayLikeObject(array)
12109       ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
12110       : [];
12111   });
12112
12113   /**
12114    * Flattens `array` a single level deep.
12115    *
12116    * @static
12117    * @memberOf _
12118    * @since 0.1.0
12119    * @category Array
12120    * @param {Array} array The array to flatten.
12121    * @returns {Array} Returns the new flattened array.
12122    * @example
12123    *
12124    * _.flatten([1, [2, [3, [4]], 5]]);
12125    * // => [1, 2, [3, [4]], 5]
12126    */
12127   function flatten(array) {
12128     var length = array ? array.length : 0;
12129     return length ? baseFlatten(array, 1) : [];
12130   }
12131
12132   /**
12133    * Gets the first element of `array`.
12134    *
12135    * @static
12136    * @memberOf _
12137    * @since 0.1.0
12138    * @alias first
12139    * @category Array
12140    * @param {Array} array The array to query.
12141    * @returns {*} Returns the first element of `array`.
12142    * @example
12143    *
12144    * _.head([1, 2, 3]);
12145    * // => 1
12146    *
12147    * _.head([]);
12148    * // => undefined
12149    */
12150   function head(array) {
12151     return (array && array.length) ? array[0] : undefined;
12152   }
12153
12154   /**
12155    * Gets the index at which the first occurrence of `value` is found in `array`
12156    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12157    * for equality comparisons. If `fromIndex` is negative, it's used as the
12158    * offset from the end of `array`.
12159    *
12160    * @static
12161    * @memberOf _
12162    * @since 0.1.0
12163    * @category Array
12164    * @param {Array} array The array to search.
12165    * @param {*} value The value to search for.
12166    * @param {number} [fromIndex=0] The index to search from.
12167    * @returns {number} Returns the index of the matched value, else `-1`.
12168    * @example
12169    *
12170    * _.indexOf([1, 2, 1, 2], 2);
12171    * // => 1
12172    *
12173    * // Search from the `fromIndex`.
12174    * _.indexOf([1, 2, 1, 2], 2, 2);
12175    * // => 3
12176    */
12177   function indexOf(array, value, fromIndex) {
12178     var length = array ? array.length : 0;
12179     if (!length) {
12180       return -1;
12181     }
12182     fromIndex = toInteger(fromIndex);
12183     if (fromIndex < 0) {
12184       fromIndex = nativeMax(length + fromIndex, 0);
12185     }
12186     return baseIndexOf(array, value, fromIndex);
12187   }
12188
12189   /**
12190    * Creates an array of unique values that are included in all given arrays
12191    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12192    * for equality comparisons. The order of result values is determined by the
12193    * order they occur in the first array.
12194    *
12195    * @static
12196    * @memberOf _
12197    * @since 0.1.0
12198    * @category Array
12199    * @param {...Array} [arrays] The arrays to inspect.
12200    * @returns {Array} Returns the new array of intersecting values.
12201    * @example
12202    *
12203    * _.intersection([2, 1], [4, 2], [1, 2]);
12204    * // => [2]
12205    */
12206   var intersection = rest(function(arrays) {
12207     var mapped = arrayMap(arrays, castArrayLikeObject);
12208     return (mapped.length && mapped[0] === arrays[0])
12209       ? baseIntersection(mapped)
12210       : [];
12211   });
12212
12213   /**
12214    * Gets the last element of `array`.
12215    *
12216    * @static
12217    * @memberOf _
12218    * @since 0.1.0
12219    * @category Array
12220    * @param {Array} array The array to query.
12221    * @returns {*} Returns the last element of `array`.
12222    * @example
12223    *
12224    * _.last([1, 2, 3]);
12225    * // => 3
12226    */
12227   function last(array) {
12228     var length = array ? array.length : 0;
12229     return length ? array[length - 1] : undefined;
12230   }
12231
12232   /**
12233    * Reverses `array` so that the first element becomes the last, the second
12234    * element becomes the second to last, and so on.
12235    *
12236    * **Note:** This method mutates `array` and is based on
12237    * [`Array#reverse`](https://mdn.io/Array/reverse).
12238    *
12239    * @static
12240    * @memberOf _
12241    * @since 4.0.0
12242    * @category Array
12243    * @param {Array} array The array to modify.
12244    * @returns {Array} Returns `array`.
12245    * @example
12246    *
12247    * var array = [1, 2, 3];
12248    *
12249    * _.reverse(array);
12250    * // => [3, 2, 1]
12251    *
12252    * console.log(array);
12253    * // => [3, 2, 1]
12254    */
12255   function reverse(array) {
12256     return array ? nativeReverse.call(array) : array;
12257   }
12258
12259   /**
12260    * Creates an array of unique values, in order, from all given arrays using
12261    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12262    * for equality comparisons.
12263    *
12264    * @static
12265    * @memberOf _
12266    * @since 0.1.0
12267    * @category Array
12268    * @param {...Array} [arrays] The arrays to inspect.
12269    * @returns {Array} Returns the new array of combined values.
12270    * @example
12271    *
12272    * _.union([2, 1], [4, 2], [1, 2]);
12273    * // => [2, 1, 4]
12274    */
12275   var union = rest(function(arrays) {
12276     return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
12277   });
12278
12279   /**
12280    * Creates a duplicate-free version of an array, using
12281    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12282    * for equality comparisons, in which only the first occurrence of each
12283    * element is kept.
12284    *
12285    * @static
12286    * @memberOf _
12287    * @since 0.1.0
12288    * @category Array
12289    * @param {Array} array The array to inspect.
12290    * @returns {Array} Returns the new duplicate free array.
12291    * @example
12292    *
12293    * _.uniq([2, 1, 2]);
12294    * // => [2, 1]
12295    */
12296   function uniq(array) {
12297     return (array && array.length)
12298       ? baseUniq(array)
12299       : [];
12300   }
12301
12302   /**
12303    * Creates an array excluding all given values using
12304    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12305    * for equality comparisons.
12306    *
12307    * @static
12308    * @memberOf _
12309    * @since 0.1.0
12310    * @category Array
12311    * @param {Array} array The array to inspect.
12312    * @param {...*} [values] The values to exclude.
12313    * @returns {Array} Returns the new array of filtered values.
12314    * @see _.difference, _.xor
12315    * @example
12316    *
12317    * _.without([1, 2, 1, 3], 1, 2);
12318    * // => [3]
12319    */
12320   var without = rest(function(array, values) {
12321     return isArrayLikeObject(array)
12322       ? baseDifference(array, values)
12323       : [];
12324   });
12325
12326   /*------------------------------------------------------------------------*/
12327
12328   /**
12329    * Creates a `lodash` wrapper instance that wraps `value` with explicit method
12330    * chain sequences enabled. The result of such sequences must be unwrapped
12331    * with `_#value`.
12332    *
12333    * @static
12334    * @memberOf _
12335    * @since 1.3.0
12336    * @category Seq
12337    * @param {*} value The value to wrap.
12338    * @returns {Object} Returns the new `lodash` wrapper instance.
12339    * @example
12340    *
12341    * var users = [
12342    *   { 'user': 'barney',  'age': 36 },
12343    *   { 'user': 'fred',    'age': 40 },
12344    *   { 'user': 'pebbles', 'age': 1 }
12345    * ];
12346    *
12347    * var youngest = _
12348    *   .chain(users)
12349    *   .sortBy('age')
12350    *   .map(function(o) {
12351    *     return o.user + ' is ' + o.age;
12352    *   })
12353    *   .head()
12354    *   .value();
12355    * // => 'pebbles is 1'
12356    */
12357   function chain(value) {
12358     var result = lodash(value);
12359     result.__chain__ = true;
12360     return result;
12361   }
12362
12363   /**
12364    * This method invokes `interceptor` and returns `value`. The interceptor
12365    * is invoked with one argument; (value). The purpose of this method is to
12366    * "tap into" a method chain sequence in order to modify intermediate results.
12367    *
12368    * @static
12369    * @memberOf _
12370    * @since 0.1.0
12371    * @category Seq
12372    * @param {*} value The value to provide to `interceptor`.
12373    * @param {Function} interceptor The function to invoke.
12374    * @returns {*} Returns `value`.
12375    * @example
12376    *
12377    * _([1, 2, 3])
12378    *  .tap(function(array) {
12379    *    // Mutate input array.
12380    *    array.pop();
12381    *  })
12382    *  .reverse()
12383    *  .value();
12384    * // => [2, 1]
12385    */
12386   function tap(value, interceptor) {
12387     interceptor(value);
12388     return value;
12389   }
12390
12391   /**
12392    * This method is like `_.tap` except that it returns the result of `interceptor`.
12393    * The purpose of this method is to "pass thru" values replacing intermediate
12394    * results in a method chain sequence.
12395    *
12396    * @static
12397    * @memberOf _
12398    * @since 3.0.0
12399    * @category Seq
12400    * @param {*} value The value to provide to `interceptor`.
12401    * @param {Function} interceptor The function to invoke.
12402    * @returns {*} Returns the result of `interceptor`.
12403    * @example
12404    *
12405    * _('  abc  ')
12406    *  .chain()
12407    *  .trim()
12408    *  .thru(function(value) {
12409    *    return [value];
12410    *  })
12411    *  .value();
12412    * // => ['abc']
12413    */
12414   function thru(value, interceptor) {
12415     return interceptor(value);
12416   }
12417
12418   /**
12419    * This method is the wrapper version of `_.at`.
12420    *
12421    * @name at
12422    * @memberOf _
12423    * @since 1.0.0
12424    * @category Seq
12425    * @param {...(string|string[])} [paths] The property paths of elements to pick.
12426    * @returns {Object} Returns the new `lodash` wrapper instance.
12427    * @example
12428    *
12429    * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
12430    *
12431    * _(object).at(['a[0].b.c', 'a[1]']).value();
12432    * // => [3, 4]
12433    *
12434    * _(['a', 'b', 'c']).at(0, 2).value();
12435    * // => ['a', 'c']
12436    */
12437   var wrapperAt = rest(function(paths) {
12438     paths = baseFlatten(paths, 1);
12439     var length = paths.length,
12440         start = length ? paths[0] : 0,
12441         value = this.__wrapped__,
12442         interceptor = function(object) { return baseAt(object, paths); };
12443
12444     if (length > 1 || this.__actions__.length ||
12445         !(value instanceof LazyWrapper) || !isIndex(start)) {
12446       return this.thru(interceptor);
12447     }
12448     value = value.slice(start, +start + (length ? 1 : 0));
12449     value.__actions__.push({
12450       'func': thru,
12451       'args': [interceptor],
12452       'thisArg': undefined
12453     });
12454     return new LodashWrapper(value, this.__chain__).thru(function(array) {
12455       if (length && !array.length) {
12456         array.push(undefined);
12457       }
12458       return array;
12459     });
12460   });
12461
12462   /**
12463    * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
12464    *
12465    * @name chain
12466    * @memberOf _
12467    * @since 0.1.0
12468    * @category Seq
12469    * @returns {Object} Returns the new `lodash` wrapper instance.
12470    * @example
12471    *
12472    * var users = [
12473    *   { 'user': 'barney', 'age': 36 },
12474    *   { 'user': 'fred',   'age': 40 }
12475    * ];
12476    *
12477    * // A sequence without explicit chaining.
12478    * _(users).head();
12479    * // => { 'user': 'barney', 'age': 36 }
12480    *
12481    * // A sequence with explicit chaining.
12482    * _(users)
12483    *   .chain()
12484    *   .head()
12485    *   .pick('user')
12486    *   .value();
12487    * // => { 'user': 'barney' }
12488    */
12489   function wrapperChain() {
12490     return chain(this);
12491   }
12492
12493   /**
12494    * Executes the chain sequence and returns the wrapped result.
12495    *
12496    * @name commit
12497    * @memberOf _
12498    * @since 3.2.0
12499    * @category Seq
12500    * @returns {Object} Returns the new `lodash` wrapper instance.
12501    * @example
12502    *
12503    * var array = [1, 2];
12504    * var wrapped = _(array).push(3);
12505    *
12506    * console.log(array);
12507    * // => [1, 2]
12508    *
12509    * wrapped = wrapped.commit();
12510    * console.log(array);
12511    * // => [1, 2, 3]
12512    *
12513    * wrapped.last();
12514    * // => 3
12515    *
12516    * console.log(array);
12517    * // => [1, 2, 3]
12518    */
12519   function wrapperCommit() {
12520     return new LodashWrapper(this.value(), this.__chain__);
12521   }
12522
12523   /**
12524    * Gets the next value on a wrapped object following the
12525    * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
12526    *
12527    * @name next
12528    * @memberOf _
12529    * @since 4.0.0
12530    * @category Seq
12531    * @returns {Object} Returns the next iterator value.
12532    * @example
12533    *
12534    * var wrapped = _([1, 2]);
12535    *
12536    * wrapped.next();
12537    * // => { 'done': false, 'value': 1 }
12538    *
12539    * wrapped.next();
12540    * // => { 'done': false, 'value': 2 }
12541    *
12542    * wrapped.next();
12543    * // => { 'done': true, 'value': undefined }
12544    */
12545   function wrapperNext() {
12546     if (this.__values__ === undefined) {
12547       this.__values__ = toArray(this.value());
12548     }
12549     var done = this.__index__ >= this.__values__.length,
12550         value = done ? undefined : this.__values__[this.__index__++];
12551
12552     return { 'done': done, 'value': value };
12553   }
12554
12555   /**
12556    * Enables the wrapper to be iterable.
12557    *
12558    * @name Symbol.iterator
12559    * @memberOf _
12560    * @since 4.0.0
12561    * @category Seq
12562    * @returns {Object} Returns the wrapper object.
12563    * @example
12564    *
12565    * var wrapped = _([1, 2]);
12566    *
12567    * wrapped[Symbol.iterator]() === wrapped;
12568    * // => true
12569    *
12570    * Array.from(wrapped);
12571    * // => [1, 2]
12572    */
12573   function wrapperToIterator() {
12574     return this;
12575   }
12576
12577   /**
12578    * Creates a clone of the chain sequence planting `value` as the wrapped value.
12579    *
12580    * @name plant
12581    * @memberOf _
12582    * @since 3.2.0
12583    * @category Seq
12584    * @param {*} value The value to plant.
12585    * @returns {Object} Returns the new `lodash` wrapper instance.
12586    * @example
12587    *
12588    * function square(n) {
12589    *   return n * n;
12590    * }
12591    *
12592    * var wrapped = _([1, 2]).map(square);
12593    * var other = wrapped.plant([3, 4]);
12594    *
12595    * other.value();
12596    * // => [9, 16]
12597    *
12598    * wrapped.value();
12599    * // => [1, 4]
12600    */
12601   function wrapperPlant(value) {
12602     var result,
12603         parent = this;
12604
12605     while (parent instanceof baseLodash) {
12606       var clone = wrapperClone(parent);
12607       clone.__index__ = 0;
12608       clone.__values__ = undefined;
12609       if (result) {
12610         previous.__wrapped__ = clone;
12611       } else {
12612         result = clone;
12613       }
12614       var previous = clone;
12615       parent = parent.__wrapped__;
12616     }
12617     previous.__wrapped__ = value;
12618     return result;
12619   }
12620
12621   /**
12622    * This method is the wrapper version of `_.reverse`.
12623    *
12624    * **Note:** This method mutates the wrapped array.
12625    *
12626    * @name reverse
12627    * @memberOf _
12628    * @since 0.1.0
12629    * @category Seq
12630    * @returns {Object} Returns the new `lodash` wrapper instance.
12631    * @example
12632    *
12633    * var array = [1, 2, 3];
12634    *
12635    * _(array).reverse().value()
12636    * // => [3, 2, 1]
12637    *
12638    * console.log(array);
12639    * // => [3, 2, 1]
12640    */
12641   function wrapperReverse() {
12642     var value = this.__wrapped__;
12643     if (value instanceof LazyWrapper) {
12644       var wrapped = value;
12645       if (this.__actions__.length) {
12646         wrapped = new LazyWrapper(this);
12647       }
12648       wrapped = wrapped.reverse();
12649       wrapped.__actions__.push({
12650         'func': thru,
12651         'args': [reverse],
12652         'thisArg': undefined
12653       });
12654       return new LodashWrapper(wrapped, this.__chain__);
12655     }
12656     return this.thru(reverse);
12657   }
12658
12659   /**
12660    * Executes the chain sequence to resolve the unwrapped value.
12661    *
12662    * @name value
12663    * @memberOf _
12664    * @since 0.1.0
12665    * @alias toJSON, valueOf
12666    * @category Seq
12667    * @returns {*} Returns the resolved unwrapped value.
12668    * @example
12669    *
12670    * _([1, 2, 3]).value();
12671    * // => [1, 2, 3]
12672    */
12673   function wrapperValue() {
12674     return baseWrapperValue(this.__wrapped__, this.__actions__);
12675   }
12676
12677   /*------------------------------------------------------------------------*/
12678
12679   /**
12680    * Checks if `predicate` returns truthy for **all** elements of `collection`.
12681    * Iteration is stopped once `predicate` returns falsey. The predicate is
12682    * invoked with three arguments: (value, index|key, collection).
12683    *
12684    * @static
12685    * @memberOf _
12686    * @since 0.1.0
12687    * @category Collection
12688    * @param {Array|Object} collection The collection to iterate over.
12689    * @param {Array|Function|Object|string} [predicate=_.identity]
12690    *  The function invoked per iteration.
12691    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
12692    * @returns {boolean} Returns `true` if all elements pass the predicate check,
12693    *  else `false`.
12694    * @example
12695    *
12696    * _.every([true, 1, null, 'yes'], Boolean);
12697    * // => false
12698    *
12699    * var users = [
12700    *   { 'user': 'barney', 'age': 36, 'active': false },
12701    *   { 'user': 'fred',   'age': 40, 'active': false }
12702    * ];
12703    *
12704    * // The `_.matches` iteratee shorthand.
12705    * _.every(users, { 'user': 'barney', 'active': false });
12706    * // => false
12707    *
12708    * // The `_.matchesProperty` iteratee shorthand.
12709    * _.every(users, ['active', false]);
12710    * // => true
12711    *
12712    * // The `_.property` iteratee shorthand.
12713    * _.every(users, 'active');
12714    * // => false
12715    */
12716   function every(collection, predicate, guard) {
12717     var func = isArray(collection) ? arrayEvery : baseEvery;
12718     if (guard && isIterateeCall(collection, predicate, guard)) {
12719       predicate = undefined;
12720     }
12721     return func(collection, getIteratee(predicate, 3));
12722   }
12723
12724   /**
12725    * Iterates over elements of `collection`, returning an array of all elements
12726    * `predicate` returns truthy for. The predicate is invoked with three
12727    * arguments: (value, index|key, collection).
12728    *
12729    * @static
12730    * @memberOf _
12731    * @since 0.1.0
12732    * @category Collection
12733    * @param {Array|Object} collection The collection to iterate over.
12734    * @param {Array|Function|Object|string} [predicate=_.identity]
12735    *  The function invoked per iteration.
12736    * @returns {Array} Returns the new filtered array.
12737    * @see _.reject
12738    * @example
12739    *
12740    * var users = [
12741    *   { 'user': 'barney', 'age': 36, 'active': true },
12742    *   { 'user': 'fred',   'age': 40, 'active': false }
12743    * ];
12744    *
12745    * _.filter(users, function(o) { return !o.active; });
12746    * // => objects for ['fred']
12747    *
12748    * // The `_.matches` iteratee shorthand.
12749    * _.filter(users, { 'age': 36, 'active': true });
12750    * // => objects for ['barney']
12751    *
12752    * // The `_.matchesProperty` iteratee shorthand.
12753    * _.filter(users, ['active', false]);
12754    * // => objects for ['fred']
12755    *
12756    * // The `_.property` iteratee shorthand.
12757    * _.filter(users, 'active');
12758    * // => objects for ['barney']
12759    */
12760   function filter(collection, predicate) {
12761     var func = isArray(collection) ? arrayFilter : baseFilter;
12762     return func(collection, getIteratee(predicate, 3));
12763   }
12764
12765   /**
12766    * Iterates over elements of `collection`, returning the first element
12767    * `predicate` returns truthy for. The predicate is invoked with three
12768    * arguments: (value, index|key, collection).
12769    *
12770    * @static
12771    * @memberOf _
12772    * @since 0.1.0
12773    * @category Collection
12774    * @param {Array|Object} collection The collection to search.
12775    * @param {Array|Function|Object|string} [predicate=_.identity]
12776    *  The function invoked per iteration.
12777    * @returns {*} Returns the matched element, else `undefined`.
12778    * @example
12779    *
12780    * var users = [
12781    *   { 'user': 'barney',  'age': 36, 'active': true },
12782    *   { 'user': 'fred',    'age': 40, 'active': false },
12783    *   { 'user': 'pebbles', 'age': 1,  'active': true }
12784    * ];
12785    *
12786    * _.find(users, function(o) { return o.age < 40; });
12787    * // => object for 'barney'
12788    *
12789    * // The `_.matches` iteratee shorthand.
12790    * _.find(users, { 'age': 1, 'active': true });
12791    * // => object for 'pebbles'
12792    *
12793    * // The `_.matchesProperty` iteratee shorthand.
12794    * _.find(users, ['active', false]);
12795    * // => object for 'fred'
12796    *
12797    * // The `_.property` iteratee shorthand.
12798    * _.find(users, 'active');
12799    * // => object for 'barney'
12800    */
12801   function find(collection, predicate) {
12802     predicate = getIteratee(predicate, 3);
12803     if (isArray(collection)) {
12804       var index = baseFindIndex(collection, predicate);
12805       return index > -1 ? collection[index] : undefined;
12806     }
12807     return baseFind(collection, predicate, baseEach);
12808   }
12809
12810   /**
12811    * Iterates over elements of `collection` and invokes `iteratee` for each element.
12812    * The iteratee is invoked with three arguments: (value, index|key, collection).
12813    * Iteratee functions may exit iteration early by explicitly returning `false`.
12814    *
12815    * **Note:** As with other "Collections" methods, objects with a "length"
12816    * property are iterated like arrays. To avoid this behavior use `_.forIn`
12817    * or `_.forOwn` for object iteration.
12818    *
12819    * @static
12820    * @memberOf _
12821    * @since 0.1.0
12822    * @alias each
12823    * @category Collection
12824    * @param {Array|Object} collection The collection to iterate over.
12825    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12826    * @returns {Array|Object} Returns `collection`.
12827    * @see _.forEachRight
12828    * @example
12829    *
12830    * _([1, 2]).forEach(function(value) {
12831    *   console.log(value);
12832    * });
12833    * // => Logs `1` then `2`.
12834    *
12835    * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
12836    *   console.log(key);
12837    * });
12838    * // => Logs 'a' then 'b' (iteration order is not guaranteed).
12839    */
12840   function forEach(collection, iteratee) {
12841     var func = isArray(collection) ? arrayEach : baseEach;
12842     return func(collection, getIteratee(iteratee, 3));
12843   }
12844
12845   /**
12846    * Creates an object composed of keys generated from the results of running
12847    * each element of `collection` thru `iteratee`. The order of grouped values
12848    * is determined by the order they occur in `collection`. The corresponding
12849    * value of each key is an array of elements responsible for generating the
12850    * key. The iteratee is invoked with one argument: (value).
12851    *
12852    * @static
12853    * @memberOf _
12854    * @since 0.1.0
12855    * @category Collection
12856    * @param {Array|Object} collection The collection to iterate over.
12857    * @param {Array|Function|Object|string} [iteratee=_.identity]
12858    *  The iteratee to transform keys.
12859    * @returns {Object} Returns the composed aggregate object.
12860    * @example
12861    *
12862    * _.groupBy([6.1, 4.2, 6.3], Math.floor);
12863    * // => { '4': [4.2], '6': [6.1, 6.3] }
12864    *
12865    * // The `_.property` iteratee shorthand.
12866    * _.groupBy(['one', 'two', 'three'], 'length');
12867    * // => { '3': ['one', 'two'], '5': ['three'] }
12868    */
12869   var groupBy = createAggregator(function(result, value, key) {
12870     if (hasOwnProperty.call(result, key)) {
12871       result[key].push(value);
12872     } else {
12873       result[key] = [value];
12874     }
12875   });
12876
12877   /**
12878    * Checks if `value` is in `collection`. If `collection` is a string, it's
12879    * checked for a substring of `value`, otherwise
12880    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12881    * is used for equality comparisons. If `fromIndex` is negative, it's used as
12882    * the offset from the end of `collection`.
12883    *
12884    * @static
12885    * @memberOf _
12886    * @since 0.1.0
12887    * @category Collection
12888    * @param {Array|Object|string} collection The collection to search.
12889    * @param {*} value The value to search for.
12890    * @param {number} [fromIndex=0] The index to search from.
12891    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
12892    * @returns {boolean} Returns `true` if `value` is found, else `false`.
12893    * @example
12894    *
12895    * _.includes([1, 2, 3], 1);
12896    * // => true
12897    *
12898    * _.includes([1, 2, 3], 1, 2);
12899    * // => false
12900    *
12901    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
12902    * // => true
12903    *
12904    * _.includes('pebbles', 'eb');
12905    * // => true
12906    */
12907   function includes(collection, value, fromIndex, guard) {
12908     collection = isArrayLike(collection) ? collection : values(collection);
12909     fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;
12910
12911     var length = collection.length;
12912     if (fromIndex < 0) {
12913       fromIndex = nativeMax(length + fromIndex, 0);
12914     }
12915     return isString(collection)
12916       ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
12917       : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
12918   }
12919
12920   /**
12921    * Creates an array of values by running each element in `collection` thru
12922    * `iteratee`. The iteratee is invoked with three arguments:
12923    * (value, index|key, collection).
12924    *
12925    * Many lodash methods are guarded to work as iteratees for methods like
12926    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
12927    *
12928    * The guarded methods are:
12929    * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
12930    * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
12931    * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
12932    * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
12933    *
12934    * @static
12935    * @memberOf _
12936    * @since 0.1.0
12937    * @category Collection
12938    * @param {Array|Object} collection The collection to iterate over.
12939    * @param {Array|Function|Object|string} [iteratee=_.identity]
12940    *  The function invoked per iteration.
12941    * @returns {Array} Returns the new mapped array.
12942    * @example
12943    *
12944    * function square(n) {
12945    *   return n * n;
12946    * }
12947    *
12948    * _.map([4, 8], square);
12949    * // => [16, 64]
12950    *
12951    * _.map({ 'a': 4, 'b': 8 }, square);
12952    * // => [16, 64] (iteration order is not guaranteed)
12953    *
12954    * var users = [
12955    *   { 'user': 'barney' },
12956    *   { 'user': 'fred' }
12957    * ];
12958    *
12959    * // The `_.property` iteratee shorthand.
12960    * _.map(users, 'user');
12961    * // => ['barney', 'fred']
12962    */
12963   function map(collection, iteratee) {
12964     var func = isArray(collection) ? arrayMap : baseMap;
12965     return func(collection, getIteratee(iteratee, 3));
12966   }
12967
12968   /**
12969    * Reduces `collection` to a value which is the accumulated result of running
12970    * each element in `collection` thru `iteratee`, where each successive
12971    * invocation is supplied the return value of the previous. If `accumulator`
12972    * is not given, the first element of `collection` is used as the initial
12973    * value. The iteratee is invoked with four arguments:
12974    * (accumulator, value, index|key, collection).
12975    *
12976    * Many lodash methods are guarded to work as iteratees for methods like
12977    * `_.reduce`, `_.reduceRight`, and `_.transform`.
12978    *
12979    * The guarded methods are:
12980    * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
12981    * and `sortBy`
12982    *
12983    * @static
12984    * @memberOf _
12985    * @since 0.1.0
12986    * @category Collection
12987    * @param {Array|Object} collection The collection to iterate over.
12988    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12989    * @param {*} [accumulator] The initial value.
12990    * @returns {*} Returns the accumulated value.
12991    * @see _.reduceRight
12992    * @example
12993    *
12994    * _.reduce([1, 2], function(sum, n) {
12995    *   return sum + n;
12996    * }, 0);
12997    * // => 3
12998    *
12999    * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
13000    *   (result[value] || (result[value] = [])).push(key);
13001    *   return result;
13002    * }, {});
13003    * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
13004    */
13005   function reduce(collection, iteratee, accumulator) {
13006     var func = isArray(collection) ? arrayReduce : baseReduce,
13007         initAccum = arguments.length < 3;
13008
13009     return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
13010   }
13011
13012   /**
13013    * The opposite of `_.filter`; this method returns the elements of `collection`
13014    * that `predicate` does **not** return truthy for.
13015    *
13016    * @static
13017    * @memberOf _
13018    * @since 0.1.0
13019    * @category Collection
13020    * @param {Array|Object} collection The collection to iterate over.
13021    * @param {Array|Function|Object|string} [predicate=_.identity]
13022    *  The function invoked per iteration.
13023    * @returns {Array} Returns the new filtered array.
13024    * @see _.filter
13025    * @example
13026    *
13027    * var users = [
13028    *   { 'user': 'barney', 'age': 36, 'active': false },
13029    *   { 'user': 'fred',   'age': 40, 'active': true }
13030    * ];
13031    *
13032    * _.reject(users, function(o) { return !o.active; });
13033    * // => objects for ['fred']
13034    *
13035    * // The `_.matches` iteratee shorthand.
13036    * _.reject(users, { 'age': 40, 'active': true });
13037    * // => objects for ['barney']
13038    *
13039    * // The `_.matchesProperty` iteratee shorthand.
13040    * _.reject(users, ['active', false]);
13041    * // => objects for ['fred']
13042    *
13043    * // The `_.property` iteratee shorthand.
13044    * _.reject(users, 'active');
13045    * // => objects for ['barney']
13046    */
13047   function reject(collection, predicate) {
13048     var func = isArray(collection) ? arrayFilter : baseFilter;
13049     predicate = getIteratee(predicate, 3);
13050     return func(collection, function(value, index, collection) {
13051       return !predicate(value, index, collection);
13052     });
13053   }
13054
13055   /**
13056    * Checks if `predicate` returns truthy for **any** element of `collection`.
13057    * Iteration is stopped once `predicate` returns truthy. The predicate is
13058    * invoked with three arguments: (value, index|key, collection).
13059    *
13060    * @static
13061    * @memberOf _
13062    * @since 0.1.0
13063    * @category Collection
13064    * @param {Array|Object} collection The collection to iterate over.
13065    * @param {Array|Function|Object|string} [predicate=_.identity]
13066    *  The function invoked per iteration.
13067    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
13068    * @returns {boolean} Returns `true` if any element passes the predicate check,
13069    *  else `false`.
13070    * @example
13071    *
13072    * _.some([null, 0, 'yes', false], Boolean);
13073    * // => true
13074    *
13075    * var users = [
13076    *   { 'user': 'barney', 'active': true },
13077    *   { 'user': 'fred',   'active': false }
13078    * ];
13079    *
13080    * // The `_.matches` iteratee shorthand.
13081    * _.some(users, { 'user': 'barney', 'active': false });
13082    * // => false
13083    *
13084    * // The `_.matchesProperty` iteratee shorthand.
13085    * _.some(users, ['active', false]);
13086    * // => true
13087    *
13088    * // The `_.property` iteratee shorthand.
13089    * _.some(users, 'active');
13090    * // => true
13091    */
13092   function some(collection, predicate, guard) {
13093     var func = isArray(collection) ? arraySome : baseSome;
13094     if (guard && isIterateeCall(collection, predicate, guard)) {
13095       predicate = undefined;
13096     }
13097     return func(collection, getIteratee(predicate, 3));
13098   }
13099
13100   /*------------------------------------------------------------------------*/
13101
13102   /**
13103    * Gets the timestamp of the number of milliseconds that have elapsed since
13104    * the Unix epoch (1 January 1970 00:00:00 UTC).
13105    *
13106    * @static
13107    * @memberOf _
13108    * @since 2.4.0
13109    * @type {Function}
13110    * @category Date
13111    * @returns {number} Returns the timestamp.
13112    * @example
13113    *
13114    * _.defer(function(stamp) {
13115    *   console.log(_.now() - stamp);
13116    * }, _.now());
13117    * // => Logs the number of milliseconds it took for the deferred function to be invoked.
13118    */
13119   var now = Date.now;
13120
13121   /*------------------------------------------------------------------------*/
13122
13123   /**
13124    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13125    * and `partials` prepended to the arguments it receives.
13126    *
13127    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
13128    * may be used as a placeholder for partially applied arguments.
13129    *
13130    * **Note:** Unlike native `Function#bind` this method doesn't set the "length"
13131    * property of bound functions.
13132    *
13133    * @static
13134    * @memberOf _
13135    * @since 0.1.0
13136    * @category Function
13137    * @param {Function} func The function to bind.
13138    * @param {*} thisArg The `this` binding of `func`.
13139    * @param {...*} [partials] The arguments to be partially applied.
13140    * @returns {Function} Returns the new bound function.
13141    * @example
13142    *
13143    * var greet = function(greeting, punctuation) {
13144    *   return greeting + ' ' + this.user + punctuation;
13145    * };
13146    *
13147    * var object = { 'user': 'fred' };
13148    *
13149    * var bound = _.bind(greet, object, 'hi');
13150    * bound('!');
13151    * // => 'hi fred!'
13152    *
13153    * // Bound with placeholders.
13154    * var bound = _.bind(greet, object, _, '!');
13155    * bound('hi');
13156    * // => 'hi fred!'
13157    */
13158   var bind = rest(function(func, thisArg, partials) {
13159     var bitmask = BIND_FLAG;
13160     if (partials.length) {
13161       var holders = replaceHolders(partials, getHolder(bind));
13162       bitmask |= PARTIAL_FLAG;
13163     }
13164     return createWrapper(func, bitmask, thisArg, partials, holders);
13165   });
13166
13167   /**
13168    * Creates a debounced function that delays invoking `func` until after `wait`
13169    * milliseconds have elapsed since the last time the debounced function was
13170    * invoked. The debounced function comes with a `cancel` method to cancel
13171    * delayed `func` invocations and a `flush` method to immediately invoke them.
13172    * Provide an options object to indicate whether `func` should be invoked on
13173    * the leading and/or trailing edge of the `wait` timeout. The `func` is invoked
13174    * with the last arguments provided to the debounced function. Subsequent calls
13175    * to the debounced function return the result of the last `func` invocation.
13176    *
13177    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
13178    * on the trailing edge of the timeout only if the debounced function is
13179    * invoked more than once during the `wait` timeout.
13180    *
13181    * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
13182    * for details over the differences between `_.debounce` and `_.throttle`.
13183    *
13184    * @static
13185    * @memberOf _
13186    * @since 0.1.0
13187    * @category Function
13188    * @param {Function} func The function to debounce.
13189    * @param {number} [wait=0] The number of milliseconds to delay.
13190    * @param {Object} [options={}] The options object.
13191    * @param {boolean} [options.leading=false]
13192    *  Specify invoking on the leading edge of the timeout.
13193    * @param {number} [options.maxWait]
13194    *  The maximum time `func` is allowed to be delayed before it's invoked.
13195    * @param {boolean} [options.trailing=true]
13196    *  Specify invoking on the trailing edge of the timeout.
13197    * @returns {Function} Returns the new debounced function.
13198    * @example
13199    *
13200    * // Avoid costly calculations while the window size is in flux.
13201    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
13202    *
13203    * // Invoke `sendMail` when clicked, debouncing subsequent calls.
13204    * jQuery(element).on('click', _.debounce(sendMail, 300, {
13205    *   'leading': true,
13206    *   'trailing': false
13207    * }));
13208    *
13209    * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
13210    * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
13211    * var source = new EventSource('/stream');
13212    * jQuery(source).on('message', debounced);
13213    *
13214    * // Cancel the trailing debounced invocation.
13215    * jQuery(window).on('popstate', debounced.cancel);
13216    */
13217   function debounce(func, wait, options) {
13218     var lastArgs,
13219         lastThis,
13220         maxWait,
13221         result,
13222         timerId,
13223         lastCallTime = 0,
13224         lastInvokeTime = 0,
13225         leading = false,
13226         maxing = false,
13227         trailing = true;
13228
13229     if (typeof func != 'function') {
13230       throw new TypeError(FUNC_ERROR_TEXT);
13231     }
13232     wait = toNumber(wait) || 0;
13233     if (isObject(options)) {
13234       leading = !!options.leading;
13235       maxing = 'maxWait' in options;
13236       maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
13237       trailing = 'trailing' in options ? !!options.trailing : trailing;
13238     }
13239
13240     function invokeFunc(time) {
13241       var args = lastArgs,
13242           thisArg = lastThis;
13243
13244       lastArgs = lastThis = undefined;
13245       lastInvokeTime = time;
13246       result = func.apply(thisArg, args);
13247       return result;
13248     }
13249
13250     function leadingEdge(time) {
13251       // Reset any `maxWait` timer.
13252       lastInvokeTime = time;
13253       // Start the timer for the trailing edge.
13254       timerId = setTimeout(timerExpired, wait);
13255       // Invoke the leading edge.
13256       return leading ? invokeFunc(time) : result;
13257     }
13258
13259     function remainingWait(time) {
13260       var timeSinceLastCall = time - lastCallTime,
13261           timeSinceLastInvoke = time - lastInvokeTime,
13262           result = wait - timeSinceLastCall;
13263
13264       return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
13265     }
13266
13267     function shouldInvoke(time) {
13268       var timeSinceLastCall = time - lastCallTime,
13269           timeSinceLastInvoke = time - lastInvokeTime;
13270
13271       // Either this is the first call, activity has stopped and we're at the
13272       // trailing edge, the system time has gone backwards and we're treating
13273       // it as the trailing edge, or we've hit the `maxWait` limit.
13274       return (!lastCallTime || (timeSinceLastCall >= wait) ||
13275         (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
13276     }
13277
13278     function timerExpired() {
13279       var time = now();
13280       if (shouldInvoke(time)) {
13281         return trailingEdge(time);
13282       }
13283       // Restart the timer.
13284       timerId = setTimeout(timerExpired, remainingWait(time));
13285     }
13286
13287     function trailingEdge(time) {
13288       clearTimeout(timerId);
13289       timerId = undefined;
13290
13291       // Only invoke if we have `lastArgs` which means `func` has been
13292       // debounced at least once.
13293       if (trailing && lastArgs) {
13294         return invokeFunc(time);
13295       }
13296       lastArgs = lastThis = undefined;
13297       return result;
13298     }
13299
13300     function cancel() {
13301       if (timerId !== undefined) {
13302         clearTimeout(timerId);
13303       }
13304       lastCallTime = lastInvokeTime = 0;
13305       lastArgs = lastThis = timerId = undefined;
13306     }
13307
13308     function flush() {
13309       return timerId === undefined ? result : trailingEdge(now());
13310     }
13311
13312     function debounced() {
13313       var time = now(),
13314           isInvoking = shouldInvoke(time);
13315
13316       lastArgs = arguments;
13317       lastThis = this;
13318       lastCallTime = time;
13319
13320       if (isInvoking) {
13321         if (timerId === undefined) {
13322           return leadingEdge(lastCallTime);
13323         }
13324         if (maxing) {
13325           // Handle invocations in a tight loop.
13326           clearTimeout(timerId);
13327           timerId = setTimeout(timerExpired, wait);
13328           return invokeFunc(lastCallTime);
13329         }
13330       }
13331       if (timerId === undefined) {
13332         timerId = setTimeout(timerExpired, wait);
13333       }
13334       return result;
13335     }
13336     debounced.cancel = cancel;
13337     debounced.flush = flush;
13338     return debounced;
13339   }
13340
13341   /**
13342    * Creates a function that memoizes the result of `func`. If `resolver` is
13343    * provided, it determines the cache key for storing the result based on the
13344    * arguments provided to the memoized function. By default, the first argument
13345    * provided to the memoized function is used as the map cache key. The `func`
13346    * is invoked with the `this` binding of the memoized function.
13347    *
13348    * **Note:** The cache is exposed as the `cache` property on the memoized
13349    * function. Its creation may be customized by replacing the `_.memoize.Cache`
13350    * constructor with one whose instances implement the
13351    * [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)
13352    * method interface of `delete`, `get`, `has`, and `set`.
13353    *
13354    * @static
13355    * @memberOf _
13356    * @since 0.1.0
13357    * @category Function
13358    * @param {Function} func The function to have its output memoized.
13359    * @param {Function} [resolver] The function to resolve the cache key.
13360    * @returns {Function} Returns the new memoized function.
13361    * @example
13362    *
13363    * var object = { 'a': 1, 'b': 2 };
13364    * var other = { 'c': 3, 'd': 4 };
13365    *
13366    * var values = _.memoize(_.values);
13367    * values(object);
13368    * // => [1, 2]
13369    *
13370    * values(other);
13371    * // => [3, 4]
13372    *
13373    * object.a = 2;
13374    * values(object);
13375    * // => [1, 2]
13376    *
13377    * // Modify the result cache.
13378    * values.cache.set(object, ['a', 'b']);
13379    * values(object);
13380    * // => ['a', 'b']
13381    *
13382    * // Replace `_.memoize.Cache`.
13383    * _.memoize.Cache = WeakMap;
13384    */
13385   function memoize(func, resolver) {
13386     if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
13387       throw new TypeError(FUNC_ERROR_TEXT);
13388     }
13389     var memoized = function() {
13390       var args = arguments,
13391           key = resolver ? resolver.apply(this, args) : args[0],
13392           cache = memoized.cache;
13393
13394       if (cache.has(key)) {
13395         return cache.get(key);
13396       }
13397       var result = func.apply(this, args);
13398       memoized.cache = cache.set(key, result);
13399       return result;
13400     };
13401     memoized.cache = new (memoize.Cache || MapCache);
13402     return memoized;
13403   }
13404
13405   // Assign cache to `_.memoize`.
13406   memoize.Cache = MapCache;
13407
13408   /**
13409    * Creates a function that invokes `func` with the `this` binding of the
13410    * created function and arguments from `start` and beyond provided as
13411    * an array.
13412    *
13413    * **Note:** This method is based on the
13414    * [rest parameter](https://mdn.io/rest_parameters).
13415    *
13416    * @static
13417    * @memberOf _
13418    * @since 4.0.0
13419    * @category Function
13420    * @param {Function} func The function to apply a rest parameter to.
13421    * @param {number} [start=func.length-1] The start position of the rest parameter.
13422    * @returns {Function} Returns the new function.
13423    * @example
13424    *
13425    * var say = _.rest(function(what, names) {
13426    *   return what + ' ' + _.initial(names).join(', ') +
13427    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
13428    * });
13429    *
13430    * say('hello', 'fred', 'barney', 'pebbles');
13431    * // => 'hello fred, barney, & pebbles'
13432    */
13433   function rest(func, start) {
13434     if (typeof func != 'function') {
13435       throw new TypeError(FUNC_ERROR_TEXT);
13436     }
13437     start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0);
13438     return function() {
13439       var args = arguments,
13440           index = -1,
13441           length = nativeMax(args.length - start, 0),
13442           array = Array(length);
13443
13444       while (++index < length) {
13445         array[index] = args[start + index];
13446       }
13447       switch (start) {
13448         case 0: return func.call(this, array);
13449         case 1: return func.call(this, args[0], array);
13450         case 2: return func.call(this, args[0], args[1], array);
13451       }
13452       var otherArgs = Array(start + 1);
13453       index = -1;
13454       while (++index < start) {
13455         otherArgs[index] = args[index];
13456       }
13457       otherArgs[start] = array;
13458       return apply(func, this, otherArgs);
13459     };
13460   }
13461
13462   /**
13463    * Creates a throttled function that only invokes `func` at most once per
13464    * every `wait` milliseconds. The throttled function comes with a `cancel`
13465    * method to cancel delayed `func` invocations and a `flush` method to
13466    * immediately invoke them. Provide an options object to indicate whether
13467    * `func` should be invoked on the leading and/or trailing edge of the `wait`
13468    * timeout. The `func` is invoked with the last arguments provided to the
13469    * throttled function. Subsequent calls to the throttled function return the
13470    * result of the last `func` invocation.
13471    *
13472    * **Note:** If `leading` and `trailing` options are `true`, `func` is
13473    * invoked on the trailing edge of the timeout only if the throttled function
13474    * is invoked more than once during the `wait` timeout.
13475    *
13476    * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
13477    * for details over the differences between `_.throttle` and `_.debounce`.
13478    *
13479    * @static
13480    * @memberOf _
13481    * @since 0.1.0
13482    * @category Function
13483    * @param {Function} func The function to throttle.
13484    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
13485    * @param {Object} [options={}] The options object.
13486    * @param {boolean} [options.leading=true]
13487    *  Specify invoking on the leading edge of the timeout.
13488    * @param {boolean} [options.trailing=true]
13489    *  Specify invoking on the trailing edge of the timeout.
13490    * @returns {Function} Returns the new throttled function.
13491    * @example
13492    *
13493    * // Avoid excessively updating the position while scrolling.
13494    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
13495    *
13496    * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
13497    * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
13498    * jQuery(element).on('click', throttled);
13499    *
13500    * // Cancel the trailing throttled invocation.
13501    * jQuery(window).on('popstate', throttled.cancel);
13502    */
13503   function throttle(func, wait, options) {
13504     var leading = true,
13505         trailing = true;
13506
13507     if (typeof func != 'function') {
13508       throw new TypeError(FUNC_ERROR_TEXT);
13509     }
13510     if (isObject(options)) {
13511       leading = 'leading' in options ? !!options.leading : leading;
13512       trailing = 'trailing' in options ? !!options.trailing : trailing;
13513     }
13514     return debounce(func, wait, {
13515       'leading': leading,
13516       'maxWait': wait,
13517       'trailing': trailing
13518     });
13519   }
13520
13521   /*------------------------------------------------------------------------*/
13522
13523   /**
13524    * Creates a shallow clone of `value`.
13525    *
13526    * **Note:** This method is loosely based on the
13527    * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
13528    * and supports cloning arrays, array buffers, booleans, date objects, maps,
13529    * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
13530    * arrays. The own enumerable properties of `arguments` objects are cloned
13531    * as plain objects. An empty object is returned for uncloneable values such
13532    * as error objects, functions, DOM nodes, and WeakMaps.
13533    *
13534    * @static
13535    * @memberOf _
13536    * @since 0.1.0
13537    * @category Lang
13538    * @param {*} value The value to clone.
13539    * @returns {*} Returns the cloned value.
13540    * @see _.cloneDeep
13541    * @example
13542    *
13543    * var objects = [{ 'a': 1 }, { 'b': 2 }];
13544    *
13545    * var shallow = _.clone(objects);
13546    * console.log(shallow[0] === objects[0]);
13547    * // => true
13548    */
13549   function clone(value) {
13550     return baseClone(value, false, true);
13551   }
13552
13553   /**
13554    * This method is like `_.clone` except that it recursively clones `value`.
13555    *
13556    * @static
13557    * @memberOf _
13558    * @since 1.0.0
13559    * @category Lang
13560    * @param {*} value The value to recursively clone.
13561    * @returns {*} Returns the deep cloned value.
13562    * @see _.clone
13563    * @example
13564    *
13565    * var objects = [{ 'a': 1 }, { 'b': 2 }];
13566    *
13567    * var deep = _.cloneDeep(objects);
13568    * console.log(deep[0] === objects[0]);
13569    * // => false
13570    */
13571   function cloneDeep(value) {
13572     return baseClone(value, true, true);
13573   }
13574
13575   /**
13576    * Performs a
13577    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
13578    * comparison between two values to determine if they are equivalent.
13579    *
13580    * @static
13581    * @memberOf _
13582    * @since 4.0.0
13583    * @category Lang
13584    * @param {*} value The value to compare.
13585    * @param {*} other The other value to compare.
13586    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
13587    * @example
13588    *
13589    * var object = { 'user': 'fred' };
13590    * var other = { 'user': 'fred' };
13591    *
13592    * _.eq(object, object);
13593    * // => true
13594    *
13595    * _.eq(object, other);
13596    * // => false
13597    *
13598    * _.eq('a', 'a');
13599    * // => true
13600    *
13601    * _.eq('a', Object('a'));
13602    * // => false
13603    *
13604    * _.eq(NaN, NaN);
13605    * // => true
13606    */
13607   function eq(value, other) {
13608     return value === other || (value !== value && other !== other);
13609   }
13610
13611   /**
13612    * Checks if `value` is likely an `arguments` object.
13613    *
13614    * @static
13615    * @memberOf _
13616    * @since 0.1.0
13617    * @category Lang
13618    * @param {*} value The value to check.
13619    * @returns {boolean} Returns `true` if `value` is correctly classified,
13620    *  else `false`.
13621    * @example
13622    *
13623    * _.isArguments(function() { return arguments; }());
13624    * // => true
13625    *
13626    * _.isArguments([1, 2, 3]);
13627    * // => false
13628    */
13629   function isArguments(value) {
13630     // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.
13631     return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
13632       (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
13633   }
13634
13635   /**
13636    * Checks if `value` is classified as an `Array` object.
13637    *
13638    * @static
13639    * @memberOf _
13640    * @since 0.1.0
13641    * @type {Function}
13642    * @category Lang
13643    * @param {*} value The value to check.
13644    * @returns {boolean} Returns `true` if `value` is correctly classified,
13645    *  else `false`.
13646    * @example
13647    *
13648    * _.isArray([1, 2, 3]);
13649    * // => true
13650    *
13651    * _.isArray(document.body.children);
13652    * // => false
13653    *
13654    * _.isArray('abc');
13655    * // => false
13656    *
13657    * _.isArray(_.noop);
13658    * // => false
13659    */
13660   var isArray = Array.isArray;
13661
13662   /**
13663    * Checks if `value` is array-like. A value is considered array-like if it's
13664    * not a function and has a `value.length` that's an integer greater than or
13665    * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
13666    *
13667    * @static
13668    * @memberOf _
13669    * @since 4.0.0
13670    * @category Lang
13671    * @param {*} value The value to check.
13672    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
13673    * @example
13674    *
13675    * _.isArrayLike([1, 2, 3]);
13676    * // => true
13677    *
13678    * _.isArrayLike(document.body.children);
13679    * // => true
13680    *
13681    * _.isArrayLike('abc');
13682    * // => true
13683    *
13684    * _.isArrayLike(_.noop);
13685    * // => false
13686    */
13687   function isArrayLike(value) {
13688     return value != null && isLength(getLength(value)) && !isFunction(value);
13689   }
13690
13691   /**
13692    * This method is like `_.isArrayLike` except that it also checks if `value`
13693    * is an object.
13694    *
13695    * @static
13696    * @memberOf _
13697    * @since 4.0.0
13698    * @category Lang
13699    * @param {*} value The value to check.
13700    * @returns {boolean} Returns `true` if `value` is an array-like object,
13701    *  else `false`.
13702    * @example
13703    *
13704    * _.isArrayLikeObject([1, 2, 3]);
13705    * // => true
13706    *
13707    * _.isArrayLikeObject(document.body.children);
13708    * // => true
13709    *
13710    * _.isArrayLikeObject('abc');
13711    * // => false
13712    *
13713    * _.isArrayLikeObject(_.noop);
13714    * // => false
13715    */
13716   function isArrayLikeObject(value) {
13717     return isObjectLike(value) && isArrayLike(value);
13718   }
13719
13720   /**
13721    * Checks if `value` is a buffer.
13722    *
13723    * @static
13724    * @memberOf _
13725    * @since 4.3.0
13726    * @category Lang
13727    * @param {*} value The value to check.
13728    * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
13729    * @example
13730    *
13731    * _.isBuffer(new Buffer(2));
13732    * // => true
13733    *
13734    * _.isBuffer(new Uint8Array(2));
13735    * // => false
13736    */
13737   var isBuffer = !Buffer ? constant(false) : function(value) {
13738     return value instanceof Buffer;
13739   };
13740
13741   /**
13742    * Checks if `value` is an empty object, collection, map, or set.
13743    *
13744    * Objects are considered empty if they have no own enumerable string keyed
13745    * properties.
13746    *
13747    * Array-like values such as `arguments` objects, arrays, buffers, strings, or
13748    * jQuery-like collections are considered empty if they have a `length` of `0`.
13749    * Similarly, maps and sets are considered empty if they have a `size` of `0`.
13750    *
13751    * @static
13752    * @memberOf _
13753    * @since 0.1.0
13754    * @category Lang
13755    * @param {*} value The value to check.
13756    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
13757    * @example
13758    *
13759    * _.isEmpty(null);
13760    * // => true
13761    *
13762    * _.isEmpty(true);
13763    * // => true
13764    *
13765    * _.isEmpty(1);
13766    * // => true
13767    *
13768    * _.isEmpty([1, 2, 3]);
13769    * // => false
13770    *
13771    * _.isEmpty({ 'a': 1 });
13772    * // => false
13773    */
13774   function isEmpty(value) {
13775     if (isArrayLike(value) &&
13776         (isArray(value) || isString(value) || isFunction(value.splice) ||
13777           isArguments(value) || isBuffer(value))) {
13778       return !value.length;
13779     }
13780     if (isObjectLike(value)) {
13781       var tag = getTag(value);
13782       if (tag == mapTag || tag == setTag) {
13783         return !value.size;
13784       }
13785     }
13786     for (var key in value) {
13787       if (hasOwnProperty.call(value, key)) {
13788         return false;
13789       }
13790     }
13791     return !(nonEnumShadows && keys(value).length);
13792   }
13793
13794   /**
13795    * Performs a deep comparison between two values to determine if they are
13796    * equivalent.
13797    *
13798    * **Note:** This method supports comparing arrays, array buffers, booleans,
13799    * date objects, error objects, maps, numbers, `Object` objects, regexes,
13800    * sets, strings, symbols, and typed arrays. `Object` objects are compared
13801    * by their own, not inherited, enumerable properties. Functions and DOM
13802    * nodes are **not** supported.
13803    *
13804    * @static
13805    * @memberOf _
13806    * @since 0.1.0
13807    * @category Lang
13808    * @param {*} value The value to compare.
13809    * @param {*} other The other value to compare.
13810    * @returns {boolean} Returns `true` if the values are equivalent,
13811    *  else `false`.
13812    * @example
13813    *
13814    * var object = { 'user': 'fred' };
13815    * var other = { 'user': 'fred' };
13816    *
13817    * _.isEqual(object, other);
13818    * // => true
13819    *
13820    * object === other;
13821    * // => false
13822    */
13823   function isEqual(value, other) {
13824     return baseIsEqual(value, other);
13825   }
13826
13827   /**
13828    * Checks if `value` is classified as a `Function` object.
13829    *
13830    * @static
13831    * @memberOf _
13832    * @since 0.1.0
13833    * @category Lang
13834    * @param {*} value The value to check.
13835    * @returns {boolean} Returns `true` if `value` is correctly classified,
13836    *  else `false`.
13837    * @example
13838    *
13839    * _.isFunction(_);
13840    * // => true
13841    *
13842    * _.isFunction(/abc/);
13843    * // => false
13844    */
13845   function isFunction(value) {
13846     // The use of `Object#toString` avoids issues with the `typeof` operator
13847     // in Safari 8 which returns 'object' for typed array and weak map constructors,
13848     // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
13849     var tag = isObject(value) ? objectToString.call(value) : '';
13850     return tag == funcTag || tag == genTag;
13851   }
13852
13853   /**
13854    * Checks if `value` is a valid array-like length.
13855    *
13856    * **Note:** This function is loosely based on
13857    * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
13858    *
13859    * @static
13860    * @memberOf _
13861    * @since 4.0.0
13862    * @category Lang
13863    * @param {*} value The value to check.
13864    * @returns {boolean} Returns `true` if `value` is a valid length,
13865    *  else `false`.
13866    * @example
13867    *
13868    * _.isLength(3);
13869    * // => true
13870    *
13871    * _.isLength(Number.MIN_VALUE);
13872    * // => false
13873    *
13874    * _.isLength(Infinity);
13875    * // => false
13876    *
13877    * _.isLength('3');
13878    * // => false
13879    */
13880   function isLength(value) {
13881     return typeof value == 'number' &&
13882       value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
13883   }
13884
13885   /**
13886    * Checks if `value` is the
13887    * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)
13888    * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
13889    *
13890    * @static
13891    * @memberOf _
13892    * @since 0.1.0
13893    * @category Lang
13894    * @param {*} value The value to check.
13895    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
13896    * @example
13897    *
13898    * _.isObject({});
13899    * // => true
13900    *
13901    * _.isObject([1, 2, 3]);
13902    * // => true
13903    *
13904    * _.isObject(_.noop);
13905    * // => true
13906    *
13907    * _.isObject(null);
13908    * // => false
13909    */
13910   function isObject(value) {
13911     var type = typeof value;
13912     return !!value && (type == 'object' || type == 'function');
13913   }
13914
13915   /**
13916    * Checks if `value` is object-like. A value is object-like if it's not `null`
13917    * and has a `typeof` result of "object".
13918    *
13919    * @static
13920    * @memberOf _
13921    * @since 4.0.0
13922    * @category Lang
13923    * @param {*} value The value to check.
13924    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
13925    * @example
13926    *
13927    * _.isObjectLike({});
13928    * // => true
13929    *
13930    * _.isObjectLike([1, 2, 3]);
13931    * // => true
13932    *
13933    * _.isObjectLike(_.noop);
13934    * // => false
13935    *
13936    * _.isObjectLike(null);
13937    * // => false
13938    */
13939   function isObjectLike(value) {
13940     return !!value && typeof value == 'object';
13941   }
13942
13943   /**
13944    * Checks if `value` is a native function.
13945    *
13946    * @static
13947    * @memberOf _
13948    * @since 3.0.0
13949    * @category Lang
13950    * @param {*} value The value to check.
13951    * @returns {boolean} Returns `true` if `value` is a native function,
13952    *  else `false`.
13953    * @example
13954    *
13955    * _.isNative(Array.prototype.push);
13956    * // => true
13957    *
13958    * _.isNative(_);
13959    * // => false
13960    */
13961   function isNative(value) {
13962     if (!isObject(value)) {
13963       return false;
13964     }
13965     var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
13966     return pattern.test(toSource(value));
13967   }
13968
13969   /**
13970    * Checks if `value` is a plain object, that is, an object created by the
13971    * `Object` constructor or one with a `[[Prototype]]` of `null`.
13972    *
13973    * @static
13974    * @memberOf _
13975    * @since 0.8.0
13976    * @category Lang
13977    * @param {*} value The value to check.
13978    * @returns {boolean} Returns `true` if `value` is a plain object,
13979    *  else `false`.
13980    * @example
13981    *
13982    * function Foo() {
13983    *   this.a = 1;
13984    * }
13985    *
13986    * _.isPlainObject(new Foo);
13987    * // => false
13988    *
13989    * _.isPlainObject([1, 2, 3]);
13990    * // => false
13991    *
13992    * _.isPlainObject({ 'x': 0, 'y': 0 });
13993    * // => true
13994    *
13995    * _.isPlainObject(Object.create(null));
13996    * // => true
13997    */
13998   function isPlainObject(value) {
13999     if (!isObjectLike(value) ||
14000         objectToString.call(value) != objectTag || isHostObject(value)) {
14001       return false;
14002     }
14003     var proto = getPrototype(value);
14004     if (proto === null) {
14005       return true;
14006     }
14007     var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
14008     return (typeof Ctor == 'function' &&
14009       Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
14010   }
14011
14012   /**
14013    * Checks if `value` is classified as a `String` primitive or object.
14014    *
14015    * @static
14016    * @since 0.1.0
14017    * @memberOf _
14018    * @category Lang
14019    * @param {*} value The value to check.
14020    * @returns {boolean} Returns `true` if `value` is correctly classified,
14021    *  else `false`.
14022    * @example
14023    *
14024    * _.isString('abc');
14025    * // => true
14026    *
14027    * _.isString(1);
14028    * // => false
14029    */
14030   function isString(value) {
14031     return typeof value == 'string' ||
14032       (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);
14033   }
14034
14035   /**
14036    * Checks if `value` is classified as a `Symbol` primitive or object.
14037    *
14038    * @static
14039    * @memberOf _
14040    * @since 4.0.0
14041    * @category Lang
14042    * @param {*} value The value to check.
14043    * @returns {boolean} Returns `true` if `value` is correctly classified,
14044    *  else `false`.
14045    * @example
14046    *
14047    * _.isSymbol(Symbol.iterator);
14048    * // => true
14049    *
14050    * _.isSymbol('abc');
14051    * // => false
14052    */
14053   function isSymbol(value) {
14054     return typeof value == 'symbol' ||
14055       (isObjectLike(value) && objectToString.call(value) == symbolTag);
14056   }
14057
14058   /**
14059    * Checks if `value` is classified as a typed array.
14060    *
14061    * @static
14062    * @memberOf _
14063    * @since 3.0.0
14064    * @category Lang
14065    * @param {*} value The value to check.
14066    * @returns {boolean} Returns `true` if `value` is correctly classified,
14067    *  else `false`.
14068    * @example
14069    *
14070    * _.isTypedArray(new Uint8Array);
14071    * // => true
14072    *
14073    * _.isTypedArray([]);
14074    * // => false
14075    */
14076   function isTypedArray(value) {
14077     return isObjectLike(value) &&
14078       isLength(value.length) && !!typedArrayTags[objectToString.call(value)];
14079   }
14080
14081   /**
14082    * Converts `value` to an array.
14083    *
14084    * @static
14085    * @since 0.1.0
14086    * @memberOf _
14087    * @category Lang
14088    * @param {*} value The value to convert.
14089    * @returns {Array} Returns the converted array.
14090    * @example
14091    *
14092    * _.toArray({ 'a': 1, 'b': 2 });
14093    * // => [1, 2]
14094    *
14095    * _.toArray('abc');
14096    * // => ['a', 'b', 'c']
14097    *
14098    * _.toArray(1);
14099    * // => []
14100    *
14101    * _.toArray(null);
14102    * // => []
14103    */
14104   function toArray(value) {
14105     if (!value) {
14106       return [];
14107     }
14108     if (isArrayLike(value)) {
14109       return isString(value) ? stringToArray(value) : copyArray(value);
14110     }
14111     if (iteratorSymbol && value[iteratorSymbol]) {
14112       return iteratorToArray(value[iteratorSymbol]());
14113     }
14114     var tag = getTag(value),
14115         func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
14116
14117     return func(value);
14118   }
14119
14120   /**
14121    * Converts `value` to a finite number.
14122    *
14123    * @static
14124    * @memberOf _
14125    * @since 4.12.0
14126    * @category Lang
14127    * @param {*} value The value to convert.
14128    * @returns {number} Returns the converted number.
14129    * @example
14130    *
14131    * _.toFinite(3.2);
14132    * // => 3.2
14133    *
14134    * _.toFinite(Number.MIN_VALUE);
14135    * // => 5e-324
14136    *
14137    * _.toFinite(Infinity);
14138    * // => 1.7976931348623157e+308
14139    *
14140    * _.toFinite('3.2');
14141    * // => 3.2
14142    */
14143   function toFinite(value) {
14144     if (!value) {
14145       return value === 0 ? value : 0;
14146     }
14147     value = toNumber(value);
14148     if (value === INFINITY || value === -INFINITY) {
14149       var sign = (value < 0 ? -1 : 1);
14150       return sign * MAX_INTEGER;
14151     }
14152     return value === value ? value : 0;
14153   }
14154
14155   /**
14156    * Converts `value` to an integer.
14157    *
14158    * **Note:** This function is loosely based on
14159    * [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
14160    *
14161    * @static
14162    * @memberOf _
14163    * @since 4.0.0
14164    * @category Lang
14165    * @param {*} value The value to convert.
14166    * @returns {number} Returns the converted integer.
14167    * @example
14168    *
14169    * _.toInteger(3.2);
14170    * // => 3
14171    *
14172    * _.toInteger(Number.MIN_VALUE);
14173    * // => 0
14174    *
14175    * _.toInteger(Infinity);
14176    * // => 1.7976931348623157e+308
14177    *
14178    * _.toInteger('3.2');
14179    * // => 3
14180    */
14181   function toInteger(value) {
14182     var result = toFinite(value),
14183         remainder = result % 1;
14184
14185     return result === result ? (remainder ? result - remainder : result) : 0;
14186   }
14187
14188   /**
14189    * Converts `value` to a number.
14190    *
14191    * @static
14192    * @memberOf _
14193    * @since 4.0.0
14194    * @category Lang
14195    * @param {*} value The value to process.
14196    * @returns {number} Returns the number.
14197    * @example
14198    *
14199    * _.toNumber(3.2);
14200    * // => 3.2
14201    *
14202    * _.toNumber(Number.MIN_VALUE);
14203    * // => 5e-324
14204    *
14205    * _.toNumber(Infinity);
14206    * // => Infinity
14207    *
14208    * _.toNumber('3.2');
14209    * // => 3.2
14210    */
14211   function toNumber(value) {
14212     if (typeof value == 'number') {
14213       return value;
14214     }
14215     if (isSymbol(value)) {
14216       return NAN;
14217     }
14218     if (isObject(value)) {
14219       var other = isFunction(value.valueOf) ? value.valueOf() : value;
14220       value = isObject(other) ? (other + '') : other;
14221     }
14222     if (typeof value != 'string') {
14223       return value === 0 ? value : +value;
14224     }
14225     value = value.replace(reTrim, '');
14226     var isBinary = reIsBinary.test(value);
14227     return (isBinary || reIsOctal.test(value))
14228       ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
14229       : (reIsBadHex.test(value) ? NAN : +value);
14230   }
14231
14232   /**
14233    * Converts `value` to a plain object flattening inherited enumerable string
14234    * keyed properties of `value` to own properties of the plain object.
14235    *
14236    * @static
14237    * @memberOf _
14238    * @since 3.0.0
14239    * @category Lang
14240    * @param {*} value The value to convert.
14241    * @returns {Object} Returns the converted plain object.
14242    * @example
14243    *
14244    * function Foo() {
14245    *   this.b = 2;
14246    * }
14247    *
14248    * Foo.prototype.c = 3;
14249    *
14250    * _.assign({ 'a': 1 }, new Foo);
14251    * // => { 'a': 1, 'b': 2 }
14252    *
14253    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
14254    * // => { 'a': 1, 'b': 2, 'c': 3 }
14255    */
14256   function toPlainObject(value) {
14257     return copyObject(value, keysIn(value));
14258   }
14259
14260   /**
14261    * Converts `value` to a string. An empty string is returned for `null`
14262    * and `undefined` values. The sign of `-0` is preserved.
14263    *
14264    * @static
14265    * @memberOf _
14266    * @since 4.0.0
14267    * @category Lang
14268    * @param {*} value The value to process.
14269    * @returns {string} Returns the string.
14270    * @example
14271    *
14272    * _.toString(null);
14273    * // => ''
14274    *
14275    * _.toString(-0);
14276    * // => '-0'
14277    *
14278    * _.toString([1, 2, 3]);
14279    * // => '1,2,3'
14280    */
14281   function toString(value) {
14282     return value == null ? '' : baseToString(value);
14283   }
14284
14285   /*------------------------------------------------------------------------*/
14286
14287   /**
14288    * Assigns own enumerable string keyed properties of source objects to the
14289    * destination object. Source objects are applied from left to right.
14290    * Subsequent sources overwrite property assignments of previous sources.
14291    *
14292    * **Note:** This method mutates `object` and is loosely based on
14293    * [`Object.assign`](https://mdn.io/Object/assign).
14294    *
14295    * @static
14296    * @memberOf _
14297    * @since 0.10.0
14298    * @category Object
14299    * @param {Object} object The destination object.
14300    * @param {...Object} [sources] The source objects.
14301    * @returns {Object} Returns `object`.
14302    * @see _.assignIn
14303    * @example
14304    *
14305    * function Foo() {
14306    *   this.c = 3;
14307    * }
14308    *
14309    * function Bar() {
14310    *   this.e = 5;
14311    * }
14312    *
14313    * Foo.prototype.d = 4;
14314    * Bar.prototype.f = 6;
14315    *
14316    * _.assign({ 'a': 1 }, new Foo, new Bar);
14317    * // => { 'a': 1, 'c': 3, 'e': 5 }
14318    */
14319   var assign = createAssigner(function(object, source) {
14320     if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
14321       copyObject(source, keys(source), object);
14322       return;
14323     }
14324     for (var key in source) {
14325       if (hasOwnProperty.call(source, key)) {
14326         assignValue(object, key, source[key]);
14327       }
14328     }
14329   });
14330
14331   /**
14332    * This method is like `_.assign` except that it iterates over own and
14333    * inherited source properties.
14334    *
14335    * **Note:** This method mutates `object`.
14336    *
14337    * @static
14338    * @memberOf _
14339    * @since 4.0.0
14340    * @alias extend
14341    * @category Object
14342    * @param {Object} object The destination object.
14343    * @param {...Object} [sources] The source objects.
14344    * @returns {Object} Returns `object`.
14345    * @see _.assign
14346    * @example
14347    *
14348    * function Foo() {
14349    *   this.b = 2;
14350    * }
14351    *
14352    * function Bar() {
14353    *   this.d = 4;
14354    * }
14355    *
14356    * Foo.prototype.c = 3;
14357    * Bar.prototype.e = 5;
14358    *
14359    * _.assignIn({ 'a': 1 }, new Foo, new Bar);
14360    * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }
14361    */
14362   var assignIn = createAssigner(function(object, source) {
14363     if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
14364       copyObject(source, keysIn(source), object);
14365       return;
14366     }
14367     for (var key in source) {
14368       assignValue(object, key, source[key]);
14369     }
14370   });
14371
14372   /**
14373    * Iterates over own enumerable string keyed properties of an object and
14374    * invokes `iteratee` for each property. The iteratee is invoked with three
14375    * arguments: (value, key, object). Iteratee functions may exit iteration
14376    * early by explicitly returning `false`.
14377    *
14378    * @static
14379    * @memberOf _
14380    * @since 0.3.0
14381    * @category Object
14382    * @param {Object} object The object to iterate over.
14383    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
14384    * @returns {Object} Returns `object`.
14385    * @see _.forOwnRight
14386    * @example
14387    *
14388    * function Foo() {
14389    *   this.a = 1;
14390    *   this.b = 2;
14391    * }
14392    *
14393    * Foo.prototype.c = 3;
14394    *
14395    * _.forOwn(new Foo, function(value, key) {
14396    *   console.log(key);
14397    * });
14398    * // => Logs 'a' then 'b' (iteration order is not guaranteed).
14399    */
14400   function forOwn(object, iteratee) {
14401     return object && baseForOwn(object, getIteratee(iteratee, 3));
14402   }
14403
14404   /**
14405    * Gets the value at `path` of `object`. If the resolved value is
14406    * `undefined`, the `defaultValue` is used in its place.
14407    *
14408    * @static
14409    * @memberOf _
14410    * @since 3.7.0
14411    * @category Object
14412    * @param {Object} object The object to query.
14413    * @param {Array|string} path The path of the property to get.
14414    * @param {*} [defaultValue] The value returned for `undefined` resolved values.
14415    * @returns {*} Returns the resolved value.
14416    * @example
14417    *
14418    * var object = { 'a': [{ 'b': { 'c': 3 } }] };
14419    *
14420    * _.get(object, 'a[0].b.c');
14421    * // => 3
14422    *
14423    * _.get(object, ['a', '0', 'b', 'c']);
14424    * // => 3
14425    *
14426    * _.get(object, 'a.b.c', 'default');
14427    * // => 'default'
14428    */
14429   function get(object, path, defaultValue) {
14430     var result = object == null ? undefined : baseGet(object, path);
14431     return result === undefined ? defaultValue : result;
14432   }
14433
14434   /**
14435    * Checks if `path` is a direct or inherited property of `object`.
14436    *
14437    * @static
14438    * @memberOf _
14439    * @since 4.0.0
14440    * @category Object
14441    * @param {Object} object The object to query.
14442    * @param {Array|string} path The path to check.
14443    * @returns {boolean} Returns `true` if `path` exists, else `false`.
14444    * @example
14445    *
14446    * var object = _.create({ 'a': _.create({ 'b': 2 }) });
14447    *
14448    * _.hasIn(object, 'a');
14449    * // => true
14450    *
14451    * _.hasIn(object, 'a.b');
14452    * // => true
14453    *
14454    * _.hasIn(object, ['a', 'b']);
14455    * // => true
14456    *
14457    * _.hasIn(object, 'b');
14458    * // => false
14459    */
14460   function hasIn(object, path) {
14461     return object != null && hasPath(object, path, baseHasIn);
14462   }
14463
14464   /**
14465    * Creates an array of the own enumerable property names of `object`.
14466    *
14467    * **Note:** Non-object values are coerced to objects. See the
14468    * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
14469    * for more details.
14470    *
14471    * @static
14472    * @since 0.1.0
14473    * @memberOf _
14474    * @category Object
14475    * @param {Object} object The object to query.
14476    * @returns {Array} Returns the array of property names.
14477    * @example
14478    *
14479    * function Foo() {
14480    *   this.a = 1;
14481    *   this.b = 2;
14482    * }
14483    *
14484    * Foo.prototype.c = 3;
14485    *
14486    * _.keys(new Foo);
14487    * // => ['a', 'b'] (iteration order is not guaranteed)
14488    *
14489    * _.keys('hi');
14490    * // => ['0', '1']
14491    */
14492   function keys(object) {
14493     var isProto = isPrototype(object);
14494     if (!(isProto || isArrayLike(object))) {
14495       return baseKeys(object);
14496     }
14497     var indexes = indexKeys(object),
14498         skipIndexes = !!indexes,
14499         result = indexes || [],
14500         length = result.length;
14501
14502     for (var key in object) {
14503       if (baseHas(object, key) &&
14504           !(skipIndexes && (key == 'length' || isIndex(key, length))) &&
14505           !(isProto && key == 'constructor')) {
14506         result.push(key);
14507       }
14508     }
14509     return result;
14510   }
14511
14512   /**
14513    * Creates an array of the own and inherited enumerable property names of `object`.
14514    *
14515    * **Note:** Non-object values are coerced to objects.
14516    *
14517    * @static
14518    * @memberOf _
14519    * @since 3.0.0
14520    * @category Object
14521    * @param {Object} object The object to query.
14522    * @returns {Array} Returns the array of property names.
14523    * @example
14524    *
14525    * function Foo() {
14526    *   this.a = 1;
14527    *   this.b = 2;
14528    * }
14529    *
14530    * Foo.prototype.c = 3;
14531    *
14532    * _.keysIn(new Foo);
14533    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
14534    */
14535   function keysIn(object) {
14536     var index = -1,
14537         isProto = isPrototype(object),
14538         props = baseKeysIn(object),
14539         propsLength = props.length,
14540         indexes = indexKeys(object),
14541         skipIndexes = !!indexes,
14542         result = indexes || [],
14543         length = result.length;
14544
14545     while (++index < propsLength) {
14546       var key = props[index];
14547       if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
14548           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
14549         result.push(key);
14550       }
14551     }
14552     return result;
14553   }
14554
14555   /**
14556    * This method is like `_.assign` except that it recursively merges own and
14557    * inherited enumerable string keyed properties of source objects into the
14558    * destination object. Source properties that resolve to `undefined` are
14559    * skipped if a destination value exists. Array and plain object properties
14560    * are merged recursively. Other objects and value types are overridden by
14561    * assignment. Source objects are applied from left to right. Subsequent
14562    * sources overwrite property assignments of previous sources.
14563    *
14564    * **Note:** This method mutates `object`.
14565    *
14566    * @static
14567    * @memberOf _
14568    * @since 0.5.0
14569    * @category Object
14570    * @param {Object} object The destination object.
14571    * @param {...Object} [sources] The source objects.
14572    * @returns {Object} Returns `object`.
14573    * @example
14574    *
14575    * var users = {
14576    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
14577    * };
14578    *
14579    * var ages = {
14580    *   'data': [{ 'age': 36 }, { 'age': 40 }]
14581    * };
14582    *
14583    * _.merge(users, ages);
14584    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
14585    */
14586   var merge = createAssigner(function(object, source, srcIndex) {
14587     baseMerge(object, source, srcIndex);
14588   });
14589
14590   /**
14591    * The opposite of `_.pick`; this method creates an object composed of the
14592    * own and inherited enumerable string keyed properties of `object` that are
14593    * not omitted.
14594    *
14595    * @static
14596    * @since 0.1.0
14597    * @memberOf _
14598    * @category Object
14599    * @param {Object} object The source object.
14600    * @param {...(string|string[])} [props] The property identifiers to omit.
14601    * @returns {Object} Returns the new object.
14602    * @example
14603    *
14604    * var object = { 'a': 1, 'b': '2', 'c': 3 };
14605    *
14606    * _.omit(object, ['a', 'c']);
14607    * // => { 'b': '2' }
14608    */
14609   var omit = rest(function(object, props) {
14610     if (object == null) {
14611       return {};
14612     }
14613     props = arrayMap(baseFlatten(props, 1), toKey);
14614     return basePick(object, baseDifference(getAllKeysIn(object), props));
14615   });
14616
14617   /**
14618    * Creates an object composed of the picked `object` properties.
14619    *
14620    * @static
14621    * @since 0.1.0
14622    * @memberOf _
14623    * @category Object
14624    * @param {Object} object The source object.
14625    * @param {...(string|string[])} [props] The property identifiers to pick.
14626    * @returns {Object} Returns the new object.
14627    * @example
14628    *
14629    * var object = { 'a': 1, 'b': '2', 'c': 3 };
14630    *
14631    * _.pick(object, ['a', 'c']);
14632    * // => { 'a': 1, 'c': 3 }
14633    */
14634   var pick = rest(function(object, props) {
14635     return object == null ? {} : basePick(object, arrayMap(baseFlatten(props, 1), toKey));
14636   });
14637
14638   /**
14639    * Creates an array of own enumerable string keyed-value pairs for `object`
14640    * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
14641    * entries are returned.
14642    *
14643    * @static
14644    * @memberOf _
14645    * @since 4.0.0
14646    * @alias entries
14647    * @category Object
14648    * @param {Object} object The object to query.
14649    * @returns {Array} Returns the key-value pairs.
14650    * @example
14651    *
14652    * function Foo() {
14653    *   this.a = 1;
14654    *   this.b = 2;
14655    * }
14656    *
14657    * Foo.prototype.c = 3;
14658    *
14659    * _.toPairs(new Foo);
14660    * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
14661    */
14662   var toPairs = createToPairs(keys);
14663
14664   /**
14665    * Creates an array of the own enumerable string keyed property values of `object`.
14666    *
14667    * **Note:** Non-object values are coerced to objects.
14668    *
14669    * @static
14670    * @since 0.1.0
14671    * @memberOf _
14672    * @category Object
14673    * @param {Object} object The object to query.
14674    * @returns {Array} Returns the array of property values.
14675    * @example
14676    *
14677    * function Foo() {
14678    *   this.a = 1;
14679    *   this.b = 2;
14680    * }
14681    *
14682    * Foo.prototype.c = 3;
14683    *
14684    * _.values(new Foo);
14685    * // => [1, 2] (iteration order is not guaranteed)
14686    *
14687    * _.values('hi');
14688    * // => ['h', 'i']
14689    */
14690   function values(object) {
14691     return object ? baseValues(object, keys(object)) : [];
14692   }
14693
14694   /*------------------------------------------------------------------------*/
14695
14696   /**
14697    * Creates a function that returns `value`.
14698    *
14699    * @static
14700    * @memberOf _
14701    * @since 2.4.0
14702    * @category Util
14703    * @param {*} value The value to return from the new function.
14704    * @returns {Function} Returns the new constant function.
14705    * @example
14706    *
14707    * var object = { 'user': 'fred' };
14708    * var getter = _.constant(object);
14709    *
14710    * getter() === object;
14711    * // => true
14712    */
14713   function constant(value) {
14714     return function() {
14715       return value;
14716     };
14717   }
14718
14719   /**
14720    * This method returns the first argument given to it.
14721    *
14722    * @static
14723    * @since 0.1.0
14724    * @memberOf _
14725    * @category Util
14726    * @param {*} value Any value.
14727    * @returns {*} Returns `value`.
14728    * @example
14729    *
14730    * var object = { 'user': 'fred' };
14731    *
14732    * _.identity(object) === object;
14733    * // => true
14734    */
14735   function identity(value) {
14736     return value;
14737   }
14738
14739   /**
14740    * Creates a function that invokes `func` with the arguments of the created
14741    * function. If `func` is a property name, the created function returns the
14742    * property value for a given element. If `func` is an array or object, the
14743    * created function returns `true` for elements that contain the equivalent
14744    * source properties, otherwise it returns `false`.
14745    *
14746    * @static
14747    * @since 4.0.0
14748    * @memberOf _
14749    * @category Util
14750    * @param {*} [func=_.identity] The value to convert to a callback.
14751    * @returns {Function} Returns the callback.
14752    * @example
14753    *
14754    * var users = [
14755    *   { 'user': 'barney', 'age': 36, 'active': true },
14756    *   { 'user': 'fred',   'age': 40, 'active': false }
14757    * ];
14758    *
14759    * // The `_.matches` iteratee shorthand.
14760    * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));
14761    * // => [{ 'user': 'barney', 'age': 36, 'active': true }]
14762    *
14763    * // The `_.matchesProperty` iteratee shorthand.
14764    * _.filter(users, _.iteratee(['user', 'fred']));
14765    * // => [{ 'user': 'fred', 'age': 40 }]
14766    *
14767    * // The `_.property` iteratee shorthand.
14768    * _.map(users, _.iteratee('user'));
14769    * // => ['barney', 'fred']
14770    *
14771    * // Create custom iteratee shorthands.
14772    * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {
14773    *   return !_.isRegExp(func) ? iteratee(func) : function(string) {
14774    *     return func.test(string);
14775    *   };
14776    * });
14777    *
14778    * _.filter(['abc', 'def'], /ef/);
14779    * // => ['def']
14780    */
14781   function iteratee(func) {
14782     return baseIteratee(typeof func == 'function' ? func : baseClone(func, true));
14783   }
14784
14785   /**
14786    * Adds all own enumerable string keyed function properties of a source
14787    * object to the destination object. If `object` is a function, then methods
14788    * are added to its prototype as well.
14789    *
14790    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
14791    * avoid conflicts caused by modifying the original.
14792    *
14793    * @static
14794    * @since 0.1.0
14795    * @memberOf _
14796    * @category Util
14797    * @param {Function|Object} [object=lodash] The destination object.
14798    * @param {Object} source The object of functions to add.
14799    * @param {Object} [options={}] The options object.
14800    * @param {boolean} [options.chain=true] Specify whether mixins are chainable.
14801    * @returns {Function|Object} Returns `object`.
14802    * @example
14803    *
14804    * function vowels(string) {
14805    *   return _.filter(string, function(v) {
14806    *     return /[aeiou]/i.test(v);
14807    *   });
14808    * }
14809    *
14810    * _.mixin({ 'vowels': vowels });
14811    * _.vowels('fred');
14812    * // => ['e']
14813    *
14814    * _('fred').vowels().value();
14815    * // => ['e']
14816    *
14817    * _.mixin({ 'vowels': vowels }, { 'chain': false });
14818    * _('fred').vowels();
14819    * // => ['e']
14820    */
14821   function mixin(object, source, options) {
14822     var props = keys(source),
14823         methodNames = baseFunctions(source, props);
14824
14825     if (options == null &&
14826         !(isObject(source) && (methodNames.length || !props.length))) {
14827       options = source;
14828       source = object;
14829       object = this;
14830       methodNames = baseFunctions(source, keys(source));
14831     }
14832     var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
14833         isFunc = isFunction(object);
14834
14835     arrayEach(methodNames, function(methodName) {
14836       var func = source[methodName];
14837       object[methodName] = func;
14838       if (isFunc) {
14839         object.prototype[methodName] = function() {
14840           var chainAll = this.__chain__;
14841           if (chain || chainAll) {
14842             var result = object(this.__wrapped__),
14843                 actions = result.__actions__ = copyArray(this.__actions__);
14844
14845             actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
14846             result.__chain__ = chainAll;
14847             return result;
14848           }
14849           return func.apply(object, arrayPush([this.value()], arguments));
14850         };
14851       }
14852     });
14853
14854     return object;
14855   }
14856
14857   /**
14858    * A no-operation function that returns `undefined` regardless of the
14859    * arguments it receives.
14860    *
14861    * @static
14862    * @memberOf _
14863    * @since 2.3.0
14864    * @category Util
14865    * @example
14866    *
14867    * var object = { 'user': 'fred' };
14868    *
14869    * _.noop(object) === undefined;
14870    * // => true
14871    */
14872   function noop() {
14873     // No operation performed.
14874   }
14875
14876   /**
14877    * Creates a function that returns the value at `path` of a given object.
14878    *
14879    * @static
14880    * @memberOf _
14881    * @since 2.4.0
14882    * @category Util
14883    * @param {Array|string} path The path of the property to get.
14884    * @returns {Function} Returns the new accessor function.
14885    * @example
14886    *
14887    * var objects = [
14888    *   { 'a': { 'b': 2 } },
14889    *   { 'a': { 'b': 1 } }
14890    * ];
14891    *
14892    * _.map(objects, _.property('a.b'));
14893    * // => [2, 1]
14894    *
14895    * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
14896    * // => [1, 2]
14897    */
14898   function property(path) {
14899     return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
14900   }
14901
14902   /*------------------------------------------------------------------------*/
14903
14904   // Add methods that return wrapped values in chain sequences.
14905   lodash.assign = assign;
14906   lodash.assignIn = assignIn;
14907   lodash.bind = bind;
14908   lodash.chain = chain;
14909   lodash.chunk = chunk;
14910   lodash.compact = compact;
14911   lodash.constant = constant;
14912   lodash.debounce = debounce;
14913   lodash.difference = difference;
14914   lodash.filter = filter;
14915   lodash.flatten = flatten;
14916   lodash.groupBy = groupBy;
14917   lodash.intersection = intersection;
14918   lodash.iteratee = iteratee;
14919   lodash.keys = keys;
14920   lodash.keysIn = keysIn;
14921   lodash.map = map;
14922   lodash.memoize = memoize;
14923   lodash.merge = merge;
14924   lodash.mixin = mixin;
14925   lodash.omit = omit;
14926   lodash.pick = pick;
14927   lodash.property = property;
14928   lodash.reject = reject;
14929   lodash.rest = rest;
14930   lodash.reverse = reverse;
14931   lodash.tap = tap;
14932   lodash.throttle = throttle;
14933   lodash.thru = thru;
14934   lodash.toArray = toArray;
14935   lodash.toPairs = toPairs;
14936   lodash.toPlainObject = toPlainObject;
14937   lodash.union = union;
14938   lodash.uniq = uniq;
14939   lodash.values = values;
14940   lodash.without = without;
14941
14942   // Add aliases.
14943   lodash.entries = toPairs;
14944   lodash.extend = assignIn;
14945
14946   // Add methods to `lodash.prototype`.
14947   mixin(lodash, lodash);
14948
14949   /*------------------------------------------------------------------------*/
14950
14951   // Add methods that return unwrapped values in chain sequences.
14952   lodash.clone = clone;
14953   lodash.cloneDeep = cloneDeep;
14954   lodash.eq = eq;
14955   lodash.every = every;
14956   lodash.find = find;
14957   lodash.forEach = forEach;
14958   lodash.forOwn = forOwn;
14959   lodash.get = get;
14960   lodash.hasIn = hasIn;
14961   lodash.head = head;
14962   lodash.identity = identity;
14963   lodash.includes = includes;
14964   lodash.indexOf = indexOf;
14965   lodash.isArguments = isArguments;
14966   lodash.isArray = isArray;
14967   lodash.isArrayLike = isArrayLike;
14968   lodash.isArrayLikeObject = isArrayLikeObject;
14969   lodash.isBuffer = isBuffer;
14970   lodash.isEmpty = isEmpty;
14971   lodash.isEqual = isEqual;
14972   lodash.isFunction = isFunction;
14973   lodash.isLength = isLength;
14974   lodash.isNative = isNative;
14975   lodash.isObject = isObject;
14976   lodash.isObjectLike = isObjectLike;
14977   lodash.isPlainObject = isPlainObject;
14978   lodash.isString = isString;
14979   lodash.isSymbol = isSymbol;
14980   lodash.isTypedArray = isTypedArray;
14981   lodash.last = last;
14982   lodash.noop = noop;
14983   lodash.now = now;
14984   lodash.reduce = reduce;
14985   lodash.some = some;
14986   lodash.toFinite = toFinite;
14987   lodash.toInteger = toInteger;
14988   lodash.toNumber = toNumber;
14989   lodash.toString = toString;
14990
14991   // Add aliases.
14992   lodash.each = forEach;
14993   lodash.first = head;
14994
14995   mixin(lodash, (function() {
14996     var source = {};
14997     baseForOwn(lodash, function(func, methodName) {
14998       if (!hasOwnProperty.call(lodash.prototype, methodName)) {
14999         source[methodName] = func;
15000       }
15001     });
15002     return source;
15003   }()), { 'chain': false });
15004
15005   /*------------------------------------------------------------------------*/
15006
15007   /**
15008    * The semantic version number.
15009    *
15010    * @static
15011    * @memberOf _
15012    * @type {string}
15013    */
15014   lodash.VERSION = VERSION;
15015
15016   // Assign default placeholders.
15017   bind.placeholder = lodash;
15018
15019   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
15020   arrayEach(['drop', 'take'], function(methodName, index) {
15021     LazyWrapper.prototype[methodName] = function(n) {
15022       var filtered = this.__filtered__;
15023       if (filtered && !index) {
15024         return new LazyWrapper(this);
15025       }
15026       n = n === undefined ? 1 : nativeMax(toInteger(n), 0);
15027
15028       var result = this.clone();
15029       if (filtered) {
15030         result.__takeCount__ = nativeMin(n, result.__takeCount__);
15031       } else {
15032         result.__views__.push({
15033           'size': nativeMin(n, MAX_ARRAY_LENGTH),
15034           'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
15035         });
15036       }
15037       return result;
15038     };
15039
15040     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
15041       return this.reverse()[methodName](n).reverse();
15042     };
15043   });
15044
15045   // Add `LazyWrapper` methods that accept an `iteratee` value.
15046   arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
15047     var type = index + 1,
15048         isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
15049
15050     LazyWrapper.prototype[methodName] = function(iteratee) {
15051       var result = this.clone();
15052       result.__iteratees__.push({
15053         'iteratee': getIteratee(iteratee, 3),
15054         'type': type
15055       });
15056       result.__filtered__ = result.__filtered__ || isFilter;
15057       return result;
15058     };
15059   });
15060
15061   // Add `LazyWrapper` methods for `_.head` and `_.last`.
15062   arrayEach(['head', 'last'], function(methodName, index) {
15063     var takeName = 'take' + (index ? 'Right' : '');
15064
15065     LazyWrapper.prototype[methodName] = function() {
15066       return this[takeName](1).value()[0];
15067     };
15068   });
15069
15070   // Add `LazyWrapper` methods for `_.initial` and `_.tail`.
15071   arrayEach(['initial', 'tail'], function(methodName, index) {
15072     var dropName = 'drop' + (index ? '' : 'Right');
15073
15074     LazyWrapper.prototype[methodName] = function() {
15075       return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
15076     };
15077   });
15078
15079   LazyWrapper.prototype.compact = function() {
15080     return this.filter(identity);
15081   };
15082
15083   LazyWrapper.prototype.find = function(predicate) {
15084     return this.filter(predicate).head();
15085   };
15086
15087   LazyWrapper.prototype.findLast = function(predicate) {
15088     return this.reverse().find(predicate);
15089   };
15090
15091   LazyWrapper.prototype.invokeMap = rest(function(path, args) {
15092     if (typeof path == 'function') {
15093       return new LazyWrapper(this);
15094     }
15095     return this.map(function(value) {
15096       return baseInvoke(value, path, args);
15097     });
15098   });
15099
15100   LazyWrapper.prototype.reject = function(predicate) {
15101     predicate = getIteratee(predicate, 3);
15102     return this.filter(function(value) {
15103       return !predicate(value);
15104     });
15105   };
15106
15107   LazyWrapper.prototype.slice = function(start, end) {
15108     start = toInteger(start);
15109
15110     var result = this;
15111     if (result.__filtered__ && (start > 0 || end < 0)) {
15112       return new LazyWrapper(result);
15113     }
15114     if (start < 0) {
15115       result = result.takeRight(-start);
15116     } else if (start) {
15117       result = result.drop(start);
15118     }
15119     if (end !== undefined) {
15120       end = toInteger(end);
15121       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
15122     }
15123     return result;
15124   };
15125
15126   LazyWrapper.prototype.takeRightWhile = function(predicate) {
15127     return this.reverse().takeWhile(predicate).reverse();
15128   };
15129
15130   LazyWrapper.prototype.toArray = function() {
15131     return this.take(MAX_ARRAY_LENGTH);
15132   };
15133
15134   // Add `LazyWrapper` methods to `lodash.prototype`.
15135   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
15136     var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
15137         isTaker = /^(?:head|last)$/.test(methodName),
15138         lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
15139         retUnwrapped = isTaker || /^find/.test(methodName);
15140
15141     if (!lodashFunc) {
15142       return;
15143     }
15144     lodash.prototype[methodName] = function() {
15145       var value = this.__wrapped__,
15146           args = isTaker ? [1] : arguments,
15147           isLazy = value instanceof LazyWrapper,
15148           iteratee = args[0],
15149           useLazy = isLazy || isArray(value);
15150
15151       var interceptor = function(value) {
15152         var result = lodashFunc.apply(lodash, arrayPush([value], args));
15153         return (isTaker && chainAll) ? result[0] : result;
15154       };
15155
15156       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
15157         // Avoid lazy use if the iteratee has a "length" value other than `1`.
15158         isLazy = useLazy = false;
15159       }
15160       var chainAll = this.__chain__,
15161           isHybrid = !!this.__actions__.length,
15162           isUnwrapped = retUnwrapped && !chainAll,
15163           onlyLazy = isLazy && !isHybrid;
15164
15165       if (!retUnwrapped && useLazy) {
15166         value = onlyLazy ? value : new LazyWrapper(this);
15167         var result = func.apply(value, args);
15168         result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
15169         return new LodashWrapper(result, chainAll);
15170       }
15171       if (isUnwrapped && onlyLazy) {
15172         return func.apply(this, args);
15173       }
15174       result = this.thru(interceptor);
15175       return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
15176     };
15177   });
15178
15179   // Add `Array` methods to `lodash.prototype`.
15180   arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
15181     var func = arrayProto[methodName],
15182         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
15183         retUnwrapped = /^(?:pop|shift)$/.test(methodName);
15184
15185     lodash.prototype[methodName] = function() {
15186       var args = arguments;
15187       if (retUnwrapped && !this.__chain__) {
15188         var value = this.value();
15189         return func.apply(isArray(value) ? value : [], args);
15190       }
15191       return this[chainName](function(value) {
15192         return func.apply(isArray(value) ? value : [], args);
15193       });
15194     };
15195   });
15196
15197   // Map minified method names to their real names.
15198   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
15199     var lodashFunc = lodash[methodName];
15200     if (lodashFunc) {
15201       var key = (lodashFunc.name + ''),
15202           names = realNames[key] || (realNames[key] = []);
15203
15204       names.push({ 'name': methodName, 'func': lodashFunc });
15205     }
15206   });
15207
15208   realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{
15209     'name': 'wrapper',
15210     'func': undefined
15211   }];
15212
15213   // Add methods to `LazyWrapper`.
15214   LazyWrapper.prototype.clone = lazyClone;
15215   LazyWrapper.prototype.reverse = lazyReverse;
15216   LazyWrapper.prototype.value = lazyValue;
15217
15218   // Add chain sequence methods to the `lodash` wrapper.
15219   lodash.prototype.chain = wrapperChain;
15220   lodash.prototype.commit = wrapperCommit;
15221   lodash.prototype.next = wrapperNext;
15222   lodash.prototype.plant = wrapperPlant;
15223   lodash.prototype.reverse = wrapperReverse;
15224   lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
15225
15226   if (iteratorSymbol) {
15227     lodash.prototype[iteratorSymbol] = wrapperToIterator;
15228   }
15229
15230   /*--------------------------------------------------------------------------*/
15231
15232   // Expose Lodash on the free variable `window` or `self` when available so it's
15233   // globally accessible, even when bundled with Browserify, Webpack, etc. This
15234   // also prevents errors in cases where Lodash is loaded by a script tag in the
15235   // presence of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch
15236   // for more details. Use `_.noConflict` to remove Lodash from the global object.
15237   (freeWindow || freeSelf || {})._ = lodash;
15238
15239   if (freeExports && freeModule) {
15240     // Export for Node.js.
15241     if (moduleExports) {
15242       (freeModule.exports = lodash)._ = lodash;
15243     }
15244     // Export for CommonJS support.
15245     freeExports._ = lodash;
15246   }
15247   else {
15248     // Export to the global object.
15249     root._ = lodash;
15250   }
15251 }.call(this));
15252 (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;
15253 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){
15254 'use strict';
15255
15256 var ohauth = require('ohauth'),
15257     xtend = require('xtend'),
15258     store = require('store');
15259
15260 // # osm-auth
15261 //
15262 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
15263 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
15264 // does not support custom headers, which this uses everywhere.
15265 module.exports = function(o) {
15266
15267     var oauth = {};
15268
15269     // authenticated users will also have a request token secret, but it's
15270     // not used in transactions with the server
15271     oauth.authenticated = function() {
15272         return !!(token('oauth_token') && token('oauth_token_secret'));
15273     };
15274
15275     oauth.logout = function() {
15276         token('oauth_token', '');
15277         token('oauth_token_secret', '');
15278         token('oauth_request_token_secret', '');
15279         return oauth;
15280     };
15281
15282     // TODO: detect lack of click event
15283     oauth.authenticate = function(callback) {
15284         if (oauth.authenticated()) return callback();
15285
15286         oauth.logout();
15287
15288         // ## Getting a request token
15289         var params = timenonce(getAuth(o)),
15290             url = o.url + '/oauth/request_token',
15291             timer;
15292
15293         params.oauth_signature = ohauth.signature(
15294             o.oauth_secret, '',
15295             ohauth.baseString('POST', url, params));
15296
15297         if (!o.singlepage) {
15298             // Create a 600x550 popup window in the center of the screen
15299             var w = 600, h = 550,
15300                 settings = [
15301                     ['width', w], ['height', h],
15302                     ['left', screen.width / 2 - w / 2],
15303                     ['top', screen.height / 2 - h / 2]].map(function(x) {
15304                         return x.join('=');
15305                     }).join(','),
15306                 popup = window.open('about:blank', 'oauth_window', settings);
15307
15308
15309             timer = setInterval(function() {
15310                 if (popup.closed) {
15311                     o.done();
15312                     clearInterval(timer);
15313                     callback('not authenticated', null);
15314                 }
15315             }, 100);
15316         }
15317
15318         // Request a request token. When this is complete, the popup
15319         // window is redirected to OSM's authorization page.
15320         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
15321         o.loading();
15322
15323         function reqTokenDone(err, xhr) {
15324             o.done();
15325             if (err) {
15326                 if (timer) clearInterval(timer);
15327                 return callback(err);
15328             }
15329             var resp = ohauth.stringQs(xhr.response);
15330             token('oauth_request_token_secret', resp.oauth_token_secret);
15331             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
15332                 oauth_token: resp.oauth_token,
15333                 oauth_callback: location.href.replace('index.html', '')
15334                     .replace(/#.*/, '') + o.landing
15335             });
15336
15337             if (o.singlepage) {
15338                 location.href = authorize_url;
15339             } else {
15340                 popup.location = authorize_url;
15341             }
15342         }
15343
15344         // Called by a function in a landing page, in the popup window. The
15345         // window closes itself.
15346         window.authComplete = function(token) {
15347             if (timer) clearInterval(timer);
15348             var oauth_token = ohauth.stringQs(token.split('?')[1]);
15349             get_access_token(oauth_token.oauth_token);
15350             delete window.authComplete;
15351         };
15352
15353         // ## Getting an request token
15354         //
15355         // At this point we have an `oauth_token`, brought in from a function
15356         // call on a landing page popup.
15357         function get_access_token(oauth_token) {
15358             var url = o.url + '/oauth/access_token',
15359                 params = timenonce(getAuth(o)),
15360                 request_token_secret = token('oauth_request_token_secret');
15361             params.oauth_token = oauth_token;
15362             params.oauth_signature = ohauth.signature(
15363                 o.oauth_secret,
15364                 request_token_secret,
15365                 ohauth.baseString('POST', url, params));
15366
15367             // ## Getting an access token
15368             //
15369             // The final token required for authentication. At this point
15370             // we have a `request token secret`
15371             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
15372             o.loading();
15373         }
15374
15375         function accessTokenDone(err, xhr) {
15376             o.done();
15377             if (timer) clearInterval(timer);
15378             if (err) return callback(err);
15379             var access_token = ohauth.stringQs(xhr.response);
15380             token('oauth_token', access_token.oauth_token);
15381             token('oauth_token_secret', access_token.oauth_token_secret);
15382             callback(null, oauth);
15383         }
15384     };
15385
15386     oauth.bootstrapToken = function(oauth_token, callback) {
15387         // ## Getting an request token
15388         // At this point we have an `oauth_token`, brought in from a function
15389         // call on a landing page popup.
15390         function get_access_token(oauth_token) {
15391             var url = o.url + '/oauth/access_token',
15392                 params = timenonce(getAuth(o)),
15393                 request_token_secret = token('oauth_request_token_secret');
15394             params.oauth_token = oauth_token;
15395             params.oauth_signature = ohauth.signature(
15396                 o.oauth_secret,
15397                 request_token_secret,
15398                 ohauth.baseString('POST', url, params));
15399
15400             // ## Getting an access token
15401             // The final token required for authentication. At this point
15402             // we have a `request token secret`
15403             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
15404             o.loading();
15405         }
15406
15407         function accessTokenDone(err, xhr) {
15408             o.done();
15409             if (err) return callback(err);
15410             var access_token = ohauth.stringQs(xhr.response);
15411             token('oauth_token', access_token.oauth_token);
15412             token('oauth_token_secret', access_token.oauth_token_secret);
15413             callback(null, oauth);
15414         }
15415
15416         get_access_token(oauth_token);
15417     };
15418
15419     // # xhr
15420     //
15421     // A single XMLHttpRequest wrapper that does authenticated calls if the
15422     // user has logged in.
15423     oauth.xhr = function(options, callback) {
15424         if (!oauth.authenticated()) {
15425             if (o.auto) return oauth.authenticate(run);
15426             else return callback('not authenticated', null);
15427         } else return run();
15428
15429         function run() {
15430             var params = timenonce(getAuth(o)),
15431                 url = o.url + options.path,
15432                 oauth_token_secret = token('oauth_token_secret');
15433
15434             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
15435             if ((!options.options || !options.options.header ||
15436                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
15437                 options.content) {
15438                 params = xtend(params, ohauth.stringQs(options.content));
15439             }
15440
15441             params.oauth_token = token('oauth_token');
15442             params.oauth_signature = ohauth.signature(
15443                 o.oauth_secret,
15444                 oauth_token_secret,
15445                 ohauth.baseString(options.method, url, params));
15446
15447             ohauth.xhr(options.method,
15448                 url, params, options.content, options.options, done);
15449         }
15450
15451         function done(err, xhr) {
15452             if (err) return callback(err);
15453             else if (xhr.responseXML) return callback(err, xhr.responseXML);
15454             else return callback(err, xhr.response);
15455         }
15456     };
15457
15458     // pre-authorize this object, if we can just get a token and token_secret
15459     // from the start
15460     oauth.preauth = function(c) {
15461         if (!c) return;
15462         if (c.oauth_token) token('oauth_token', c.oauth_token);
15463         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
15464         return oauth;
15465     };
15466
15467     oauth.options = function(_) {
15468         if (!arguments.length) return o;
15469
15470         o = _;
15471
15472         o.url = o.url || 'http://www.openstreetmap.org';
15473         o.landing = o.landing || 'land.html';
15474
15475         o.singlepage = o.singlepage || false;
15476
15477         // Optional loading and loading-done functions for nice UI feedback.
15478         // by default, no-ops
15479         o.loading = o.loading || function() {};
15480         o.done = o.done || function() {};
15481
15482         return oauth.preauth(o);
15483     };
15484
15485     // 'stamp' an authentication object from `getAuth()`
15486     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
15487     // and timestamp
15488     function timenonce(o) {
15489         o.oauth_timestamp = ohauth.timestamp();
15490         o.oauth_nonce = ohauth.nonce();
15491         return o;
15492     }
15493
15494     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
15495     // can be used with multiple APIs and the keys in `localStorage`
15496     // will not clash
15497     var token;
15498
15499     if (store.enabled) {
15500         token = function (x, y) {
15501             if (arguments.length === 1) return store.get(o.url + x);
15502             else if (arguments.length === 2) return store.set(o.url + x, y);
15503         };
15504     } else {
15505         var storage = {};
15506         token = function (x, y) {
15507             if (arguments.length === 1) return storage[o.url + x];
15508             else if (arguments.length === 2) return storage[o.url + x] = y;
15509         };
15510     }
15511
15512     // Get an authentication object. If you just add and remove properties
15513     // from a single object, you'll need to use `delete` to make sure that
15514     // it doesn't contain undesired properties for authentication
15515     function getAuth(o) {
15516         return {
15517             oauth_consumer_key: o.oauth_consumer_key,
15518             oauth_signature_method: "HMAC-SHA1"
15519         };
15520     }
15521
15522     // potentially pre-authorize
15523     oauth.options(o);
15524
15525     return oauth;
15526 };
15527
15528 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
15529 (function(global){;(function(win){
15530         var store = {},
15531                 doc = win.document,
15532                 localStorageName = 'localStorage',
15533                 storage
15534
15535         store.disabled = false
15536         store.set = function(key, value) {}
15537         store.get = function(key) {}
15538         store.remove = function(key) {}
15539         store.clear = function() {}
15540         store.transact = function(key, defaultVal, transactionFn) {
15541                 var val = store.get(key)
15542                 if (transactionFn == null) {
15543                         transactionFn = defaultVal
15544                         defaultVal = null
15545                 }
15546                 if (typeof val == 'undefined') { val = defaultVal || {} }
15547                 transactionFn(val)
15548                 store.set(key, val)
15549         }
15550         store.getAll = function() {}
15551         store.forEach = function() {}
15552
15553         store.serialize = function(value) {
15554                 return JSON.stringify(value)
15555         }
15556         store.deserialize = function(value) {
15557                 if (typeof value != 'string') { return undefined }
15558                 try { return JSON.parse(value) }
15559                 catch(e) { return value || undefined }
15560         }
15561
15562         // Functions to encapsulate questionable FireFox 3.6.13 behavior
15563         // when about.config::dom.storage.enabled === false
15564         // See https://github.com/marcuswestin/store.js/issues#issue/13
15565         function isLocalStorageNameSupported() {
15566                 try { return (localStorageName in win && win[localStorageName]) }
15567                 catch(err) { return false }
15568         }
15569
15570         if (isLocalStorageNameSupported()) {
15571                 storage = win[localStorageName]
15572                 store.set = function(key, val) {
15573                         if (val === undefined) { return store.remove(key) }
15574                         storage.setItem(key, store.serialize(val))
15575                         return val
15576                 }
15577                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
15578                 store.remove = function(key) { storage.removeItem(key) }
15579                 store.clear = function() { storage.clear() }
15580                 store.getAll = function() {
15581                         var ret = {}
15582                         store.forEach(function(key, val) {
15583                                 ret[key] = val
15584                         })
15585                         return ret
15586                 }
15587                 store.forEach = function(callback) {
15588                         for (var i=0; i<storage.length; i++) {
15589                                 var key = storage.key(i)
15590                                 callback(key, store.get(key))
15591                         }
15592                 }
15593         } else if (doc.documentElement.addBehavior) {
15594                 var storageOwner,
15595                         storageContainer
15596                 // Since #userData storage applies only to specific paths, we need to
15597                 // somehow link our data to a specific path.  We choose /favicon.ico
15598                 // as a pretty safe option, since all browsers already make a request to
15599                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
15600                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
15601                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
15602                 // since the iframe access rules appear to allow direct access and
15603                 // manipulation of the document element, even for a 404 page.  This
15604                 // document can be used instead of the current document (which would
15605                 // have been limited to the current path) to perform #userData storage.
15606                 try {
15607                         storageContainer = new ActiveXObject('htmlfile')
15608                         storageContainer.open()
15609                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
15610                         storageContainer.close()
15611                         storageOwner = storageContainer.w.frames[0].document
15612                         storage = storageOwner.createElement('div')
15613                 } catch(e) {
15614                         // somehow ActiveXObject instantiation failed (perhaps some special
15615                         // security settings or otherwse), fall back to per-path storage
15616                         storage = doc.createElement('div')
15617                         storageOwner = doc.body
15618                 }
15619                 function withIEStorage(storeFunction) {
15620                         return function() {
15621                                 var args = Array.prototype.slice.call(arguments, 0)
15622                                 args.unshift(storage)
15623                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
15624                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
15625                                 storageOwner.appendChild(storage)
15626                                 storage.addBehavior('#default#userData')
15627                                 storage.load(localStorageName)
15628                                 var result = storeFunction.apply(store, args)
15629                                 storageOwner.removeChild(storage)
15630                                 return result
15631                         }
15632                 }
15633
15634                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
15635                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
15636                 function ieKeyFix(key) {
15637                         return key.replace(forbiddenCharsRegex, '___')
15638                 }
15639                 store.set = withIEStorage(function(storage, key, val) {
15640                         key = ieKeyFix(key)
15641                         if (val === undefined) { return store.remove(key) }
15642                         storage.setAttribute(key, store.serialize(val))
15643                         storage.save(localStorageName)
15644                         return val
15645                 })
15646                 store.get = withIEStorage(function(storage, key) {
15647                         key = ieKeyFix(key)
15648                         return store.deserialize(storage.getAttribute(key))
15649                 })
15650                 store.remove = withIEStorage(function(storage, key) {
15651                         key = ieKeyFix(key)
15652                         storage.removeAttribute(key)
15653                         storage.save(localStorageName)
15654                 })
15655                 store.clear = withIEStorage(function(storage) {
15656                         var attributes = storage.XMLDocument.documentElement.attributes
15657                         storage.load(localStorageName)
15658                         for (var i=0, attr; attr=attributes[i]; i++) {
15659                                 storage.removeAttribute(attr.name)
15660                         }
15661                         storage.save(localStorageName)
15662                 })
15663                 store.getAll = function(storage) {
15664                         var ret = {}
15665                         store.forEach(function(key, val) {
15666                                 ret[key] = val
15667                         })
15668                         return ret
15669                 }
15670                 store.forEach = withIEStorage(function(storage, callback) {
15671                         var attributes = storage.XMLDocument.documentElement.attributes
15672                         for (var i=0, attr; attr=attributes[i]; ++i) {
15673                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
15674                         }
15675                 })
15676         }
15677
15678         try {
15679                 var testKey = '__storejs__'
15680                 store.set(testKey, testKey)
15681                 if (store.get(testKey) != testKey) { store.disabled = true }
15682                 store.remove(testKey)
15683         } catch(e) {
15684                 store.disabled = true
15685         }
15686         store.enabled = !store.disabled
15687
15688         if (typeof module != 'undefined' && module.exports) { module.exports = store }
15689         else if (typeof define === 'function' && define.amd) { define(store) }
15690         else { win.store = store }
15691
15692 })(this.window || global);
15693
15694 })(window)
15695 },{}],5:[function(require,module,exports){
15696 module.exports = hasKeys
15697
15698 function hasKeys(source) {
15699     return source !== null &&
15700         (typeof source === "object" ||
15701         typeof source === "function")
15702 }
15703
15704 },{}],4:[function(require,module,exports){
15705 var Keys = require("object-keys")
15706 var hasKeys = require("./has-keys")
15707
15708 module.exports = extend
15709
15710 function extend() {
15711     var target = {}
15712
15713     for (var i = 0; i < arguments.length; i++) {
15714         var source = arguments[i]
15715
15716         if (!hasKeys(source)) {
15717             continue
15718         }
15719
15720         var keys = Keys(source)
15721
15722         for (var j = 0; j < keys.length; j++) {
15723             var name = keys[j]
15724             target[name] = source[name]
15725         }
15726     }
15727
15728     return target
15729 }
15730
15731 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
15732 (function(global){/**
15733  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
15734  *
15735  * @class Hashes
15736  * @author Tomas Aparicio <tomas@rijndael-project.com>
15737  * @license New BSD (see LICENSE file)
15738  * @version 1.0.4
15739  *
15740  * Algorithms specification:
15741  *
15742  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
15743  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
15744  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15745  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15746  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15747  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
15748  *
15749  */
15750 (function(){
15751   var Hashes;
15752
15753   // private helper methods
15754   function utf8Encode(str) {
15755     var  x, y, output = '', i = -1, l;
15756
15757     if (str && str.length) {
15758       l = str.length;
15759       while ((i+=1) < l) {
15760         /* Decode utf-16 surrogate pairs */
15761         x = str.charCodeAt(i);
15762         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
15763         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
15764             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
15765             i += 1;
15766         }
15767         /* Encode output as utf-8 */
15768         if (x <= 0x7F) {
15769             output += String.fromCharCode(x);
15770         } else if (x <= 0x7FF) {
15771             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
15772                         0x80 | ( x & 0x3F));
15773         } else if (x <= 0xFFFF) {
15774             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
15775                         0x80 | ((x >>> 6 ) & 0x3F),
15776                         0x80 | ( x & 0x3F));
15777         } else if (x <= 0x1FFFFF) {
15778             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
15779                         0x80 | ((x >>> 12) & 0x3F),
15780                         0x80 | ((x >>> 6 ) & 0x3F),
15781                         0x80 | ( x & 0x3F));
15782         }
15783       }
15784     }
15785     return output;
15786   }
15787
15788   function utf8Decode(str) {
15789     var i, ac, c1, c2, c3, arr = [], l;
15790     i = ac = c1 = c2 = c3 = 0;
15791
15792     if (str && str.length) {
15793       l = str.length;
15794       str += '';
15795
15796       while (i < l) {
15797           c1 = str.charCodeAt(i);
15798           ac += 1;
15799           if (c1 < 128) {
15800               arr[ac] = String.fromCharCode(c1);
15801               i+=1;
15802           } else if (c1 > 191 && c1 < 224) {
15803               c2 = str.charCodeAt(i + 1);
15804               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
15805               i += 2;
15806           } else {
15807               c2 = str.charCodeAt(i + 1);
15808               c3 = str.charCodeAt(i + 2);
15809               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
15810               i += 3;
15811           }
15812       }
15813     }
15814     return arr.join('');
15815   }
15816
15817   /**
15818    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
15819    * to work around bugs in some JS interpreters.
15820    */
15821   function safe_add(x, y) {
15822     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
15823         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
15824     return (msw << 16) | (lsw & 0xFFFF);
15825   }
15826
15827   /**
15828    * Bitwise rotate a 32-bit number to the left.
15829    */
15830   function bit_rol(num, cnt) {
15831     return (num << cnt) | (num >>> (32 - cnt));
15832   }
15833
15834   /**
15835    * Convert a raw string to a hex string
15836    */
15837   function rstr2hex(input, hexcase) {
15838     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
15839         output = '', x, i = 0, l = input.length;
15840     for (; i < l; i+=1) {
15841       x = input.charCodeAt(i);
15842       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
15843     }
15844     return output;
15845   }
15846
15847   /**
15848    * Encode a string as utf-16
15849    */
15850   function str2rstr_utf16le(input) {
15851     var i, l = input.length, output = '';
15852     for (i = 0; i < l; i+=1) {
15853       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
15854     }
15855     return output;
15856   }
15857
15858   function str2rstr_utf16be(input) {
15859     var i, l = input.length, output = '';
15860     for (i = 0; i < l; i+=1) {
15861       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
15862     }
15863     return output;
15864   }
15865
15866   /**
15867    * Convert an array of big-endian words to a string
15868    */
15869   function binb2rstr(input) {
15870     var i, l = input.length * 32, output = '';
15871     for (i = 0; i < l; i += 8) {
15872         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
15873     }
15874     return output;
15875   }
15876
15877   /**
15878    * Convert an array of little-endian words to a string
15879    */
15880   function binl2rstr(input) {
15881     var i, l = input.length * 32, output = '';
15882     for (i = 0;i < l; i += 8) {
15883       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15884     }
15885     return output;
15886   }
15887
15888   /**
15889    * Convert a raw string to an array of little-endian words
15890    * Characters >255 have their high-byte silently ignored.
15891    */
15892   function rstr2binl(input) {
15893     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
15894     for (i = 0; i < lo; i+=1) {
15895       output[i] = 0;
15896     }
15897     for (i = 0; i < l; i += 8) {
15898       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
15899     }
15900     return output;
15901   }
15902
15903   /**
15904    * Convert a raw string to an array of big-endian words
15905    * Characters >255 have their high-byte silently ignored.
15906    */
15907    function rstr2binb(input) {
15908       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
15909       for (i = 0; i < lo; i+=1) {
15910             output[i] = 0;
15911         }
15912       for (i = 0; i < l; i += 8) {
15913             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
15914         }
15915       return output;
15916    }
15917
15918   /**
15919    * Convert a raw string to an arbitrary string encoding
15920    */
15921   function rstr2any(input, encoding) {
15922     var divisor = encoding.length,
15923         remainders = Array(),
15924         i, q, x, ld, quotient, dividend, output, full_length;
15925
15926     /* Convert to an array of 16-bit big-endian values, forming the dividend */
15927     dividend = Array(Math.ceil(input.length / 2));
15928     ld = dividend.length;
15929     for (i = 0; i < ld; i+=1) {
15930       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
15931     }
15932
15933     /**
15934      * Repeatedly perform a long division. The binary array forms the dividend,
15935      * the length of the encoding is the divisor. Once computed, the quotient
15936      * forms the dividend for the next step. We stop when the dividend is zerHashes.
15937      * All remainders are stored for later use.
15938      */
15939     while(dividend.length > 0) {
15940       quotient = Array();
15941       x = 0;
15942       for (i = 0; i < dividend.length; i+=1) {
15943         x = (x << 16) + dividend[i];
15944         q = Math.floor(x / divisor);
15945         x -= q * divisor;
15946         if (quotient.length > 0 || q > 0) {
15947           quotient[quotient.length] = q;
15948         }
15949       }
15950       remainders[remainders.length] = x;
15951       dividend = quotient;
15952     }
15953
15954     /* Convert the remainders to the output string */
15955     output = '';
15956     for (i = remainders.length - 1; i >= 0; i--) {
15957       output += encoding.charAt(remainders[i]);
15958     }
15959
15960     /* Append leading zero equivalents */
15961     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
15962     for (i = output.length; i < full_length; i+=1) {
15963       output = encoding[0] + output;
15964     }
15965     return output;
15966   }
15967
15968   /**
15969    * Convert a raw string to a base-64 string
15970    */
15971   function rstr2b64(input, b64pad) {
15972     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
15973         output = '',
15974         len = input.length, i, j, triplet;
15975     b64pad= b64pad || '=';
15976     for (i = 0; i < len; i += 3) {
15977       triplet = (input.charCodeAt(i) << 16)
15978             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
15979             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
15980       for (j = 0; j < 4; j+=1) {
15981         if (i * 8 + j * 6 > input.length * 8) {
15982           output += b64pad;
15983         } else {
15984           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
15985         }
15986        }
15987     }
15988     return output;
15989   }
15990
15991   Hashes = {
15992   /**
15993    * @property {String} version
15994    * @readonly
15995    */
15996   VERSION : '1.0.3',
15997   /**
15998    * @member Hashes
15999    * @class Base64
16000    * @constructor
16001    */
16002   Base64 : function () {
16003     // private properties
16004     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
16005         pad = '=', // default pad according with the RFC standard
16006         url = false, // URL encoding support @todo
16007         utf8 = true; // by default enable UTF-8 support encoding
16008
16009     // public method for encoding
16010     this.encode = function (input) {
16011       var i, j, triplet,
16012           output = '',
16013           len = input.length;
16014
16015       pad = pad || '=';
16016       input = (utf8) ? utf8Encode(input) : input;
16017
16018       for (i = 0; i < len; i += 3) {
16019         triplet = (input.charCodeAt(i) << 16)
16020               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
16021               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
16022         for (j = 0; j < 4; j+=1) {
16023           if (i * 8 + j * 6 > len * 8) {
16024               output += pad;
16025           } else {
16026               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
16027           }
16028         }
16029       }
16030       return output;
16031     };
16032
16033     // public method for decoding
16034     this.decode = function (input) {
16035       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
16036       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
16037         dec = '',
16038         arr = [];
16039       if (!input) { return input; }
16040
16041       i = ac = 0;
16042       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
16043       //input += '';
16044
16045       do { // unpack four hexets into three octets using index points in b64
16046         h1 = tab.indexOf(input.charAt(i+=1));
16047         h2 = tab.indexOf(input.charAt(i+=1));
16048         h3 = tab.indexOf(input.charAt(i+=1));
16049         h4 = tab.indexOf(input.charAt(i+=1));
16050
16051         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
16052
16053         o1 = bits >> 16 & 0xff;
16054         o2 = bits >> 8 & 0xff;
16055         o3 = bits & 0xff;
16056         ac += 1;
16057
16058         if (h3 === 64) {
16059           arr[ac] = String.fromCharCode(o1);
16060         } else if (h4 === 64) {
16061           arr[ac] = String.fromCharCode(o1, o2);
16062         } else {
16063           arr[ac] = String.fromCharCode(o1, o2, o3);
16064         }
16065       } while (i < input.length);
16066
16067       dec = arr.join('');
16068       dec = (utf8) ? utf8Decode(dec) : dec;
16069
16070       return dec;
16071     };
16072
16073     // set custom pad string
16074     this.setPad = function (str) {
16075         pad = str || pad;
16076         return this;
16077     };
16078     // set custom tab string characters
16079     this.setTab = function (str) {
16080         tab = str || tab;
16081         return this;
16082     };
16083     this.setUTF8 = function (bool) {
16084         if (typeof bool === 'boolean') {
16085           utf8 = bool;
16086         }
16087         return this;
16088     };
16089   },
16090
16091   /**
16092    * CRC-32 calculation
16093    * @member Hashes
16094    * @method CRC32
16095    * @static
16096    * @param {String} str Input String
16097    * @return {String}
16098    */
16099   CRC32 : function (str) {
16100     var crc = 0, x = 0, y = 0, table, i, iTop;
16101     str = utf8Encode(str);
16102
16103     table = [
16104         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
16105         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
16106         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
16107         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
16108         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
16109         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
16110         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
16111         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
16112         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
16113         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
16114         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
16115         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
16116         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
16117         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
16118         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
16119         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
16120         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
16121         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
16122         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
16123         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
16124         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
16125         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
16126         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
16127         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
16128         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
16129         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
16130     ].join('');
16131
16132     crc = crc ^ (-1);
16133     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
16134         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
16135         x = '0x' + table.substr( y * 9, 8 );
16136         crc = ( crc >>> 8 ) ^ x;
16137     }
16138     // always return a positive number (that's what >>> 0 does)
16139     return (crc ^ (-1)) >>> 0;
16140   },
16141   /**
16142    * @member Hashes
16143    * @class MD5
16144    * @constructor
16145    * @param {Object} [config]
16146    *
16147    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
16148    * Digest Algorithm, as defined in RFC 1321.
16149    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
16150    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16151    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
16152    */
16153   MD5 : function (options) {
16154     /**
16155      * Private config properties. You may need to tweak these to be compatible with
16156      * the server-side, but the defaults work in most cases.
16157      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
16158      */
16159     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
16160         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
16161         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
16162
16163     // privileged (public) methods
16164     this.hex = function (s) {
16165       return rstr2hex(rstr(s, utf8), hexcase);
16166     };
16167     this.b64 = function (s) {
16168       return rstr2b64(rstr(s), b64pad);
16169     };
16170     this.any = function(s, e) {
16171       return rstr2any(rstr(s, utf8), e);
16172     };
16173     this.hex_hmac = function (k, d) {
16174       return rstr2hex(rstr_hmac(k, d), hexcase);
16175     };
16176     this.b64_hmac = function (k, d) {
16177       return rstr2b64(rstr_hmac(k,d), b64pad);
16178     };
16179     this.any_hmac = function (k, d, e) {
16180       return rstr2any(rstr_hmac(k, d), e);
16181     };
16182     /**
16183      * Perform a simple self-test to see if the VM is working
16184      * @return {String} Hexadecimal hash sample
16185      */
16186     this.vm_test = function () {
16187       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16188     };
16189     /**
16190      * Enable/disable uppercase hexadecimal returned string
16191      * @param {Boolean}
16192      * @return {Object} this
16193      */
16194     this.setUpperCase = function (a) {
16195       if (typeof a === 'boolean' ) {
16196         hexcase = a;
16197       }
16198       return this;
16199     };
16200     /**
16201      * Defines a base64 pad string
16202      * @param {String} Pad
16203      * @return {Object} this
16204      */
16205     this.setPad = function (a) {
16206       b64pad = a || b64pad;
16207       return this;
16208     };
16209     /**
16210      * Defines a base64 pad string
16211      * @param {Boolean}
16212      * @return {Object} [this]
16213      */
16214     this.setUTF8 = function (a) {
16215       if (typeof a === 'boolean') {
16216         utf8 = a;
16217       }
16218       return this;
16219     };
16220
16221     // private methods
16222
16223     /**
16224      * Calculate the MD5 of a raw string
16225      */
16226     function rstr(s) {
16227       s = (utf8) ? utf8Encode(s): s;
16228       return binl2rstr(binl(rstr2binl(s), s.length * 8));
16229     }
16230
16231     /**
16232      * Calculate the HMAC-MD5, of a key and some data (raw strings)
16233      */
16234     function rstr_hmac(key, data) {
16235       var bkey, ipad, opad, hash, i;
16236
16237       key = (utf8) ? utf8Encode(key) : key;
16238       data = (utf8) ? utf8Encode(data) : data;
16239       bkey = rstr2binl(key);
16240       if (bkey.length > 16) {
16241         bkey = binl(bkey, key.length * 8);
16242       }
16243
16244       ipad = Array(16), opad = Array(16);
16245       for (i = 0; i < 16; i+=1) {
16246           ipad[i] = bkey[i] ^ 0x36363636;
16247           opad[i] = bkey[i] ^ 0x5C5C5C5C;
16248       }
16249       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
16250       return binl2rstr(binl(opad.concat(hash), 512 + 128));
16251     }
16252
16253     /**
16254      * Calculate the MD5 of an array of little-endian words, and a bit length.
16255      */
16256     function binl(x, len) {
16257       var i, olda, oldb, oldc, oldd,
16258           a =  1732584193,
16259           b = -271733879,
16260           c = -1732584194,
16261           d =  271733878;
16262
16263       /* append padding */
16264       x[len >> 5] |= 0x80 << ((len) % 32);
16265       x[(((len + 64) >>> 9) << 4) + 14] = len;
16266
16267       for (i = 0; i < x.length; i += 16) {
16268         olda = a;
16269         oldb = b;
16270         oldc = c;
16271         oldd = d;
16272
16273         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
16274         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
16275         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
16276         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
16277         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
16278         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
16279         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
16280         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
16281         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
16282         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
16283         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
16284         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
16285         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
16286         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
16287         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
16288         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
16289
16290         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
16291         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
16292         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
16293         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
16294         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
16295         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
16296         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
16297         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
16298         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
16299         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
16300         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
16301         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
16302         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
16303         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
16304         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
16305         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
16306
16307         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
16308         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
16309         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
16310         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
16311         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
16312         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
16313         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
16314         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
16315         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
16316         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
16317         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
16318         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
16319         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
16320         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
16321         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
16322         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
16323
16324         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
16325         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
16326         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
16327         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
16328         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
16329         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
16330         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
16331         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
16332         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
16333         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
16334         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
16335         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
16336         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
16337         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
16338         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
16339         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
16340
16341         a = safe_add(a, olda);
16342         b = safe_add(b, oldb);
16343         c = safe_add(c, oldc);
16344         d = safe_add(d, oldd);
16345       }
16346       return Array(a, b, c, d);
16347     }
16348
16349     /**
16350      * These functions implement the four basic operations the algorithm uses.
16351      */
16352     function md5_cmn(q, a, b, x, s, t) {
16353       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
16354     }
16355     function md5_ff(a, b, c, d, x, s, t) {
16356       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
16357     }
16358     function md5_gg(a, b, c, d, x, s, t) {
16359       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
16360     }
16361     function md5_hh(a, b, c, d, x, s, t) {
16362       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
16363     }
16364     function md5_ii(a, b, c, d, x, s, t) {
16365       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
16366     }
16367   },
16368   /**
16369    * @member Hashes
16370    * @class Hashes.SHA1
16371    * @param {Object} [config]
16372    * @constructor
16373    *
16374    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
16375    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
16376    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16377    * See http://pajhome.org.uk/crypt/md5 for details.
16378    */
16379   SHA1 : function (options) {
16380    /**
16381      * Private config properties. You may need to tweak these to be compatible with
16382      * the server-side, but the defaults work in most cases.
16383      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
16384      */
16385     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
16386         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
16387         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
16388
16389     // public methods
16390     this.hex = function (s) {
16391         return rstr2hex(rstr(s, utf8), hexcase);
16392     };
16393     this.b64 = function (s) {
16394         return rstr2b64(rstr(s, utf8), b64pad);
16395     };
16396     this.any = function (s, e) {
16397         return rstr2any(rstr(s, utf8), e);
16398     };
16399     this.hex_hmac = function (k, d) {
16400         return rstr2hex(rstr_hmac(k, d));
16401     };
16402     this.b64_hmac = function (k, d) {
16403         return rstr2b64(rstr_hmac(k, d), b64pad);
16404     };
16405     this.any_hmac = function (k, d, e) {
16406         return rstr2any(rstr_hmac(k, d), e);
16407     };
16408     /**
16409      * Perform a simple self-test to see if the VM is working
16410      * @return {String} Hexadecimal hash sample
16411      * @public
16412      */
16413     this.vm_test = function () {
16414       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16415     };
16416     /**
16417      * @description Enable/disable uppercase hexadecimal returned string
16418      * @param {boolean}
16419      * @return {Object} this
16420      * @public
16421      */
16422     this.setUpperCase = function (a) {
16423         if (typeof a === 'boolean') {
16424         hexcase = a;
16425       }
16426         return this;
16427     };
16428     /**
16429      * @description Defines a base64 pad string
16430      * @param {string} Pad
16431      * @return {Object} this
16432      * @public
16433      */
16434     this.setPad = function (a) {
16435       b64pad = a || b64pad;
16436         return this;
16437     };
16438     /**
16439      * @description Defines a base64 pad string
16440      * @param {boolean}
16441      * @return {Object} this
16442      * @public
16443      */
16444     this.setUTF8 = function (a) {
16445         if (typeof a === 'boolean') {
16446         utf8 = a;
16447       }
16448         return this;
16449     };
16450
16451     // private methods
16452
16453     /**
16454          * Calculate the SHA-512 of a raw string
16455          */
16456         function rstr(s) {
16457       s = (utf8) ? utf8Encode(s) : s;
16458       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16459         }
16460
16461     /**
16462      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
16463      */
16464     function rstr_hmac(key, data) {
16465         var bkey, ipad, opad, i, hash;
16466         key = (utf8) ? utf8Encode(key) : key;
16467         data = (utf8) ? utf8Encode(data) : data;
16468         bkey = rstr2binb(key);
16469
16470         if (bkey.length > 16) {
16471         bkey = binb(bkey, key.length * 8);
16472       }
16473         ipad = Array(16), opad = Array(16);
16474         for (i = 0; i < 16; i+=1) {
16475                 ipad[i] = bkey[i] ^ 0x36363636;
16476                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
16477         }
16478         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
16479         return binb2rstr(binb(opad.concat(hash), 512 + 160));
16480     }
16481
16482     /**
16483      * Calculate the SHA-1 of an array of big-endian words, and a bit length
16484      */
16485     function binb(x, len) {
16486       var i, j, t, olda, oldb, oldc, oldd, olde,
16487           w = Array(80),
16488           a =  1732584193,
16489           b = -271733879,
16490           c = -1732584194,
16491           d =  271733878,
16492           e = -1009589776;
16493
16494       /* append padding */
16495       x[len >> 5] |= 0x80 << (24 - len % 32);
16496       x[((len + 64 >> 9) << 4) + 15] = len;
16497
16498       for (i = 0; i < x.length; i += 16) {
16499         olda = a,
16500         oldb = b;
16501         oldc = c;
16502         oldd = d;
16503         olde = e;
16504
16505         for (j = 0; j < 80; j+=1)       {
16506           if (j < 16) {
16507             w[j] = x[i + j];
16508           } else {
16509             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
16510           }
16511           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
16512                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
16513           e = d;
16514           d = c;
16515           c = bit_rol(b, 30);
16516           b = a;
16517           a = t;
16518         }
16519
16520         a = safe_add(a, olda);
16521         b = safe_add(b, oldb);
16522         c = safe_add(c, oldc);
16523         d = safe_add(d, oldd);
16524         e = safe_add(e, olde);
16525       }
16526       return Array(a, b, c, d, e);
16527     }
16528
16529     /**
16530      * Perform the appropriate triplet combination function for the current
16531      * iteration
16532      */
16533     function sha1_ft(t, b, c, d) {
16534       if (t < 20) { return (b & c) | ((~b) & d); }
16535       if (t < 40) { return b ^ c ^ d; }
16536       if (t < 60) { return (b & c) | (b & d) | (c & d); }
16537       return b ^ c ^ d;
16538     }
16539
16540     /**
16541      * Determine the appropriate additive constant for the current iteration
16542      */
16543     function sha1_kt(t) {
16544       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
16545                  (t < 60) ? -1894007588 : -899497514;
16546     }
16547   },
16548   /**
16549    * @class Hashes.SHA256
16550    * @param {config}
16551    *
16552    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
16553    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
16554    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16555    * See http://pajhome.org.uk/crypt/md5 for details.
16556    * Also http://anmar.eu.org/projects/jssha2/
16557    */
16558   SHA256 : function (options) {
16559     /**
16560      * Private properties configuration variables. You may need to tweak these to be compatible with
16561      * the server-side, but the defaults work in most cases.
16562      * @see this.setUpperCase() method
16563      * @see this.setPad() method
16564      */
16565     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
16566               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
16567               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
16568               sha256_K;
16569
16570     /* privileged (public) methods */
16571     this.hex = function (s) {
16572       return rstr2hex(rstr(s, utf8));
16573     };
16574     this.b64 = function (s) {
16575       return rstr2b64(rstr(s, utf8), b64pad);
16576     };
16577     this.any = function (s, e) {
16578       return rstr2any(rstr(s, utf8), e);
16579     };
16580     this.hex_hmac = function (k, d) {
16581       return rstr2hex(rstr_hmac(k, d));
16582     };
16583     this.b64_hmac = function (k, d) {
16584       return rstr2b64(rstr_hmac(k, d), b64pad);
16585     };
16586     this.any_hmac = function (k, d, e) {
16587       return rstr2any(rstr_hmac(k, d), e);
16588     };
16589     /**
16590      * Perform a simple self-test to see if the VM is working
16591      * @return {String} Hexadecimal hash sample
16592      * @public
16593      */
16594     this.vm_test = function () {
16595       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16596     };
16597     /**
16598      * Enable/disable uppercase hexadecimal returned string
16599      * @param {boolean}
16600      * @return {Object} this
16601      * @public
16602      */
16603     this.setUpperCase = function (a) {
16604       if (typeof a === 'boolean') {
16605         hexcase = a;
16606       }
16607       return this;
16608     };
16609     /**
16610      * @description Defines a base64 pad string
16611      * @param {string} Pad
16612      * @return {Object} this
16613      * @public
16614      */
16615     this.setPad = function (a) {
16616       b64pad = a || b64pad;
16617       return this;
16618     };
16619     /**
16620      * Defines a base64 pad string
16621      * @param {boolean}
16622      * @return {Object} this
16623      * @public
16624      */
16625     this.setUTF8 = function (a) {
16626       if (typeof a === 'boolean') {
16627         utf8 = a;
16628       }
16629       return this;
16630     };
16631
16632     // private methods
16633
16634     /**
16635      * Calculate the SHA-512 of a raw string
16636      */
16637     function rstr(s, utf8) {
16638       s = (utf8) ? utf8Encode(s) : s;
16639       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16640     }
16641
16642     /**
16643      * Calculate the HMAC-sha256 of a key and some data (raw strings)
16644      */
16645     function rstr_hmac(key, data) {
16646       key = (utf8) ? utf8Encode(key) : key;
16647       data = (utf8) ? utf8Encode(data) : data;
16648       var hash, i = 0,
16649           bkey = rstr2binb(key),
16650           ipad = Array(16),
16651           opad = Array(16);
16652
16653       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
16654
16655       for (; i < 16; i+=1) {
16656         ipad[i] = bkey[i] ^ 0x36363636;
16657         opad[i] = bkey[i] ^ 0x5C5C5C5C;
16658       }
16659
16660       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
16661       return binb2rstr(binb(opad.concat(hash), 512 + 256));
16662     }
16663
16664     /*
16665      * Main sha256 function, with its support functions
16666      */
16667     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
16668     function sha256_R (X, n) {return ( X >>> n );}
16669     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
16670     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
16671     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
16672     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
16673     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
16674     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
16675     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
16676     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
16677     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
16678     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
16679
16680     sha256_K = [
16681       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
16682       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
16683       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
16684       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
16685       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
16686       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
16687       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
16688       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
16689       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
16690       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
16691       -1866530822, -1538233109, -1090935817, -965641998
16692     ];
16693
16694     function binb(m, l) {
16695       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
16696                  1359893119, -1694144372, 528734635, 1541459225];
16697       var W = new Array(64);
16698       var a, b, c, d, e, f, g, h;
16699       var i, j, T1, T2;
16700
16701       /* append padding */
16702       m[l >> 5] |= 0x80 << (24 - l % 32);
16703       m[((l + 64 >> 9) << 4) + 15] = l;
16704
16705       for (i = 0; i < m.length; i += 16)
16706       {
16707       a = HASH[0];
16708       b = HASH[1];
16709       c = HASH[2];
16710       d = HASH[3];
16711       e = HASH[4];
16712       f = HASH[5];
16713       g = HASH[6];
16714       h = HASH[7];
16715
16716       for (j = 0; j < 64; j+=1)
16717       {
16718         if (j < 16) {
16719           W[j] = m[j + i];
16720         } else {
16721           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
16722                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
16723         }
16724
16725         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
16726                                   sha256_K[j]), W[j]);
16727         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
16728         h = g;
16729         g = f;
16730         f = e;
16731         e = safe_add(d, T1);
16732         d = c;
16733         c = b;
16734         b = a;
16735         a = safe_add(T1, T2);
16736       }
16737
16738       HASH[0] = safe_add(a, HASH[0]);
16739       HASH[1] = safe_add(b, HASH[1]);
16740       HASH[2] = safe_add(c, HASH[2]);
16741       HASH[3] = safe_add(d, HASH[3]);
16742       HASH[4] = safe_add(e, HASH[4]);
16743       HASH[5] = safe_add(f, HASH[5]);
16744       HASH[6] = safe_add(g, HASH[6]);
16745       HASH[7] = safe_add(h, HASH[7]);
16746       }
16747       return HASH;
16748     }
16749
16750   },
16751
16752   /**
16753    * @class Hashes.SHA512
16754    * @param {config}
16755    *
16756    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
16757    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
16758    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16759    * See http://pajhome.org.uk/crypt/md5 for details.
16760    */
16761   SHA512 : function (options) {
16762     /**
16763      * Private properties configuration variables. You may need to tweak these to be compatible with
16764      * the server-side, but the defaults work in most cases.
16765      * @see this.setUpperCase() method
16766      * @see this.setPad() method
16767      */
16768     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
16769         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
16770         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
16771         sha512_k;
16772
16773     /* privileged (public) methods */
16774     this.hex = function (s) {
16775       return rstr2hex(rstr(s));
16776     };
16777     this.b64 = function (s) {
16778       return rstr2b64(rstr(s), b64pad);
16779     };
16780     this.any = function (s, e) {
16781       return rstr2any(rstr(s), e);
16782     };
16783     this.hex_hmac = function (k, d) {
16784       return rstr2hex(rstr_hmac(k, d));
16785     };
16786     this.b64_hmac = function (k, d) {
16787       return rstr2b64(rstr_hmac(k, d), b64pad);
16788     };
16789     this.any_hmac = function (k, d, e) {
16790       return rstr2any(rstr_hmac(k, d), e);
16791     };
16792     /**
16793      * Perform a simple self-test to see if the VM is working
16794      * @return {String} Hexadecimal hash sample
16795      * @public
16796      */
16797     this.vm_test = function () {
16798       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16799     };
16800     /**
16801      * @description Enable/disable uppercase hexadecimal returned string
16802      * @param {boolean}
16803      * @return {Object} this
16804      * @public
16805      */
16806     this.setUpperCase = function (a) {
16807       if (typeof a === 'boolean') {
16808         hexcase = a;
16809       }
16810       return this;
16811     };
16812     /**
16813      * @description Defines a base64 pad string
16814      * @param {string} Pad
16815      * @return {Object} this
16816      * @public
16817      */
16818     this.setPad = function (a) {
16819       b64pad = a || b64pad;
16820       return this;
16821     };
16822     /**
16823      * @description Defines a base64 pad string
16824      * @param {boolean}
16825      * @return {Object} this
16826      * @public
16827      */
16828     this.setUTF8 = function (a) {
16829       if (typeof a === 'boolean') {
16830         utf8 = a;
16831       }
16832       return this;
16833     };
16834
16835     /* private methods */
16836
16837     /**
16838      * Calculate the SHA-512 of a raw string
16839      */
16840     function rstr(s) {
16841       s = (utf8) ? utf8Encode(s) : s;
16842       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16843     }
16844     /*
16845      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
16846      */
16847     function rstr_hmac(key, data) {
16848       key = (utf8) ? utf8Encode(key) : key;
16849       data = (utf8) ? utf8Encode(data) : data;
16850
16851       var hash, i = 0,
16852           bkey = rstr2binb(key),
16853           ipad = Array(32), opad = Array(32);
16854
16855       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
16856
16857       for (; i < 32; i+=1) {
16858         ipad[i] = bkey[i] ^ 0x36363636;
16859         opad[i] = bkey[i] ^ 0x5C5C5C5C;
16860       }
16861
16862       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
16863       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
16864     }
16865
16866     /**
16867      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
16868      */
16869     function binb(x, len) {
16870       var j, i, l,
16871           W = new Array(80),
16872           hash = new Array(16),
16873           //Initial hash values
16874           H = [
16875             new int64(0x6a09e667, -205731576),
16876             new int64(-1150833019, -2067093701),
16877             new int64(0x3c6ef372, -23791573),
16878             new int64(-1521486534, 0x5f1d36f1),
16879             new int64(0x510e527f, -1377402159),
16880             new int64(-1694144372, 0x2b3e6c1f),
16881             new int64(0x1f83d9ab, -79577749),
16882             new int64(0x5be0cd19, 0x137e2179)
16883           ],
16884           T1 = new int64(0, 0),
16885           T2 = new int64(0, 0),
16886           a = new int64(0,0),
16887           b = new int64(0,0),
16888           c = new int64(0,0),
16889           d = new int64(0,0),
16890           e = new int64(0,0),
16891           f = new int64(0,0),
16892           g = new int64(0,0),
16893           h = new int64(0,0),
16894           //Temporary variables not specified by the document
16895           s0 = new int64(0, 0),
16896           s1 = new int64(0, 0),
16897           Ch = new int64(0, 0),
16898           Maj = new int64(0, 0),
16899           r1 = new int64(0, 0),
16900           r2 = new int64(0, 0),
16901           r3 = new int64(0, 0);
16902
16903       if (sha512_k === undefined) {
16904           //SHA512 constants
16905           sha512_k = [
16906             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
16907             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
16908             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
16909             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
16910             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
16911             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
16912             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
16913             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
16914             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
16915             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
16916             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
16917             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
16918             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
16919             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
16920             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
16921             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
16922             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
16923             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
16924             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
16925             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
16926             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
16927             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
16928             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
16929             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
16930             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
16931             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
16932             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
16933             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
16934             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
16935             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
16936             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
16937             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
16938             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
16939             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
16940             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
16941             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
16942             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
16943             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
16944             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
16945             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
16946           ];
16947       }
16948
16949       for (i=0; i<80; i+=1) {
16950         W[i] = new int64(0, 0);
16951       }
16952
16953       // append padding to the source string. The format is described in the FIPS.
16954       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
16955       x[((len + 128 >> 10)<< 5) + 31] = len;
16956       l = x.length;
16957       for (i = 0; i<l; i+=32) { //32 dwords is the block size
16958         int64copy(a, H[0]);
16959         int64copy(b, H[1]);
16960         int64copy(c, H[2]);
16961         int64copy(d, H[3]);
16962         int64copy(e, H[4]);
16963         int64copy(f, H[5]);
16964         int64copy(g, H[6]);
16965         int64copy(h, H[7]);
16966
16967         for (j=0; j<16; j+=1) {
16968           W[j].h = x[i + 2*j];
16969           W[j].l = x[i + 2*j + 1];
16970         }
16971
16972         for (j=16; j<80; j+=1) {
16973           //sigma1
16974           int64rrot(r1, W[j-2], 19);
16975           int64revrrot(r2, W[j-2], 29);
16976           int64shr(r3, W[j-2], 6);
16977           s1.l = r1.l ^ r2.l ^ r3.l;
16978           s1.h = r1.h ^ r2.h ^ r3.h;
16979           //sigma0
16980           int64rrot(r1, W[j-15], 1);
16981           int64rrot(r2, W[j-15], 8);
16982           int64shr(r3, W[j-15], 7);
16983           s0.l = r1.l ^ r2.l ^ r3.l;
16984           s0.h = r1.h ^ r2.h ^ r3.h;
16985
16986           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
16987         }
16988
16989         for (j = 0; j < 80; j+=1) {
16990           //Ch
16991           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
16992           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
16993
16994           //Sigma1
16995           int64rrot(r1, e, 14);
16996           int64rrot(r2, e, 18);
16997           int64revrrot(r3, e, 9);
16998           s1.l = r1.l ^ r2.l ^ r3.l;
16999           s1.h = r1.h ^ r2.h ^ r3.h;
17000
17001           //Sigma0
17002           int64rrot(r1, a, 28);
17003           int64revrrot(r2, a, 2);
17004           int64revrrot(r3, a, 7);
17005           s0.l = r1.l ^ r2.l ^ r3.l;
17006           s0.h = r1.h ^ r2.h ^ r3.h;
17007
17008           //Maj
17009           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
17010           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
17011
17012           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
17013           int64add(T2, s0, Maj);
17014
17015           int64copy(h, g);
17016           int64copy(g, f);
17017           int64copy(f, e);
17018           int64add(e, d, T1);
17019           int64copy(d, c);
17020           int64copy(c, b);
17021           int64copy(b, a);
17022           int64add(a, T1, T2);
17023         }
17024         int64add(H[0], H[0], a);
17025         int64add(H[1], H[1], b);
17026         int64add(H[2], H[2], c);
17027         int64add(H[3], H[3], d);
17028         int64add(H[4], H[4], e);
17029         int64add(H[5], H[5], f);
17030         int64add(H[6], H[6], g);
17031         int64add(H[7], H[7], h);
17032       }
17033
17034       //represent the hash as an array of 32-bit dwords
17035       for (i=0; i<8; i+=1) {
17036         hash[2*i] = H[i].h;
17037         hash[2*i + 1] = H[i].l;
17038       }
17039       return hash;
17040     }
17041
17042     //A constructor for 64-bit numbers
17043     function int64(h, l) {
17044       this.h = h;
17045       this.l = l;
17046       //this.toString = int64toString;
17047     }
17048
17049     //Copies src into dst, assuming both are 64-bit numbers
17050     function int64copy(dst, src) {
17051       dst.h = src.h;
17052       dst.l = src.l;
17053     }
17054
17055     //Right-rotates a 64-bit number by shift
17056     //Won't handle cases of shift>=32
17057     //The function revrrot() is for that
17058     function int64rrot(dst, x, shift) {
17059       dst.l = (x.l >>> shift) | (x.h << (32-shift));
17060       dst.h = (x.h >>> shift) | (x.l << (32-shift));
17061     }
17062
17063     //Reverses the dwords of the source and then rotates right by shift.
17064     //This is equivalent to rotation by 32+shift
17065     function int64revrrot(dst, x, shift) {
17066       dst.l = (x.h >>> shift) | (x.l << (32-shift));
17067       dst.h = (x.l >>> shift) | (x.h << (32-shift));
17068     }
17069
17070     //Bitwise-shifts right a 64-bit number by shift
17071     //Won't handle shift>=32, but it's never needed in SHA512
17072     function int64shr(dst, x, shift) {
17073       dst.l = (x.l >>> shift) | (x.h << (32-shift));
17074       dst.h = (x.h >>> shift);
17075     }
17076
17077     //Adds two 64-bit numbers
17078     //Like the original implementation, does not rely on 32-bit operations
17079     function int64add(dst, x, y) {
17080        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
17081        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
17082        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
17083        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
17084        dst.l = (w0 & 0xffff) | (w1 << 16);
17085        dst.h = (w2 & 0xffff) | (w3 << 16);
17086     }
17087
17088     //Same, except with 4 addends. Works faster than adding them one by one.
17089     function int64add4(dst, a, b, c, d) {
17090        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
17091        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
17092        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
17093        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
17094        dst.l = (w0 & 0xffff) | (w1 << 16);
17095        dst.h = (w2 & 0xffff) | (w3 << 16);
17096     }
17097
17098     //Same, except with 5 addends
17099     function int64add5(dst, a, b, c, d, e) {
17100       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
17101           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
17102           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
17103           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
17104        dst.l = (w0 & 0xffff) | (w1 << 16);
17105        dst.h = (w2 & 0xffff) | (w3 << 16);
17106     }
17107   },
17108   /**
17109    * @class Hashes.RMD160
17110    * @constructor
17111    * @param {Object} [config]
17112    *
17113    * A JavaScript implementation of the RIPEMD-160 Algorithm
17114    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
17115    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
17116    * See http://pajhome.org.uk/crypt/md5 for details.
17117    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
17118    */
17119   RMD160 : function (options) {
17120     /**
17121      * Private properties configuration variables. You may need to tweak these to be compatible with
17122      * the server-side, but the defaults work in most cases.
17123      * @see this.setUpperCase() method
17124      * @see this.setPad() method
17125      */
17126     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
17127         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
17128         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
17129         rmd160_r1 = [
17130            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
17131            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
17132            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
17133            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
17134            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
17135         ],
17136         rmd160_r2 = [
17137            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
17138            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
17139           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
17140            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
17141           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
17142         ],
17143         rmd160_s1 = [
17144           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
17145            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
17146           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
17147           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
17148            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
17149         ],
17150         rmd160_s2 = [
17151            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
17152            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
17153            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
17154           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
17155            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
17156         ];
17157
17158     /* privileged (public) methods */
17159     this.hex = function (s) {
17160       return rstr2hex(rstr(s, utf8));
17161     };
17162     this.b64 = function (s) {
17163       return rstr2b64(rstr(s, utf8), b64pad);
17164     };
17165     this.any = function (s, e) {
17166       return rstr2any(rstr(s, utf8), e);
17167     };
17168     this.hex_hmac = function (k, d) {
17169       return rstr2hex(rstr_hmac(k, d));
17170     };
17171     this.b64_hmac = function (k, d) {
17172       return rstr2b64(rstr_hmac(k, d), b64pad);
17173     };
17174     this.any_hmac = function (k, d, e) {
17175       return rstr2any(rstr_hmac(k, d), e);
17176     };
17177     /**
17178      * Perform a simple self-test to see if the VM is working
17179      * @return {String} Hexadecimal hash sample
17180      * @public
17181      */
17182     this.vm_test = function () {
17183       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
17184     };
17185     /**
17186      * @description Enable/disable uppercase hexadecimal returned string
17187      * @param {boolean}
17188      * @return {Object} this
17189      * @public
17190      */
17191     this.setUpperCase = function (a) {
17192       if (typeof a === 'boolean' ) { hexcase = a; }
17193       return this;
17194     };
17195     /**
17196      * @description Defines a base64 pad string
17197      * @param {string} Pad
17198      * @return {Object} this
17199      * @public
17200      */
17201     this.setPad = function (a) {
17202       if (typeof a !== 'undefined' ) { b64pad = a; }
17203       return this;
17204     };
17205     /**
17206      * @description Defines a base64 pad string
17207      * @param {boolean}
17208      * @return {Object} this
17209      * @public
17210      */
17211     this.setUTF8 = function (a) {
17212       if (typeof a === 'boolean') { utf8 = a; }
17213       return this;
17214     };
17215
17216     /* private methods */
17217
17218     /**
17219      * Calculate the rmd160 of a raw string
17220      */
17221     function rstr(s) {
17222       s = (utf8) ? utf8Encode(s) : s;
17223       return binl2rstr(binl(rstr2binl(s), s.length * 8));
17224     }
17225
17226     /**
17227      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
17228      */
17229     function rstr_hmac(key, data) {
17230       key = (utf8) ? utf8Encode(key) : key;
17231       data = (utf8) ? utf8Encode(data) : data;
17232       var i, hash,
17233           bkey = rstr2binl(key),
17234           ipad = Array(16), opad = Array(16);
17235
17236       if (bkey.length > 16) {
17237         bkey = binl(bkey, key.length * 8);
17238       }
17239
17240       for (i = 0; i < 16; i+=1) {
17241         ipad[i] = bkey[i] ^ 0x36363636;
17242         opad[i] = bkey[i] ^ 0x5C5C5C5C;
17243       }
17244       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
17245       return binl2rstr(binl(opad.concat(hash), 512 + 160));
17246     }
17247
17248     /**
17249      * Convert an array of little-endian words to a string
17250      */
17251     function binl2rstr(input) {
17252       var i, output = '', l = input.length * 32;
17253       for (i = 0; i < l; i += 8) {
17254         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
17255       }
17256       return output;
17257     }
17258
17259     /**
17260      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
17261      */
17262     function binl(x, len) {
17263       var T, j, i, l,
17264           h0 = 0x67452301,
17265           h1 = 0xefcdab89,
17266           h2 = 0x98badcfe,
17267           h3 = 0x10325476,
17268           h4 = 0xc3d2e1f0,
17269           A1, B1, C1, D1, E1,
17270           A2, B2, C2, D2, E2;
17271
17272       /* append padding */
17273       x[len >> 5] |= 0x80 << (len % 32);
17274       x[(((len + 64) >>> 9) << 4) + 14] = len;
17275       l = x.length;
17276
17277       for (i = 0; i < l; i+=16) {
17278         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
17279         for (j = 0; j <= 79; j+=1) {
17280           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
17281           T = safe_add(T, x[i + rmd160_r1[j]]);
17282           T = safe_add(T, rmd160_K1(j));
17283           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
17284           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
17285           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
17286           T = safe_add(T, x[i + rmd160_r2[j]]);
17287           T = safe_add(T, rmd160_K2(j));
17288           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
17289           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
17290         }
17291
17292         T = safe_add(h1, safe_add(C1, D2));
17293         h1 = safe_add(h2, safe_add(D1, E2));
17294         h2 = safe_add(h3, safe_add(E1, A2));
17295         h3 = safe_add(h4, safe_add(A1, B2));
17296         h4 = safe_add(h0, safe_add(B1, C2));
17297         h0 = T;
17298       }
17299       return [h0, h1, h2, h3, h4];
17300     }
17301
17302     // specific algorithm methods
17303     function rmd160_f(j, x, y, z) {
17304       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
17305          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
17306          (32 <= j && j <= 47) ? (x | ~y) ^ z :
17307          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
17308          (64 <= j && j <= 79) ? x ^ (y | ~z) :
17309          'rmd160_f: j out of range';
17310     }
17311
17312     function rmd160_K1(j) {
17313       return ( 0 <= j && j <= 15) ? 0x00000000 :
17314          (16 <= j && j <= 31) ? 0x5a827999 :
17315          (32 <= j && j <= 47) ? 0x6ed9eba1 :
17316          (48 <= j && j <= 63) ? 0x8f1bbcdc :
17317          (64 <= j && j <= 79) ? 0xa953fd4e :
17318          'rmd160_K1: j out of range';
17319     }
17320
17321     function rmd160_K2(j){
17322       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
17323          (16 <= j && j <= 31) ? 0x5c4dd124 :
17324          (32 <= j && j <= 47) ? 0x6d703ef3 :
17325          (48 <= j && j <= 63) ? 0x7a6d76e9 :
17326          (64 <= j && j <= 79) ? 0x00000000 :
17327          'rmd160_K2: j out of range';
17328     }
17329   }
17330 };
17331
17332   // exposes Hashes
17333   (function( window, undefined ) {
17334     var freeExports = false;
17335     if (typeof exports === 'object' ) {
17336       freeExports = exports;
17337       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
17338     }
17339
17340     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
17341       // define as an anonymous module, so, through path mapping, it can be aliased
17342       define(function () { return Hashes; });
17343     }
17344     else if ( freeExports ) {
17345       // in Node.js or RingoJS v0.8.0+
17346       if ( typeof module === 'object' && module && module.exports === freeExports ) {
17347         module.exports = Hashes;
17348       }
17349       // in Narwhal or RingoJS v0.7.0-
17350       else {
17351         freeExports.Hashes = Hashes;
17352       }
17353     }
17354     else {
17355       // in a browser or Rhino
17356       window.Hashes = Hashes;
17357     }
17358   }( this ));
17359 }()); // IIFE
17360
17361 })(window)
17362 },{}],2:[function(require,module,exports){
17363 'use strict';
17364
17365 var hashes = require('jshashes'),
17366     xtend = require('xtend'),
17367     sha1 = new hashes.SHA1();
17368
17369 var ohauth = {};
17370
17371 ohauth.qsString = function(obj) {
17372     return Object.keys(obj).sort().map(function(key) {
17373         return ohauth.percentEncode(key) + '=' +
17374             ohauth.percentEncode(obj[key]);
17375     }).join('&');
17376 };
17377
17378 ohauth.stringQs = function(str) {
17379     return str.split('&').reduce(function(obj, pair){
17380         var parts = pair.split('=');
17381         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
17382             '' : decodeURIComponent(parts[1]);
17383         return obj;
17384     }, {});
17385 };
17386
17387 ohauth.rawxhr = function(method, url, data, headers, callback) {
17388     var xhr = new XMLHttpRequest(),
17389         twoHundred = /^20\d$/;
17390     xhr.onreadystatechange = function() {
17391         if (4 == xhr.readyState && 0 !== xhr.status) {
17392             if (twoHundred.test(xhr.status)) callback(null, xhr);
17393             else return callback(xhr, null);
17394         }
17395     };
17396     xhr.onerror = function(e) { return callback(e, null); };
17397     xhr.open(method, url, true);
17398     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
17399     xhr.send(data);
17400 };
17401
17402 ohauth.xhr = function(method, url, auth, data, options, callback) {
17403     var headers = (options && options.header) || {
17404         'Content-Type': 'application/x-www-form-urlencoded'
17405     };
17406     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
17407     ohauth.rawxhr(method, url, data, headers, callback);
17408 };
17409
17410 ohauth.nonce = function() {
17411     for (var o = ''; o.length < 6;) {
17412         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
17413     }
17414     return o;
17415 };
17416
17417 ohauth.authHeader = function(obj) {
17418     return Object.keys(obj).sort().map(function(key) {
17419         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
17420     }).join(', ');
17421 };
17422
17423 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
17424
17425 ohauth.percentEncode = function(s) {
17426     return encodeURIComponent(s)
17427         .replace(/\!/g, '%21').replace(/\'/g, '%27')
17428         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
17429 };
17430
17431 ohauth.baseString = function(method, url, params) {
17432     if (params.oauth_signature) delete params.oauth_signature;
17433     return [
17434         method,
17435         ohauth.percentEncode(url),
17436         ohauth.percentEncode(ohauth.qsString(params))].join('&');
17437 };
17438
17439 ohauth.signature = function(oauth_secret, token_secret, baseString) {
17440     return sha1.b64_hmac(
17441         ohauth.percentEncode(oauth_secret) + '&' +
17442         ohauth.percentEncode(token_secret),
17443         baseString);
17444 };
17445
17446 /**
17447  * Takes an options object for configuration (consumer_key,
17448  * consumer_secret, version, signature_method, token) and returns a
17449  * function that generates the Authorization header for given data.
17450  *
17451  * The returned function takes these parameters:
17452  * - method: GET/POST/...
17453  * - uri: full URI with protocol, port, path and query string
17454  * - extra_params: any extra parameters (that are passed in the POST data),
17455  *   can be an object or a from-urlencoded string.
17456  *
17457  * Returned function returns full OAuth header with "OAuth" string in it.
17458  */
17459
17460 ohauth.headerGenerator = function(options) {
17461     options = options || {};
17462     var consumer_key = options.consumer_key || '',
17463         consumer_secret = options.consumer_secret || '',
17464         signature_method = options.signature_method || 'HMAC-SHA1',
17465         version = options.version || '1.0',
17466         token = options.token || '';
17467
17468     return function(method, uri, extra_params) {
17469         method = method.toUpperCase();
17470         if (typeof extra_params === 'string' && extra_params.length > 0) {
17471             extra_params = ohauth.stringQs(extra_params);
17472         }
17473
17474         var uri_parts = uri.split('?', 2),
17475         base_uri = uri_parts[0];
17476
17477         var query_params = uri_parts.length === 2 ?
17478             ohauth.stringQs(uri_parts[1]) : {};
17479
17480         var oauth_params = {
17481             oauth_consumer_key: consumer_key,
17482             oauth_signature_method: signature_method,
17483             oauth_version: version,
17484             oauth_timestamp: ohauth.timestamp(),
17485             oauth_nonce: ohauth.nonce()
17486         };
17487
17488         if (token) oauth_params.oauth_token = token;
17489
17490         var all_params = xtend({}, oauth_params, query_params, extra_params),
17491             base_str = ohauth.baseString(method, base_uri, all_params);
17492
17493         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
17494
17495         return 'OAuth ' + ohauth.authHeader(oauth_params);
17496     };
17497 };
17498
17499 module.exports = ohauth;
17500
17501 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
17502 module.exports = Object.keys || require('./shim');
17503
17504
17505 },{"./shim":8}],8:[function(require,module,exports){
17506 (function () {
17507         "use strict";
17508
17509         // modified from https://github.com/kriskowal/es5-shim
17510         var has = Object.prototype.hasOwnProperty,
17511                 is = require('is'),
17512                 forEach = require('foreach'),
17513                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
17514                 dontEnums = [
17515                         "toString",
17516                         "toLocaleString",
17517                         "valueOf",
17518                         "hasOwnProperty",
17519                         "isPrototypeOf",
17520                         "propertyIsEnumerable",
17521                         "constructor"
17522                 ],
17523                 keysShim;
17524
17525         keysShim = function keys(object) {
17526                 if (!is.object(object) && !is.array(object)) {
17527                         throw new TypeError("Object.keys called on a non-object");
17528                 }
17529
17530                 var name, theKeys = [];
17531                 for (name in object) {
17532                         if (has.call(object, name)) {
17533                                 theKeys.push(name);
17534                         }
17535                 }
17536
17537                 if (hasDontEnumBug) {
17538                         forEach(dontEnums, function (dontEnum) {
17539                                 if (has.call(object, dontEnum)) {
17540                                         theKeys.push(dontEnum);
17541                                 }
17542                         });
17543                 }
17544                 return theKeys;
17545         };
17546
17547         module.exports = keysShim;
17548 }());
17549
17550
17551 },{"is":9,"foreach":10}],9:[function(require,module,exports){
17552
17553 /**!
17554  * is
17555  * the definitive JavaScript type testing library
17556  *
17557  * @copyright 2013 Enrico Marino
17558  * @license MIT
17559  */
17560
17561 var objProto = Object.prototype;
17562 var owns = objProto.hasOwnProperty;
17563 var toString = objProto.toString;
17564 var isActualNaN = function (value) {
17565   return value !== value;
17566 };
17567 var NON_HOST_TYPES = {
17568   "boolean": 1,
17569   "number": 1,
17570   "string": 1,
17571   "undefined": 1
17572 };
17573
17574 /**
17575  * Expose `is`
17576  */
17577
17578 var is = module.exports = {};
17579
17580 /**
17581  * Test general.
17582  */
17583
17584 /**
17585  * is.type
17586  * Test if `value` is a type of `type`.
17587  *
17588  * @param {Mixed} value value to test
17589  * @param {String} type type
17590  * @return {Boolean} true if `value` is a type of `type`, false otherwise
17591  * @api public
17592  */
17593
17594 is.a =
17595 is.type = function (value, type) {
17596   return typeof value === type;
17597 };
17598
17599 /**
17600  * is.defined
17601  * Test if `value` is defined.
17602  *
17603  * @param {Mixed} value value to test
17604  * @return {Boolean} true if 'value' is defined, false otherwise
17605  * @api public
17606  */
17607
17608 is.defined = function (value) {
17609   return value !== undefined;
17610 };
17611
17612 /**
17613  * is.empty
17614  * Test if `value` is empty.
17615  *
17616  * @param {Mixed} value value to test
17617  * @return {Boolean} true if `value` is empty, false otherwise
17618  * @api public
17619  */
17620
17621 is.empty = function (value) {
17622   var type = toString.call(value);
17623   var key;
17624
17625   if ('[object Array]' === type || '[object Arguments]' === type) {
17626     return value.length === 0;
17627   }
17628
17629   if ('[object Object]' === type) {
17630     for (key in value) if (owns.call(value, key)) return false;
17631     return true;
17632   }
17633
17634   if ('[object String]' === type) {
17635     return '' === value;
17636   }
17637
17638   return false;
17639 };
17640
17641 /**
17642  * is.equal
17643  * Test if `value` is equal to `other`.
17644  *
17645  * @param {Mixed} value value to test
17646  * @param {Mixed} other value to compare with
17647  * @return {Boolean} true if `value` is equal to `other`, false otherwise
17648  */
17649
17650 is.equal = function (value, other) {
17651   var type = toString.call(value)
17652   var key;
17653
17654   if (type !== toString.call(other)) {
17655     return false;
17656   }
17657
17658   if ('[object Object]' === type) {
17659     for (key in value) {
17660       if (!is.equal(value[key], other[key])) {
17661         return false;
17662       }
17663     }
17664     return true;
17665   }
17666
17667   if ('[object Array]' === type) {
17668     key = value.length;
17669     if (key !== other.length) {
17670       return false;
17671     }
17672     while (--key) {
17673       if (!is.equal(value[key], other[key])) {
17674         return false;
17675       }
17676     }
17677     return true;
17678   }
17679
17680   if ('[object Function]' === type) {
17681     return value.prototype === other.prototype;
17682   }
17683
17684   if ('[object Date]' === type) {
17685     return value.getTime() === other.getTime();
17686   }
17687
17688   return value === other;
17689 };
17690
17691 /**
17692  * is.hosted
17693  * Test if `value` is hosted by `host`.
17694  *
17695  * @param {Mixed} value to test
17696  * @param {Mixed} host host to test with
17697  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
17698  * @api public
17699  */
17700
17701 is.hosted = function (value, host) {
17702   var type = typeof host[value];
17703   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
17704 };
17705
17706 /**
17707  * is.instance
17708  * Test if `value` is an instance of `constructor`.
17709  *
17710  * @param {Mixed} value value to test
17711  * @return {Boolean} true if `value` is an instance of `constructor`
17712  * @api public
17713  */
17714
17715 is.instance = is['instanceof'] = function (value, constructor) {
17716   return value instanceof constructor;
17717 };
17718
17719 /**
17720  * is.null
17721  * Test if `value` is null.
17722  *
17723  * @param {Mixed} value value to test
17724  * @return {Boolean} true if `value` is null, false otherwise
17725  * @api public
17726  */
17727
17728 is['null'] = function (value) {
17729   return value === null;
17730 };
17731
17732 /**
17733  * is.undefined
17734  * Test if `value` is undefined.
17735  *
17736  * @param {Mixed} value value to test
17737  * @return {Boolean} true if `value` is undefined, false otherwise
17738  * @api public
17739  */
17740
17741 is.undefined = function (value) {
17742   return value === undefined;
17743 };
17744
17745 /**
17746  * Test arguments.
17747  */
17748
17749 /**
17750  * is.arguments
17751  * Test if `value` is an arguments object.
17752  *
17753  * @param {Mixed} value value to test
17754  * @return {Boolean} true if `value` is an arguments object, false otherwise
17755  * @api public
17756  */
17757
17758 is.arguments = function (value) {
17759   var isStandardArguments = '[object Arguments]' === toString.call(value);
17760   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
17761   return isStandardArguments || isOldArguments;
17762 };
17763
17764 /**
17765  * Test array.
17766  */
17767
17768 /**
17769  * is.array
17770  * Test if 'value' is an array.
17771  *
17772  * @param {Mixed} value value to test
17773  * @return {Boolean} true if `value` is an array, false otherwise
17774  * @api public
17775  */
17776
17777 is.array = function (value) {
17778   return '[object Array]' === toString.call(value);
17779 };
17780
17781 /**
17782  * is.arguments.empty
17783  * Test if `value` is an empty arguments object.
17784  *
17785  * @param {Mixed} value value to test
17786  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
17787  * @api public
17788  */
17789 is.arguments.empty = function (value) {
17790   return is.arguments(value) && value.length === 0;
17791 };
17792
17793 /**
17794  * is.array.empty
17795  * Test if `value` is an empty array.
17796  *
17797  * @param {Mixed} value value to test
17798  * @return {Boolean} true if `value` is an empty array, false otherwise
17799  * @api public
17800  */
17801 is.array.empty = function (value) {
17802   return is.array(value) && value.length === 0;
17803 };
17804
17805 /**
17806  * is.arraylike
17807  * Test if `value` is an arraylike object.
17808  *
17809  * @param {Mixed} value value to test
17810  * @return {Boolean} true if `value` is an arguments object, false otherwise
17811  * @api public
17812  */
17813
17814 is.arraylike = function (value) {
17815   return !!value && !is.boolean(value)
17816     && owns.call(value, 'length')
17817     && isFinite(value.length)
17818     && is.number(value.length)
17819     && value.length >= 0;
17820 };
17821
17822 /**
17823  * Test boolean.
17824  */
17825
17826 /**
17827  * is.boolean
17828  * Test if `value` is a boolean.
17829  *
17830  * @param {Mixed} value value to test
17831  * @return {Boolean} true if `value` is a boolean, false otherwise
17832  * @api public
17833  */
17834
17835 is.boolean = function (value) {
17836   return '[object Boolean]' === toString.call(value);
17837 };
17838
17839 /**
17840  * is.false
17841  * Test if `value` is false.
17842  *
17843  * @param {Mixed} value value to test
17844  * @return {Boolean} true if `value` is false, false otherwise
17845  * @api public
17846  */
17847
17848 is['false'] = function (value) {
17849   return is.boolean(value) && (value === false || value.valueOf() === false);
17850 };
17851
17852 /**
17853  * is.true
17854  * Test if `value` is true.
17855  *
17856  * @param {Mixed} value value to test
17857  * @return {Boolean} true if `value` is true, false otherwise
17858  * @api public
17859  */
17860
17861 is['true'] = function (value) {
17862   return is.boolean(value) && (value === true || value.valueOf() === true);
17863 };
17864
17865 /**
17866  * Test date.
17867  */
17868
17869 /**
17870  * is.date
17871  * Test if `value` is a date.
17872  *
17873  * @param {Mixed} value value to test
17874  * @return {Boolean} true if `value` is a date, false otherwise
17875  * @api public
17876  */
17877
17878 is.date = function (value) {
17879   return '[object Date]' === toString.call(value);
17880 };
17881
17882 /**
17883  * Test element.
17884  */
17885
17886 /**
17887  * is.element
17888  * Test if `value` is an html element.
17889  *
17890  * @param {Mixed} value value to test
17891  * @return {Boolean} true if `value` is an HTML Element, false otherwise
17892  * @api public
17893  */
17894
17895 is.element = function (value) {
17896   return value !== undefined
17897     && typeof HTMLElement !== 'undefined'
17898     && value instanceof HTMLElement
17899     && value.nodeType === 1;
17900 };
17901
17902 /**
17903  * Test error.
17904  */
17905
17906 /**
17907  * is.error
17908  * Test if `value` is an error object.
17909  *
17910  * @param {Mixed} value value to test
17911  * @return {Boolean} true if `value` is an error object, false otherwise
17912  * @api public
17913  */
17914
17915 is.error = function (value) {
17916   return '[object Error]' === toString.call(value);
17917 };
17918
17919 /**
17920  * Test function.
17921  */
17922
17923 /**
17924  * is.fn / is.function (deprecated)
17925  * Test if `value` is a function.
17926  *
17927  * @param {Mixed} value value to test
17928  * @return {Boolean} true if `value` is a function, false otherwise
17929  * @api public
17930  */
17931
17932 is.fn = is['function'] = function (value) {
17933   var isAlert = typeof window !== 'undefined' && value === window.alert;
17934   return isAlert || '[object Function]' === toString.call(value);
17935 };
17936
17937 /**
17938  * Test number.
17939  */
17940
17941 /**
17942  * is.number
17943  * Test if `value` is a number.
17944  *
17945  * @param {Mixed} value value to test
17946  * @return {Boolean} true if `value` is a number, false otherwise
17947  * @api public
17948  */
17949
17950 is.number = function (value) {
17951   return '[object Number]' === toString.call(value);
17952 };
17953
17954 /**
17955  * is.infinite
17956  * Test if `value` is positive or negative infinity.
17957  *
17958  * @param {Mixed} value value to test
17959  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
17960  * @api public
17961  */
17962 is.infinite = function (value) {
17963   return value === Infinity || value === -Infinity;
17964 };
17965
17966 /**
17967  * is.decimal
17968  * Test if `value` is a decimal number.
17969  *
17970  * @param {Mixed} value value to test
17971  * @return {Boolean} true if `value` is a decimal number, false otherwise
17972  * @api public
17973  */
17974
17975 is.decimal = function (value) {
17976   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
17977 };
17978
17979 /**
17980  * is.divisibleBy
17981  * Test if `value` is divisible by `n`.
17982  *
17983  * @param {Number} value value to test
17984  * @param {Number} n dividend
17985  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
17986  * @api public
17987  */
17988
17989 is.divisibleBy = function (value, n) {
17990   var isDividendInfinite = is.infinite(value);
17991   var isDivisorInfinite = is.infinite(n);
17992   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
17993   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
17994 };
17995
17996 /**
17997  * is.int
17998  * Test if `value` is an integer.
17999  *
18000  * @param value to test
18001  * @return {Boolean} true if `value` is an integer, false otherwise
18002  * @api public
18003  */
18004
18005 is.int = function (value) {
18006   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
18007 };
18008
18009 /**
18010  * is.maximum
18011  * Test if `value` is greater than 'others' values.
18012  *
18013  * @param {Number} value value to test
18014  * @param {Array} others values to compare with
18015  * @return {Boolean} true if `value` is greater than `others` values
18016  * @api public
18017  */
18018
18019 is.maximum = function (value, others) {
18020   if (isActualNaN(value)) {
18021     throw new TypeError('NaN is not a valid value');
18022   } else if (!is.arraylike(others)) {
18023     throw new TypeError('second argument must be array-like');
18024   }
18025   var len = others.length;
18026
18027   while (--len >= 0) {
18028     if (value < others[len]) {
18029       return false;
18030     }
18031   }
18032
18033   return true;
18034 };
18035
18036 /**
18037  * is.minimum
18038  * Test if `value` is less than `others` values.
18039  *
18040  * @param {Number} value value to test
18041  * @param {Array} others values to compare with
18042  * @return {Boolean} true if `value` is less than `others` values
18043  * @api public
18044  */
18045
18046 is.minimum = function (value, others) {
18047   if (isActualNaN(value)) {
18048     throw new TypeError('NaN is not a valid value');
18049   } else if (!is.arraylike(others)) {
18050     throw new TypeError('second argument must be array-like');
18051   }
18052   var len = others.length;
18053
18054   while (--len >= 0) {
18055     if (value > others[len]) {
18056       return false;
18057     }
18058   }
18059
18060   return true;
18061 };
18062
18063 /**
18064  * is.nan
18065  * Test if `value` is not a number.
18066  *
18067  * @param {Mixed} value value to test
18068  * @return {Boolean} true if `value` is not a number, false otherwise
18069  * @api public
18070  */
18071
18072 is.nan = function (value) {
18073   return !is.number(value) || value !== value;
18074 };
18075
18076 /**
18077  * is.even
18078  * Test if `value` is an even number.
18079  *
18080  * @param {Number} value value to test
18081  * @return {Boolean} true if `value` is an even number, false otherwise
18082  * @api public
18083  */
18084
18085 is.even = function (value) {
18086   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
18087 };
18088
18089 /**
18090  * is.odd
18091  * Test if `value` is an odd number.
18092  *
18093  * @param {Number} value value to test
18094  * @return {Boolean} true if `value` is an odd number, false otherwise
18095  * @api public
18096  */
18097
18098 is.odd = function (value) {
18099   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
18100 };
18101
18102 /**
18103  * is.ge
18104  * Test if `value` is greater than or equal to `other`.
18105  *
18106  * @param {Number} value value to test
18107  * @param {Number} other value to compare with
18108  * @return {Boolean}
18109  * @api public
18110  */
18111
18112 is.ge = function (value, other) {
18113   if (isActualNaN(value) || isActualNaN(other)) {
18114     throw new TypeError('NaN is not a valid value');
18115   }
18116   return !is.infinite(value) && !is.infinite(other) && value >= other;
18117 };
18118
18119 /**
18120  * is.gt
18121  * Test if `value` is greater than `other`.
18122  *
18123  * @param {Number} value value to test
18124  * @param {Number} other value to compare with
18125  * @return {Boolean}
18126  * @api public
18127  */
18128
18129 is.gt = function (value, other) {
18130   if (isActualNaN(value) || isActualNaN(other)) {
18131     throw new TypeError('NaN is not a valid value');
18132   }
18133   return !is.infinite(value) && !is.infinite(other) && value > other;
18134 };
18135
18136 /**
18137  * is.le
18138  * Test if `value` is less than or equal to `other`.
18139  *
18140  * @param {Number} value value to test
18141  * @param {Number} other value to compare with
18142  * @return {Boolean} if 'value' is less than or equal to 'other'
18143  * @api public
18144  */
18145
18146 is.le = function (value, other) {
18147   if (isActualNaN(value) || isActualNaN(other)) {
18148     throw new TypeError('NaN is not a valid value');
18149   }
18150   return !is.infinite(value) && !is.infinite(other) && value <= other;
18151 };
18152
18153 /**
18154  * is.lt
18155  * Test if `value` is less than `other`.
18156  *
18157  * @param {Number} value value to test
18158  * @param {Number} other value to compare with
18159  * @return {Boolean} if `value` is less than `other`
18160  * @api public
18161  */
18162
18163 is.lt = function (value, other) {
18164   if (isActualNaN(value) || isActualNaN(other)) {
18165     throw new TypeError('NaN is not a valid value');
18166   }
18167   return !is.infinite(value) && !is.infinite(other) && value < other;
18168 };
18169
18170 /**
18171  * is.within
18172  * Test if `value` is within `start` and `finish`.
18173  *
18174  * @param {Number} value value to test
18175  * @param {Number} start lower bound
18176  * @param {Number} finish upper bound
18177  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
18178  * @api public
18179  */
18180 is.within = function (value, start, finish) {
18181   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
18182     throw new TypeError('NaN is not a valid value');
18183   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
18184     throw new TypeError('all arguments must be numbers');
18185   }
18186   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
18187   return isAnyInfinite || (value >= start && value <= finish);
18188 };
18189
18190 /**
18191  * Test object.
18192  */
18193
18194 /**
18195  * is.object
18196  * Test if `value` is an object.
18197  *
18198  * @param {Mixed} value value to test
18199  * @return {Boolean} true if `value` is an object, false otherwise
18200  * @api public
18201  */
18202
18203 is.object = function (value) {
18204   return value && '[object Object]' === toString.call(value);
18205 };
18206
18207 /**
18208  * is.hash
18209  * Test if `value` is a hash - a plain object literal.
18210  *
18211  * @param {Mixed} value value to test
18212  * @return {Boolean} true if `value` is a hash, false otherwise
18213  * @api public
18214  */
18215
18216 is.hash = function (value) {
18217   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
18218 };
18219
18220 /**
18221  * Test regexp.
18222  */
18223
18224 /**
18225  * is.regexp
18226  * Test if `value` is a regular expression.
18227  *
18228  * @param {Mixed} value value to test
18229  * @return {Boolean} true if `value` is a regexp, false otherwise
18230  * @api public
18231  */
18232
18233 is.regexp = function (value) {
18234   return '[object RegExp]' === toString.call(value);
18235 };
18236
18237 /**
18238  * Test string.
18239  */
18240
18241 /**
18242  * is.string
18243  * Test if `value` is a string.
18244  *
18245  * @param {Mixed} value value to test
18246  * @return {Boolean} true if 'value' is a string, false otherwise
18247  * @api public
18248  */
18249
18250 is.string = function (value) {
18251   return '[object String]' === toString.call(value);
18252 };
18253
18254
18255 },{}],10:[function(require,module,exports){
18256
18257 var hasOwn = Object.prototype.hasOwnProperty;
18258 var toString = Object.prototype.toString;
18259
18260 module.exports = function forEach (obj, fn, ctx) {
18261     if (toString.call(fn) !== '[object Function]') {
18262         throw new TypeError('iterator must be a function');
18263     }
18264     var l = obj.length;
18265     if (l === +l) {
18266         for (var i = 0; i < l; i++) {
18267             fn.call(ctx, obj[i], i, obj);
18268         }
18269     } else {
18270         for (var k in obj) {
18271             if (hasOwn.call(obj, k)) {
18272                 fn.call(ctx, obj[k], k, obj);
18273             }
18274         }
18275     }
18276 };
18277
18278
18279 },{}]},{},[1])(1)
18280 });
18281 ;
18282 /*
18283  (c) 2015, Vladimir Agafonkin
18284  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
18285  https://github.com/mourner/rbush
18286 */
18287
18288 (function () {
18289 'use strict';
18290
18291 function rbush(maxEntries, format) {
18292
18293     // jshint newcap: false, validthis: true
18294     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
18295
18296     // max entries in a node is 9 by default; min node fill is 40% for best performance
18297     this._maxEntries = Math.max(4, maxEntries || 9);
18298     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
18299
18300     if (format) {
18301         this._initFormat(format);
18302     }
18303
18304     this.clear();
18305 }
18306
18307 rbush.prototype = {
18308
18309     all: function () {
18310         return this._all(this.data, []);
18311     },
18312
18313     search: function (bbox) {
18314
18315         var node = this.data,
18316             result = [],
18317             toBBox = this.toBBox;
18318
18319         if (!intersects(bbox, node.bbox)) return result;
18320
18321         var nodesToSearch = [],
18322             i, len, child, childBBox;
18323
18324         while (node) {
18325             for (i = 0, len = node.children.length; i < len; i++) {
18326
18327                 child = node.children[i];
18328                 childBBox = node.leaf ? toBBox(child) : child.bbox;
18329
18330                 if (intersects(bbox, childBBox)) {
18331                     if (node.leaf) result.push(child);
18332                     else if (contains(bbox, childBBox)) this._all(child, result);
18333                     else nodesToSearch.push(child);
18334                 }
18335             }
18336             node = nodesToSearch.pop();
18337         }
18338
18339         return result;
18340     },
18341
18342     collides: function (bbox) {
18343
18344         var node = this.data,
18345             toBBox = this.toBBox;
18346
18347         if (!intersects(bbox, node.bbox)) return false;
18348
18349         var nodesToSearch = [],
18350             i, len, child, childBBox;
18351
18352         while (node) {
18353             for (i = 0, len = node.children.length; i < len; i++) {
18354
18355                 child = node.children[i];
18356                 childBBox = node.leaf ? toBBox(child) : child.bbox;
18357
18358                 if (intersects(bbox, childBBox)) {
18359                     if (node.leaf || contains(bbox, childBBox)) return true;
18360                     nodesToSearch.push(child);
18361                 }
18362             }
18363             node = nodesToSearch.pop();
18364         }
18365
18366         return false;
18367     },
18368
18369     load: function (data) {
18370         if (!(data && data.length)) return this;
18371
18372         if (data.length < this._minEntries) {
18373             for (var i = 0, len = data.length; i < len; i++) {
18374                 this.insert(data[i]);
18375             }
18376             return this;
18377         }
18378
18379         // recursively build the tree with the given data from stratch using OMT algorithm
18380         var node = this._build(data.slice(), 0, data.length - 1, 0);
18381
18382         if (!this.data.children.length) {
18383             // save as is if tree is empty
18384             this.data = node;
18385
18386         } else if (this.data.height === node.height) {
18387             // split root if trees have the same height
18388             this._splitRoot(this.data, node);
18389
18390         } else {
18391             if (this.data.height < node.height) {
18392                 // swap trees if inserted one is bigger
18393                 var tmpNode = this.data;
18394                 this.data = node;
18395                 node = tmpNode;
18396             }
18397
18398             // insert the small tree into the large tree at appropriate level
18399             this._insert(node, this.data.height - node.height - 1, true);
18400         }
18401
18402         return this;
18403     },
18404
18405     insert: function (item) {
18406         if (item) this._insert(item, this.data.height - 1);
18407         return this;
18408     },
18409
18410     clear: function () {
18411         this.data = {
18412             children: [],
18413             height: 1,
18414             bbox: empty(),
18415             leaf: true
18416         };
18417         return this;
18418     },
18419
18420     remove: function (item) {
18421         if (!item) return this;
18422
18423         var node = this.data,
18424             bbox = this.toBBox(item),
18425             path = [],
18426             indexes = [],
18427             i, parent, index, goingUp;
18428
18429         // depth-first iterative tree traversal
18430         while (node || path.length) {
18431
18432             if (!node) { // go up
18433                 node = path.pop();
18434                 parent = path[path.length - 1];
18435                 i = indexes.pop();
18436                 goingUp = true;
18437             }
18438
18439             if (node.leaf) { // check current node
18440                 index = node.children.indexOf(item);
18441
18442                 if (index !== -1) {
18443                     // item found, remove the item and condense tree upwards
18444                     node.children.splice(index, 1);
18445                     path.push(node);
18446                     this._condense(path);
18447                     return this;
18448                 }
18449             }
18450
18451             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
18452                 path.push(node);
18453                 indexes.push(i);
18454                 i = 0;
18455                 parent = node;
18456                 node = node.children[0];
18457
18458             } else if (parent) { // go right
18459                 i++;
18460                 node = parent.children[i];
18461                 goingUp = false;
18462
18463             } else node = null; // nothing found
18464         }
18465
18466         return this;
18467     },
18468
18469     toBBox: function (item) { return item; },
18470
18471     compareMinX: function (a, b) { return a[0] - b[0]; },
18472     compareMinY: function (a, b) { return a[1] - b[1]; },
18473
18474     toJSON: function () { return this.data; },
18475
18476     fromJSON: function (data) {
18477         this.data = data;
18478         return this;
18479     },
18480
18481     _all: function (node, result) {
18482         var nodesToSearch = [];
18483         while (node) {
18484             if (node.leaf) result.push.apply(result, node.children);
18485             else nodesToSearch.push.apply(nodesToSearch, node.children);
18486
18487             node = nodesToSearch.pop();
18488         }
18489         return result;
18490     },
18491
18492     _build: function (items, left, right, height) {
18493
18494         var N = right - left + 1,
18495             M = this._maxEntries,
18496             node;
18497
18498         if (N <= M) {
18499             // reached leaf level; return leaf
18500             node = {
18501                 children: items.slice(left, right + 1),
18502                 height: 1,
18503                 bbox: null,
18504                 leaf: true
18505             };
18506             calcBBox(node, this.toBBox);
18507             return node;
18508         }
18509
18510         if (!height) {
18511             // target height of the bulk-loaded tree
18512             height = Math.ceil(Math.log(N) / Math.log(M));
18513
18514             // target number of root entries to maximize storage utilization
18515             M = Math.ceil(N / Math.pow(M, height - 1));
18516         }
18517
18518         node = {
18519             children: [],
18520             height: height,
18521             bbox: null,
18522             leaf: false
18523         };
18524
18525         // split the items into M mostly square tiles
18526
18527         var N2 = Math.ceil(N / M),
18528             N1 = N2 * Math.ceil(Math.sqrt(M)),
18529             i, j, right2, right3;
18530
18531         multiSelect(items, left, right, N1, this.compareMinX);
18532
18533         for (i = left; i <= right; i += N1) {
18534
18535             right2 = Math.min(i + N1 - 1, right);
18536
18537             multiSelect(items, i, right2, N2, this.compareMinY);
18538
18539             for (j = i; j <= right2; j += N2) {
18540
18541                 right3 = Math.min(j + N2 - 1, right2);
18542
18543                 // pack each entry recursively
18544                 node.children.push(this._build(items, j, right3, height - 1));
18545             }
18546         }
18547
18548         calcBBox(node, this.toBBox);
18549
18550         return node;
18551     },
18552
18553     _chooseSubtree: function (bbox, node, level, path) {
18554
18555         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
18556
18557         while (true) {
18558             path.push(node);
18559
18560             if (node.leaf || path.length - 1 === level) break;
18561
18562             minArea = minEnlargement = Infinity;
18563
18564             for (i = 0, len = node.children.length; i < len; i++) {
18565                 child = node.children[i];
18566                 area = bboxArea(child.bbox);
18567                 enlargement = enlargedArea(bbox, child.bbox) - area;
18568
18569                 // choose entry with the least area enlargement
18570                 if (enlargement < minEnlargement) {
18571                     minEnlargement = enlargement;
18572                     minArea = area < minArea ? area : minArea;
18573                     targetNode = child;
18574
18575                 } else if (enlargement === minEnlargement) {
18576                     // otherwise choose one with the smallest area
18577                     if (area < minArea) {
18578                         minArea = area;
18579                         targetNode = child;
18580                     }
18581                 }
18582             }
18583
18584             node = targetNode;
18585         }
18586
18587         return node;
18588     },
18589
18590     _insert: function (item, level, isNode) {
18591
18592         var toBBox = this.toBBox,
18593             bbox = isNode ? item.bbox : toBBox(item),
18594             insertPath = [];
18595
18596         // find the best node for accommodating the item, saving all nodes along the path too
18597         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
18598
18599         // put the item into the node
18600         node.children.push(item);
18601         extend(node.bbox, bbox);
18602
18603         // split on node overflow; propagate upwards if necessary
18604         while (level >= 0) {
18605             if (insertPath[level].children.length > this._maxEntries) {
18606                 this._split(insertPath, level);
18607                 level--;
18608             } else break;
18609         }
18610
18611         // adjust bboxes along the insertion path
18612         this._adjustParentBBoxes(bbox, insertPath, level);
18613     },
18614
18615     // split overflowed node into two
18616     _split: function (insertPath, level) {
18617
18618         var node = insertPath[level],
18619             M = node.children.length,
18620             m = this._minEntries;
18621
18622         this._chooseSplitAxis(node, m, M);
18623
18624         var splitIndex = this._chooseSplitIndex(node, m, M);
18625
18626         var newNode = {
18627             children: node.children.splice(splitIndex, node.children.length - splitIndex),
18628             height: node.height,
18629             bbox: null,
18630             leaf: false
18631         };
18632
18633         if (node.leaf) newNode.leaf = true;
18634
18635         calcBBox(node, this.toBBox);
18636         calcBBox(newNode, this.toBBox);
18637
18638         if (level) insertPath[level - 1].children.push(newNode);
18639         else this._splitRoot(node, newNode);
18640     },
18641
18642     _splitRoot: function (node, newNode) {
18643         // split root node
18644         this.data = {
18645             children: [node, newNode],
18646             height: node.height + 1,
18647             bbox: null,
18648             leaf: false
18649         };
18650         calcBBox(this.data, this.toBBox);
18651     },
18652
18653     _chooseSplitIndex: function (node, m, M) {
18654
18655         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
18656
18657         minOverlap = minArea = Infinity;
18658
18659         for (i = m; i <= M - m; i++) {
18660             bbox1 = distBBox(node, 0, i, this.toBBox);
18661             bbox2 = distBBox(node, i, M, this.toBBox);
18662
18663             overlap = intersectionArea(bbox1, bbox2);
18664             area = bboxArea(bbox1) + bboxArea(bbox2);
18665
18666             // choose distribution with minimum overlap
18667             if (overlap < minOverlap) {
18668                 minOverlap = overlap;
18669                 index = i;
18670
18671                 minArea = area < minArea ? area : minArea;
18672
18673             } else if (overlap === minOverlap) {
18674                 // otherwise choose distribution with minimum area
18675                 if (area < minArea) {
18676                     minArea = area;
18677                     index = i;
18678                 }
18679             }
18680         }
18681
18682         return index;
18683     },
18684
18685     // sorts node children by the best axis for split
18686     _chooseSplitAxis: function (node, m, M) {
18687
18688         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
18689             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
18690             xMargin = this._allDistMargin(node, m, M, compareMinX),
18691             yMargin = this._allDistMargin(node, m, M, compareMinY);
18692
18693         // if total distributions margin value is minimal for x, sort by minX,
18694         // otherwise it's already sorted by minY
18695         if (xMargin < yMargin) node.children.sort(compareMinX);
18696     },
18697
18698     // total margin of all possible split distributions where each node is at least m full
18699     _allDistMargin: function (node, m, M, compare) {
18700
18701         node.children.sort(compare);
18702
18703         var toBBox = this.toBBox,
18704             leftBBox = distBBox(node, 0, m, toBBox),
18705             rightBBox = distBBox(node, M - m, M, toBBox),
18706             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
18707             i, child;
18708
18709         for (i = m; i < M - m; i++) {
18710             child = node.children[i];
18711             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
18712             margin += bboxMargin(leftBBox);
18713         }
18714
18715         for (i = M - m - 1; i >= m; i--) {
18716             child = node.children[i];
18717             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
18718             margin += bboxMargin(rightBBox);
18719         }
18720
18721         return margin;
18722     },
18723
18724     _adjustParentBBoxes: function (bbox, path, level) {
18725         // adjust bboxes along the given tree path
18726         for (var i = level; i >= 0; i--) {
18727             extend(path[i].bbox, bbox);
18728         }
18729     },
18730
18731     _condense: function (path) {
18732         // go through the path, removing empty nodes and updating bboxes
18733         for (var i = path.length - 1, siblings; i >= 0; i--) {
18734             if (path[i].children.length === 0) {
18735                 if (i > 0) {
18736                     siblings = path[i - 1].children;
18737                     siblings.splice(siblings.indexOf(path[i]), 1);
18738
18739                 } else this.clear();
18740
18741             } else calcBBox(path[i], this.toBBox);
18742         }
18743     },
18744
18745     _initFormat: function (format) {
18746         // data format (minX, minY, maxX, maxY accessors)
18747
18748         // uses eval-type function compilation instead of just accepting a toBBox function
18749         // because the algorithms are very sensitive to sorting functions performance,
18750         // so they should be dead simple and without inner calls
18751
18752         // jshint evil: true
18753
18754         var compareArr = ['return a', ' - b', ';'];
18755
18756         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
18757         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
18758
18759         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
18760     }
18761 };
18762
18763
18764 // calculate node's bbox from bboxes of its children
18765 function calcBBox(node, toBBox) {
18766     node.bbox = distBBox(node, 0, node.children.length, toBBox);
18767 }
18768
18769 // min bounding rectangle of node children from k to p-1
18770 function distBBox(node, k, p, toBBox) {
18771     var bbox = empty();
18772
18773     for (var i = k, child; i < p; i++) {
18774         child = node.children[i];
18775         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
18776     }
18777
18778     return bbox;
18779 }
18780
18781 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
18782
18783 function extend(a, b) {
18784     a[0] = Math.min(a[0], b[0]);
18785     a[1] = Math.min(a[1], b[1]);
18786     a[2] = Math.max(a[2], b[2]);
18787     a[3] = Math.max(a[3], b[3]);
18788     return a;
18789 }
18790
18791 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
18792 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
18793
18794 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
18795 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
18796
18797 function enlargedArea(a, b) {
18798     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
18799            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
18800 }
18801
18802 function intersectionArea(a, b) {
18803     var minX = Math.max(a[0], b[0]),
18804         minY = Math.max(a[1], b[1]),
18805         maxX = Math.min(a[2], b[2]),
18806         maxY = Math.min(a[3], b[3]);
18807
18808     return Math.max(0, maxX - minX) *
18809            Math.max(0, maxY - minY);
18810 }
18811
18812 function contains(a, b) {
18813     return a[0] <= b[0] &&
18814            a[1] <= b[1] &&
18815            b[2] <= a[2] &&
18816            b[3] <= a[3];
18817 }
18818
18819 function intersects(a, b) {
18820     return b[0] <= a[2] &&
18821            b[1] <= a[3] &&
18822            b[2] >= a[0] &&
18823            b[3] >= a[1];
18824 }
18825
18826 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
18827 // combines selection algorithm with binary divide & conquer approach
18828
18829 function multiSelect(arr, left, right, n, compare) {
18830     var stack = [left, right],
18831         mid;
18832
18833     while (stack.length) {
18834         right = stack.pop();
18835         left = stack.pop();
18836
18837         if (right - left <= n) continue;
18838
18839         mid = left + Math.ceil((right - left) / n / 2) * n;
18840         select(arr, left, right, mid, compare);
18841
18842         stack.push(left, mid, mid, right);
18843     }
18844 }
18845
18846 // Floyd-Rivest selection algorithm:
18847 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
18848 function select(arr, left, right, k, compare) {
18849     var n, i, z, s, sd, newLeft, newRight, t, j;
18850
18851     while (right > left) {
18852         if (right - left > 600) {
18853             n = right - left + 1;
18854             i = k - left + 1;
18855             z = Math.log(n);
18856             s = 0.5 * Math.exp(2 * z / 3);
18857             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
18858             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
18859             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
18860             select(arr, newLeft, newRight, k, compare);
18861         }
18862
18863         t = arr[k];
18864         i = left;
18865         j = right;
18866
18867         swap(arr, left, k);
18868         if (compare(arr[right], t) > 0) swap(arr, left, right);
18869
18870         while (i < j) {
18871             swap(arr, i, j);
18872             i++;
18873             j--;
18874             while (compare(arr[i], t) < 0) i++;
18875             while (compare(arr[j], t) > 0) j--;
18876         }
18877
18878         if (compare(arr[left], t) === 0) swap(arr, left, j);
18879         else {
18880             j++;
18881             swap(arr, j, right);
18882         }
18883
18884         if (j <= k) left = j + 1;
18885         if (k <= j) right = j - 1;
18886     }
18887 }
18888
18889 function swap(arr, i, j) {
18890     var tmp = arr[i];
18891     arr[i] = arr[j];
18892     arr[j] = tmp;
18893 }
18894
18895
18896 // export as AMD/CommonJS module or global variable
18897 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
18898 else if (typeof module !== 'undefined') module.exports = rbush;
18899 else if (typeof self !== 'undefined') self.rbush = rbush;
18900 else window.rbush = rbush;
18901
18902 })();
18903 (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;
18904 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){
18905 module.exports = element;
18906 module.exports.pair = pair;
18907 module.exports.format = format;
18908 module.exports.formatPair = formatPair;
18909
18910 function element(x, dims) {
18911     return search(x, dims).val;
18912 }
18913
18914 function formatPair(x) {
18915     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
18916 }
18917
18918 // Is 0 North or South?
18919 function format(x, dim) {
18920     var dirs = {
18921             lat: ['N', 'S'],
18922             lon: ['E', 'W']
18923         }[dim] || '',
18924         dir = dirs[x >= 0 ? 0 : 1],
18925         abs = Math.abs(x),
18926         whole = Math.floor(abs),
18927         fraction = abs - whole,
18928         fractionMinutes = fraction * 60,
18929         minutes = Math.floor(fractionMinutes),
18930         seconds = Math.floor((fractionMinutes - minutes) * 60);
18931
18932     return whole + '° ' +
18933         (minutes ? minutes + "' " : '') +
18934         (seconds ? seconds + '" ' : '') + dir;
18935 }
18936
18937 function search(x, dims, r) {
18938     if (!dims) dims = 'NSEW';
18939     if (typeof x !== 'string') return { val: null, regex: r };
18940     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
18941     var m = r.exec(x);
18942     if (!m) return { val: null, regex: r };
18943     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
18944     else return {
18945         val: (((m[1]) ? parseFloat(m[1]) : 0) +
18946             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
18947             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
18948             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
18949         regex: r,
18950         raw: m[0],
18951         dim: m[4]
18952     };
18953 }
18954
18955 function pair(x, dims) {
18956     x = x.trim();
18957     var one = search(x, dims);
18958     if (one.val === null) return null;
18959     var two = search(x, dims, one.regex);
18960     if (two.val === null) return null;
18961     // null if one/two are not contiguous.
18962     if (one.raw + two.raw !== x) return null;
18963     if (one.dim) return swapdim(one.val, two.val, one.dim);
18964     else return [one.val, two.val];
18965 }
18966
18967 function swapdim(a, b, dim) {
18968     if (dim == 'N' || dim == 'S') return [a, b];
18969     if (dim == 'W' || dim == 'E') return [b, a];
18970 }
18971
18972 },{}]},{},[1])
18973 (1)
18974 });
18975 ;toGeoJSON = (function() {
18976     'use strict';
18977
18978     var removeSpace = (/\s*/g),
18979         trimSpace = (/^\s*|\s*$/g),
18980         splitSpace = (/\s+/);
18981     // generate a short, numeric hash of a string
18982     function okhash(x) {
18983         if (!x || !x.length) return 0;
18984         for (var i = 0, h = 0; i < x.length; i++) {
18985             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
18986         } return h;
18987     }
18988     // all Y children of X
18989     function get(x, y) { return x.getElementsByTagName(y); }
18990     function attr(x, y) { return x.getAttribute(y); }
18991     function attrf(x, y) { return parseFloat(attr(x, y)); }
18992     // one Y child of X, if any, otherwise null
18993     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
18994     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
18995     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
18996     // cast array x into numbers
18997     function numarray(x) {
18998         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
18999         return o;
19000     }
19001     function clean(x) {
19002         var o = {};
19003         for (var i in x) if (x[i]) o[i] = x[i];
19004         return o;
19005     }
19006     // get the content of a text node, if any
19007     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
19008     // get one coordinate from a coordinate array, if any
19009     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
19010     // get all coordinates from a coordinate array as [[],[]]
19011     function coord(v) {
19012         var coords = v.replace(trimSpace, '').split(splitSpace),
19013             o = [];
19014         for (var i = 0; i < coords.length; i++) {
19015             o.push(coord1(coords[i]));
19016         }
19017         return o;
19018     }
19019     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
19020
19021     // create a new feature collection parent object
19022     function fc() {
19023         return {
19024             type: 'FeatureCollection',
19025             features: []
19026         };
19027     }
19028
19029     var styleSupport = false;
19030     if (typeof XMLSerializer !== 'undefined') {
19031         var serializer = new XMLSerializer();
19032         styleSupport = true;
19033     }
19034     function xml2str(str) { return serializer.serializeToString(str); }
19035
19036     var t = {
19037         kml: function(doc, o) {
19038             o = o || {};
19039
19040             var gj = fc(),
19041                 // styleindex keeps track of hashed styles in order to match features
19042                 styleIndex = {},
19043                 // atomic geospatial types supported by KML - MultiGeometry is
19044                 // handled separately
19045                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
19046                 // all root placemarks in the file
19047                 placemarks = get(doc, 'Placemark'),
19048                 styles = get(doc, 'Style');
19049
19050             if (styleSupport) for (var k = 0; k < styles.length; k++) {
19051                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
19052             }
19053             for (var j = 0; j < placemarks.length; j++) {
19054                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
19055             }
19056             function gxCoord(v) { return numarray(v.split(' ')); }
19057             function gxCoords(root) {
19058                 var elems = get(root, 'coord', 'gx'), coords = [];
19059                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
19060                 return coords;
19061             }
19062             function getGeometry(root) {
19063                 var geomNode, geomNodes, i, j, k, geoms = [];
19064                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
19065                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
19066                 for (i = 0; i < geotypes.length; i++) {
19067                     geomNodes = get(root, geotypes[i]);
19068                     if (geomNodes) {
19069                         for (j = 0; j < geomNodes.length; j++) {
19070                             geomNode = geomNodes[j];
19071                             if (geotypes[i] == 'Point') {
19072                                 geoms.push({
19073                                     type: 'Point',
19074                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
19075                                 });
19076                             } else if (geotypes[i] == 'LineString') {
19077                                 geoms.push({
19078                                     type: 'LineString',
19079                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
19080                                 });
19081                             } else if (geotypes[i] == 'Polygon') {
19082                                 var rings = get(geomNode, 'LinearRing'),
19083                                     coords = [];
19084                                 for (k = 0; k < rings.length; k++) {
19085                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
19086                                 }
19087                                 geoms.push({
19088                                     type: 'Polygon',
19089                                     coordinates: coords
19090                                 });
19091                             } else if (geotypes[i] == 'Track') {
19092                                 geoms.push({
19093                                     type: 'LineString',
19094                                     coordinates: gxCoords(geomNode)
19095                                 });
19096                             }
19097                         }
19098                     }
19099                 }
19100                 return geoms;
19101             }
19102             function getPlacemark(root) {
19103                 var geoms = getGeometry(root), i, properties = {},
19104                     name = nodeVal(get1(root, 'name')),
19105                     styleUrl = nodeVal(get1(root, 'styleUrl')),
19106                     description = nodeVal(get1(root, 'description')),
19107                     extendedData = get1(root, 'ExtendedData');
19108
19109                 if (!geoms.length) return [];
19110                 if (name) properties.name = name;
19111                 if (styleUrl && styleIndex[styleUrl]) {
19112                     properties.styleUrl = styleUrl;
19113                     properties.styleHash = styleIndex[styleUrl];
19114                 }
19115                 if (description) properties.description = description;
19116                 if (extendedData) {
19117                     var datas = get(extendedData, 'Data'),
19118                         simpleDatas = get(extendedData, 'SimpleData');
19119
19120                     for (i = 0; i < datas.length; i++) {
19121                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
19122                     }
19123                     for (i = 0; i < simpleDatas.length; i++) {
19124                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
19125                     }
19126                 }
19127                 return [{
19128                     type: 'Feature',
19129                     geometry: (geoms.length === 1) ? geoms[0] : {
19130                         type: 'GeometryCollection',
19131                         geometries: geoms
19132                     },
19133                     properties: properties
19134                 }];
19135             }
19136             return gj;
19137         },
19138         gpx: function(doc, o) {
19139             var i,
19140                 tracks = get(doc, 'trk'),
19141                 routes = get(doc, 'rte'),
19142                 waypoints = get(doc, 'wpt'),
19143                 // a feature collection
19144                 gj = fc();
19145             for (i = 0; i < tracks.length; i++) {
19146                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
19147             }
19148             for (i = 0; i < routes.length; i++) {
19149                 gj.features.push(getLinestring(routes[i], 'rtept'));
19150             }
19151             for (i = 0; i < waypoints.length; i++) {
19152                 gj.features.push(getPoint(waypoints[i]));
19153             }
19154             function getLinestring(node, pointname) {
19155                 var j, pts = get(node, pointname), line = [];
19156                 for (j = 0; j < pts.length; j++) {
19157                     line.push(coordPair(pts[j]));
19158                 }
19159                 return {
19160                     type: 'Feature',
19161                     properties: getProperties(node),
19162                     geometry: {
19163                         type: 'LineString',
19164                         coordinates: line
19165                     }
19166                 };
19167             }
19168             function getPoint(node) {
19169                 var prop = getProperties(node);
19170                 prop.ele = nodeVal(get1(node, 'ele'));
19171                 prop.sym = nodeVal(get1(node, 'sym'));
19172                 return {
19173                     type: 'Feature',
19174                     properties: prop,
19175                     geometry: {
19176                         type: 'Point',
19177                         coordinates: coordPair(node)
19178                     }
19179                 };
19180             }
19181             function getProperties(node) {
19182                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
19183                             'time', 'keywords'],
19184                     prop = {},
19185                     k;
19186                 for (k = 0; k < meta.length; k++) {
19187                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
19188                 }
19189                 return clean(prop);
19190             }
19191             return gj;
19192         }
19193     };
19194     return t;
19195 })();
19196
19197 if (typeof module !== 'undefined') module.exports = toGeoJSON;
19198 /**
19199  * marked - a markdown parser
19200  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
19201  * https://github.com/chjj/marked
19202  */
19203
19204 ;(function() {
19205
19206 /**
19207  * Block-Level Grammar
19208  */
19209
19210 var block = {
19211   newline: /^\n+/,
19212   code: /^( {4}[^\n]+\n*)+/,
19213   fences: noop,
19214   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
19215   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19216   nptable: noop,
19217   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
19218   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
19219   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
19220   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
19221   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
19222   table: noop,
19223   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
19224   text: /^[^\n]+/
19225 };
19226
19227 block.bullet = /(?:[*+-]|\d+\.)/;
19228 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
19229 block.item = replace(block.item, 'gm')
19230   (/bull/g, block.bullet)
19231   ();
19232
19233 block.list = replace(block.list)
19234   (/bull/g, block.bullet)
19235   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
19236   ();
19237
19238 block._tag = '(?!(?:'
19239   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
19240   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
19241   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
19242
19243 block.html = replace(block.html)
19244   ('comment', /<!--[\s\S]*?-->/)
19245   ('closed', /<(tag)[\s\S]+?<\/\1>/)
19246   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
19247   (/tag/g, block._tag)
19248   ();
19249
19250 block.paragraph = replace(block.paragraph)
19251   ('hr', block.hr)
19252   ('heading', block.heading)
19253   ('lheading', block.lheading)
19254   ('blockquote', block.blockquote)
19255   ('tag', '<' + block._tag)
19256   ('def', block.def)
19257   ();
19258
19259 /**
19260  * Normal Block Grammar
19261  */
19262
19263 block.normal = merge({}, block);
19264
19265 /**
19266  * GFM Block Grammar
19267  */
19268
19269 block.gfm = merge({}, block.normal, {
19270   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
19271   paragraph: /^/
19272 });
19273
19274 block.gfm.paragraph = replace(block.paragraph)
19275   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
19276   ();
19277
19278 /**
19279  * GFM + Tables Block Grammar
19280  */
19281
19282 block.tables = merge({}, block.gfm, {
19283   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
19284   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
19285 });
19286
19287 /**
19288  * Block Lexer
19289  */
19290
19291 function Lexer(options) {
19292   this.tokens = [];
19293   this.tokens.links = {};
19294   this.options = options || marked.defaults;
19295   this.rules = block.normal;
19296
19297   if (this.options.gfm) {
19298     if (this.options.tables) {
19299       this.rules = block.tables;
19300     } else {
19301       this.rules = block.gfm;
19302     }
19303   }
19304 }
19305
19306 /**
19307  * Expose Block Rules
19308  */
19309
19310 Lexer.rules = block;
19311
19312 /**
19313  * Static Lex Method
19314  */
19315
19316 Lexer.lex = function(src, options) {
19317   var lexer = new Lexer(options);
19318   return lexer.lex(src);
19319 };
19320
19321 /**
19322  * Preprocessing
19323  */
19324
19325 Lexer.prototype.lex = function(src) {
19326   src = src
19327     .replace(/\r\n|\r/g, '\n')
19328     .replace(/\t/g, '    ')
19329     .replace(/\u00a0/g, ' ')
19330     .replace(/\u2424/g, '\n');
19331
19332   return this.token(src, true);
19333 };
19334
19335 /**
19336  * Lexing
19337  */
19338
19339 Lexer.prototype.token = function(src, top) {
19340   var src = src.replace(/^ +$/gm, '')
19341     , next
19342     , loose
19343     , cap
19344     , bull
19345     , b
19346     , item
19347     , space
19348     , i
19349     , l;
19350
19351   while (src) {
19352     // newline
19353     if (cap = this.rules.newline.exec(src)) {
19354       src = src.substring(cap[0].length);
19355       if (cap[0].length > 1) {
19356         this.tokens.push({
19357           type: 'space'
19358         });
19359       }
19360     }
19361
19362     // code
19363     if (cap = this.rules.code.exec(src)) {
19364       src = src.substring(cap[0].length);
19365       cap = cap[0].replace(/^ {4}/gm, '');
19366       this.tokens.push({
19367         type: 'code',
19368         text: !this.options.pedantic
19369           ? cap.replace(/\n+$/, '')
19370           : cap
19371       });
19372       continue;
19373     }
19374
19375     // fences (gfm)
19376     if (cap = this.rules.fences.exec(src)) {
19377       src = src.substring(cap[0].length);
19378       this.tokens.push({
19379         type: 'code',
19380         lang: cap[2],
19381         text: cap[3]
19382       });
19383       continue;
19384     }
19385
19386     // heading
19387     if (cap = this.rules.heading.exec(src)) {
19388       src = src.substring(cap[0].length);
19389       this.tokens.push({
19390         type: 'heading',
19391         depth: cap[1].length,
19392         text: cap[2]
19393       });
19394       continue;
19395     }
19396
19397     // table no leading pipe (gfm)
19398     if (top && (cap = this.rules.nptable.exec(src))) {
19399       src = src.substring(cap[0].length);
19400
19401       item = {
19402         type: 'table',
19403         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19404         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19405         cells: cap[3].replace(/\n$/, '').split('\n')
19406       };
19407
19408       for (i = 0; i < item.align.length; i++) {
19409         if (/^ *-+: *$/.test(item.align[i])) {
19410           item.align[i] = 'right';
19411         } else if (/^ *:-+: *$/.test(item.align[i])) {
19412           item.align[i] = 'center';
19413         } else if (/^ *:-+ *$/.test(item.align[i])) {
19414           item.align[i] = 'left';
19415         } else {
19416           item.align[i] = null;
19417         }
19418       }
19419
19420       for (i = 0; i < item.cells.length; i++) {
19421         item.cells[i] = item.cells[i].split(/ *\| */);
19422       }
19423
19424       this.tokens.push(item);
19425
19426       continue;
19427     }
19428
19429     // lheading
19430     if (cap = this.rules.lheading.exec(src)) {
19431       src = src.substring(cap[0].length);
19432       this.tokens.push({
19433         type: 'heading',
19434         depth: cap[2] === '=' ? 1 : 2,
19435         text: cap[1]
19436       });
19437       continue;
19438     }
19439
19440     // hr
19441     if (cap = this.rules.hr.exec(src)) {
19442       src = src.substring(cap[0].length);
19443       this.tokens.push({
19444         type: 'hr'
19445       });
19446       continue;
19447     }
19448
19449     // blockquote
19450     if (cap = this.rules.blockquote.exec(src)) {
19451       src = src.substring(cap[0].length);
19452
19453       this.tokens.push({
19454         type: 'blockquote_start'
19455       });
19456
19457       cap = cap[0].replace(/^ *> ?/gm, '');
19458
19459       // Pass `top` to keep the current
19460       // "toplevel" state. This is exactly
19461       // how markdown.pl works.
19462       this.token(cap, top);
19463
19464       this.tokens.push({
19465         type: 'blockquote_end'
19466       });
19467
19468       continue;
19469     }
19470
19471     // list
19472     if (cap = this.rules.list.exec(src)) {
19473       src = src.substring(cap[0].length);
19474       bull = cap[2];
19475
19476       this.tokens.push({
19477         type: 'list_start',
19478         ordered: bull.length > 1
19479       });
19480
19481       // Get each top-level item.
19482       cap = cap[0].match(this.rules.item);
19483
19484       next = false;
19485       l = cap.length;
19486       i = 0;
19487
19488       for (; i < l; i++) {
19489         item = cap[i];
19490
19491         // Remove the list item's bullet
19492         // so it is seen as the next token.
19493         space = item.length;
19494         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19495
19496         // Outdent whatever the
19497         // list item contains. Hacky.
19498         if (~item.indexOf('\n ')) {
19499           space -= item.length;
19500           item = !this.options.pedantic
19501             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19502             : item.replace(/^ {1,4}/gm, '');
19503         }
19504
19505         // Determine whether the next list item belongs here.
19506         // Backpedal if it does not belong in this list.
19507         if (this.options.smartLists && i !== l - 1) {
19508           b = block.bullet.exec(cap[i+1])[0];
19509           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19510             src = cap.slice(i + 1).join('\n') + src;
19511             i = l - 1;
19512           }
19513         }
19514
19515         // Determine whether item is loose or not.
19516         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19517         // for discount behavior.
19518         loose = next || /\n\n(?!\s*$)/.test(item);
19519         if (i !== l - 1) {
19520           next = item[item.length-1] === '\n';
19521           if (!loose) loose = next;
19522         }
19523
19524         this.tokens.push({
19525           type: loose
19526             ? 'loose_item_start'
19527             : 'list_item_start'
19528         });
19529
19530         // Recurse.
19531         this.token(item, false);
19532
19533         this.tokens.push({
19534           type: 'list_item_end'
19535         });
19536       }
19537
19538       this.tokens.push({
19539         type: 'list_end'
19540       });
19541
19542       continue;
19543     }
19544
19545     // html
19546     if (cap = this.rules.html.exec(src)) {
19547       src = src.substring(cap[0].length);
19548       this.tokens.push({
19549         type: this.options.sanitize
19550           ? 'paragraph'
19551           : 'html',
19552         pre: cap[1] === 'pre' || cap[1] === 'script',
19553         text: cap[0]
19554       });
19555       continue;
19556     }
19557
19558     // def
19559     if (top && (cap = this.rules.def.exec(src))) {
19560       src = src.substring(cap[0].length);
19561       this.tokens.links[cap[1].toLowerCase()] = {
19562         href: cap[2],
19563         title: cap[3]
19564       };
19565       continue;
19566     }
19567
19568     // table (gfm)
19569     if (top && (cap = this.rules.table.exec(src))) {
19570       src = src.substring(cap[0].length);
19571
19572       item = {
19573         type: 'table',
19574         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19575         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19576         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19577       };
19578
19579       for (i = 0; i < item.align.length; i++) {
19580         if (/^ *-+: *$/.test(item.align[i])) {
19581           item.align[i] = 'right';
19582         } else if (/^ *:-+: *$/.test(item.align[i])) {
19583           item.align[i] = 'center';
19584         } else if (/^ *:-+ *$/.test(item.align[i])) {
19585           item.align[i] = 'left';
19586         } else {
19587           item.align[i] = null;
19588         }
19589       }
19590
19591       for (i = 0; i < item.cells.length; i++) {
19592         item.cells[i] = item.cells[i]
19593           .replace(/^ *\| *| *\| *$/g, '')
19594           .split(/ *\| */);
19595       }
19596
19597       this.tokens.push(item);
19598
19599       continue;
19600     }
19601
19602     // top-level paragraph
19603     if (top && (cap = this.rules.paragraph.exec(src))) {
19604       src = src.substring(cap[0].length);
19605       this.tokens.push({
19606         type: 'paragraph',
19607         text: cap[1][cap[1].length-1] === '\n'
19608           ? cap[1].slice(0, -1)
19609           : cap[1]
19610       });
19611       continue;
19612     }
19613
19614     // text
19615     if (cap = this.rules.text.exec(src)) {
19616       // Top-level should never reach here.
19617       src = src.substring(cap[0].length);
19618       this.tokens.push({
19619         type: 'text',
19620         text: cap[0]
19621       });
19622       continue;
19623     }
19624
19625     if (src) {
19626       throw new
19627         Error('Infinite loop on byte: ' + src.charCodeAt(0));
19628     }
19629   }
19630
19631   return this.tokens;
19632 };
19633
19634 /**
19635  * Inline-Level Grammar
19636  */
19637
19638 var inline = {
19639   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19640   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19641   url: noop,
19642   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19643   link: /^!?\[(inside)\]\(href\)/,
19644   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19645   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19646   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19647   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19648   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19649   br: /^ {2,}\n(?!\s*$)/,
19650   del: noop,
19651   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19652 };
19653
19654 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
19655 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19656
19657 inline.link = replace(inline.link)
19658   ('inside', inline._inside)
19659   ('href', inline._href)
19660   ();
19661
19662 inline.reflink = replace(inline.reflink)
19663   ('inside', inline._inside)
19664   ();
19665
19666 /**
19667  * Normal Inline Grammar
19668  */
19669
19670 inline.normal = merge({}, inline);
19671
19672 /**
19673  * Pedantic Inline Grammar
19674  */
19675
19676 inline.pedantic = merge({}, inline.normal, {
19677   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19678   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19679 });
19680
19681 /**
19682  * GFM Inline Grammar
19683  */
19684
19685 inline.gfm = merge({}, inline.normal, {
19686   escape: replace(inline.escape)('])', '~|])')(),
19687   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19688   del: /^~~(?=\S)([\s\S]*?\S)~~/,
19689   text: replace(inline.text)
19690     (']|', '~]|')
19691     ('|', '|https?://|')
19692     ()
19693 });
19694
19695 /**
19696  * GFM + Line Breaks Inline Grammar
19697  */
19698
19699 inline.breaks = merge({}, inline.gfm, {
19700   br: replace(inline.br)('{2,}', '*')(),
19701   text: replace(inline.gfm.text)('{2,}', '*')()
19702 });
19703
19704 /**
19705  * Inline Lexer & Compiler
19706  */
19707
19708 function InlineLexer(links, options) {
19709   this.options = options || marked.defaults;
19710   this.links = links;
19711   this.rules = inline.normal;
19712
19713   if (!this.links) {
19714     throw new
19715       Error('Tokens array requires a `links` property.');
19716   }
19717
19718   if (this.options.gfm) {
19719     if (this.options.breaks) {
19720       this.rules = inline.breaks;
19721     } else {
19722       this.rules = inline.gfm;
19723     }
19724   } else if (this.options.pedantic) {
19725     this.rules = inline.pedantic;
19726   }
19727 }
19728
19729 /**
19730  * Expose Inline Rules
19731  */
19732
19733 InlineLexer.rules = inline;
19734
19735 /**
19736  * Static Lexing/Compiling Method
19737  */
19738
19739 InlineLexer.output = function(src, links, options) {
19740   var inline = new InlineLexer(links, options);
19741   return inline.output(src);
19742 };
19743
19744 /**
19745  * Lexing/Compiling
19746  */
19747
19748 InlineLexer.prototype.output = function(src) {
19749   var out = ''
19750     , link
19751     , text
19752     , href
19753     , cap;
19754
19755   while (src) {
19756     // escape
19757     if (cap = this.rules.escape.exec(src)) {
19758       src = src.substring(cap[0].length);
19759       out += cap[1];
19760       continue;
19761     }
19762
19763     // autolink
19764     if (cap = this.rules.autolink.exec(src)) {
19765       src = src.substring(cap[0].length);
19766       if (cap[2] === '@') {
19767         text = cap[1][6] === ':'
19768           ? this.mangle(cap[1].substring(7))
19769           : this.mangle(cap[1]);
19770         href = this.mangle('mailto:') + text;
19771       } else {
19772         text = escape(cap[1]);
19773         href = text;
19774       }
19775       out += '<a href="'
19776         + href
19777         + '">'
19778         + text
19779         + '</a>';
19780       continue;
19781     }
19782
19783     // url (gfm)
19784     if (cap = this.rules.url.exec(src)) {
19785       src = src.substring(cap[0].length);
19786       text = escape(cap[1]);
19787       href = text;
19788       out += '<a href="'
19789         + href
19790         + '">'
19791         + text
19792         + '</a>';
19793       continue;
19794     }
19795
19796     // tag
19797     if (cap = this.rules.tag.exec(src)) {
19798       src = src.substring(cap[0].length);
19799       out += this.options.sanitize
19800         ? escape(cap[0])
19801         : cap[0];
19802       continue;
19803     }
19804
19805     // link
19806     if (cap = this.rules.link.exec(src)) {
19807       src = src.substring(cap[0].length);
19808       out += this.outputLink(cap, {
19809         href: cap[2],
19810         title: cap[3]
19811       });
19812       continue;
19813     }
19814
19815     // reflink, nolink
19816     if ((cap = this.rules.reflink.exec(src))
19817         || (cap = this.rules.nolink.exec(src))) {
19818       src = src.substring(cap[0].length);
19819       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19820       link = this.links[link.toLowerCase()];
19821       if (!link || !link.href) {
19822         out += cap[0][0];
19823         src = cap[0].substring(1) + src;
19824         continue;
19825       }
19826       out += this.outputLink(cap, link);
19827       continue;
19828     }
19829
19830     // strong
19831     if (cap = this.rules.strong.exec(src)) {
19832       src = src.substring(cap[0].length);
19833       out += '<strong>'
19834         + this.output(cap[2] || cap[1])
19835         + '</strong>';
19836       continue;
19837     }
19838
19839     // em
19840     if (cap = this.rules.em.exec(src)) {
19841       src = src.substring(cap[0].length);
19842       out += '<em>'
19843         + this.output(cap[2] || cap[1])
19844         + '</em>';
19845       continue;
19846     }
19847
19848     // code
19849     if (cap = this.rules.code.exec(src)) {
19850       src = src.substring(cap[0].length);
19851       out += '<code>'
19852         + escape(cap[2], true)
19853         + '</code>';
19854       continue;
19855     }
19856
19857     // br
19858     if (cap = this.rules.br.exec(src)) {
19859       src = src.substring(cap[0].length);
19860       out += '<br>';
19861       continue;
19862     }
19863
19864     // del (gfm)
19865     if (cap = this.rules.del.exec(src)) {
19866       src = src.substring(cap[0].length);
19867       out += '<del>'
19868         + this.output(cap[1])
19869         + '</del>';
19870       continue;
19871     }
19872
19873     // text
19874     if (cap = this.rules.text.exec(src)) {
19875       src = src.substring(cap[0].length);
19876       out += escape(cap[0]);
19877       continue;
19878     }
19879
19880     if (src) {
19881       throw new
19882         Error('Infinite loop on byte: ' + src.charCodeAt(0));
19883     }
19884   }
19885
19886   return out;
19887 };
19888
19889 /**
19890  * Compile Link
19891  */
19892
19893 InlineLexer.prototype.outputLink = function(cap, link) {
19894   if (cap[0][0] !== '!') {
19895     return '<a href="'
19896       + escape(link.href)
19897       + '"'
19898       + (link.title
19899       ? ' title="'
19900       + escape(link.title)
19901       + '"'
19902       : '')
19903       + '>'
19904       + this.output(cap[1])
19905       + '</a>';
19906   } else {
19907     return '<img src="'
19908       + escape(link.href)
19909       + '" alt="'
19910       + escape(cap[1])
19911       + '"'
19912       + (link.title
19913       ? ' title="'
19914       + escape(link.title)
19915       + '"'
19916       : '')
19917       + '>';
19918   }
19919 };
19920
19921 /**
19922  * Smartypants Transformations
19923  */
19924
19925 InlineLexer.prototype.smartypants = function(text) {
19926   if (!this.options.smartypants) return text;
19927   return text
19928     .replace(/--/g, '—')
19929     .replace(/'([^']*)'/g, '‘$1’')
19930     .replace(/"([^"]*)"/g, '“$1”')
19931     .replace(/\.{3}/g, '…');
19932 };
19933
19934 /**
19935  * Mangle Links
19936  */
19937
19938 InlineLexer.prototype.mangle = function(text) {
19939   var out = ''
19940     , l = text.length
19941     , i = 0
19942     , ch;
19943
19944   for (; i < l; i++) {
19945     ch = text.charCodeAt(i);
19946     if (Math.random() > 0.5) {
19947       ch = 'x' + ch.toString(16);
19948     }
19949     out += '&#' + ch + ';';
19950   }
19951
19952   return out;
19953 };
19954
19955 /**
19956  * Parsing & Compiling
19957  */
19958
19959 function Parser(options) {
19960   this.tokens = [];
19961   this.token = null;
19962   this.options = options || marked.defaults;
19963 }
19964
19965 /**
19966  * Static Parse Method
19967  */
19968
19969 Parser.parse = function(src, options) {
19970   var parser = new Parser(options);
19971   return parser.parse(src);
19972 };
19973
19974 /**
19975  * Parse Loop
19976  */
19977
19978 Parser.prototype.parse = function(src) {
19979   this.inline = new InlineLexer(src.links, this.options);
19980   this.tokens = src.reverse();
19981
19982   var out = '';
19983   while (this.next()) {
19984     out += this.tok();
19985   }
19986
19987   return out;
19988 };
19989
19990 /**
19991  * Next Token
19992  */
19993
19994 Parser.prototype.next = function() {
19995   return this.token = this.tokens.pop();
19996 };
19997
19998 /**
19999  * Preview Next Token
20000  */
20001
20002 Parser.prototype.peek = function() {
20003   return this.tokens[this.tokens.length-1] || 0;
20004 };
20005
20006 /**
20007  * Parse Text Tokens
20008  */
20009
20010 Parser.prototype.parseText = function() {
20011   var body = this.token.text;
20012
20013   while (this.peek().type === 'text') {
20014     body += '\n' + this.next().text;
20015   }
20016
20017   return this.inline.output(body);
20018 };
20019
20020 /**
20021  * Parse Current Token
20022  */
20023
20024 Parser.prototype.tok = function() {
20025   switch (this.token.type) {
20026     case 'space': {
20027       return '';
20028     }
20029     case 'hr': {
20030       return '<hr>\n';
20031     }
20032     case 'heading': {
20033       return '<h'
20034         + this.token.depth
20035         + '>'
20036         + this.inline.output(this.token.text)
20037         + '</h'
20038         + this.token.depth
20039         + '>\n';
20040     }
20041     case 'code': {
20042       if (this.options.highlight) {
20043         var code = this.options.highlight(this.token.text, this.token.lang);
20044         if (code != null && code !== this.token.text) {
20045           this.token.escaped = true;
20046           this.token.text = code;
20047         }
20048       }
20049
20050       if (!this.token.escaped) {
20051         this.token.text = escape(this.token.text, true);
20052       }
20053
20054       return '<pre><code'
20055         + (this.token.lang
20056         ? ' class="'
20057         + this.options.langPrefix
20058         + this.token.lang
20059         + '"'
20060         : '')
20061         + '>'
20062         + this.token.text
20063         + '</code></pre>\n';
20064     }
20065     case 'table': {
20066       var body = ''
20067         , heading
20068         , i
20069         , row
20070         , cell
20071         , j;
20072
20073       // header
20074       body += '<thead>\n<tr>\n';
20075       for (i = 0; i < this.token.header.length; i++) {
20076         heading = this.inline.output(this.token.header[i]);
20077         body += this.token.align[i]
20078           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
20079           : '<th>' + heading + '</th>\n';
20080       }
20081       body += '</tr>\n</thead>\n';
20082
20083       // body
20084       body += '<tbody>\n'
20085       for (i = 0; i < this.token.cells.length; i++) {
20086         row = this.token.cells[i];
20087         body += '<tr>\n';
20088         for (j = 0; j < row.length; j++) {
20089           cell = this.inline.output(row[j]);
20090           body += this.token.align[j]
20091             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
20092             : '<td>' + cell + '</td>\n';
20093         }
20094         body += '</tr>\n';
20095       }
20096       body += '</tbody>\n';
20097
20098       return '<table>\n'
20099         + body
20100         + '</table>\n';
20101     }
20102     case 'blockquote_start': {
20103       var body = '';
20104
20105       while (this.next().type !== 'blockquote_end') {
20106         body += this.tok();
20107       }
20108
20109       return '<blockquote>\n'
20110         + body
20111         + '</blockquote>\n';
20112     }
20113     case 'list_start': {
20114       var type = this.token.ordered ? 'ol' : 'ul'
20115         , body = '';
20116
20117       while (this.next().type !== 'list_end') {
20118         body += this.tok();
20119       }
20120
20121       return '<'
20122         + type
20123         + '>\n'
20124         + body
20125         + '</'
20126         + type
20127         + '>\n';
20128     }
20129     case 'list_item_start': {
20130       var body = '';
20131
20132       while (this.next().type !== 'list_item_end') {
20133         body += this.token.type === 'text'
20134           ? this.parseText()
20135           : this.tok();
20136       }
20137
20138       return '<li>'
20139         + body
20140         + '</li>\n';
20141     }
20142     case 'loose_item_start': {
20143       var body = '';
20144
20145       while (this.next().type !== 'list_item_end') {
20146         body += this.tok();
20147       }
20148
20149       return '<li>'
20150         + body
20151         + '</li>\n';
20152     }
20153     case 'html': {
20154       return !this.token.pre && !this.options.pedantic
20155         ? this.inline.output(this.token.text)
20156         : this.token.text;
20157     }
20158     case 'paragraph': {
20159       return '<p>'
20160         + this.inline.output(this.token.text)
20161         + '</p>\n';
20162     }
20163     case 'text': {
20164       return '<p>'
20165         + this.parseText()
20166         + '</p>\n';
20167     }
20168   }
20169 };
20170
20171 /**
20172  * Helpers
20173  */
20174
20175 function escape(html, encode) {
20176   return html
20177     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
20178     .replace(/</g, '&lt;')
20179     .replace(/>/g, '&gt;')
20180     .replace(/"/g, '&quot;')
20181     .replace(/'/g, '&#39;');
20182 }
20183
20184 function replace(regex, opt) {
20185   regex = regex.source;
20186   opt = opt || '';
20187   return function self(name, val) {
20188     if (!name) return new RegExp(regex, opt);
20189     val = val.source || val;
20190     val = val.replace(/(^|[^\[])\^/g, '$1');
20191     regex = regex.replace(name, val);
20192     return self;
20193   };
20194 }
20195
20196 function noop() {}
20197 noop.exec = noop;
20198
20199 function merge(obj) {
20200   var i = 1
20201     , target
20202     , key;
20203
20204   for (; i < arguments.length; i++) {
20205     target = arguments[i];
20206     for (key in target) {
20207       if (Object.prototype.hasOwnProperty.call(target, key)) {
20208         obj[key] = target[key];
20209       }
20210     }
20211   }
20212
20213   return obj;
20214 }
20215
20216 /**
20217  * Marked
20218  */
20219
20220 function marked(src, opt, callback) {
20221   if (callback || typeof opt === 'function') {
20222     if (!callback) {
20223       callback = opt;
20224       opt = null;
20225     }
20226
20227     if (opt) opt = merge({}, marked.defaults, opt);
20228
20229     var tokens = Lexer.lex(tokens, opt)
20230       , highlight = opt.highlight
20231       , pending = 0
20232       , l = tokens.length
20233       , i = 0;
20234
20235     if (!highlight || highlight.length < 3) {
20236       return callback(null, Parser.parse(tokens, opt));
20237     }
20238
20239     var done = function() {
20240       delete opt.highlight;
20241       var out = Parser.parse(tokens, opt);
20242       opt.highlight = highlight;
20243       return callback(null, out);
20244     };
20245
20246     for (; i < l; i++) {
20247       (function(token) {
20248         if (token.type !== 'code') return;
20249         pending++;
20250         return highlight(token.text, token.lang, function(err, code) {
20251           if (code == null || code === token.text) {
20252             return --pending || done();
20253           }
20254           token.text = code;
20255           token.escaped = true;
20256           --pending || done();
20257         });
20258       })(tokens[i]);
20259     }
20260
20261     return;
20262   }
20263   try {
20264     if (opt) opt = merge({}, marked.defaults, opt);
20265     return Parser.parse(Lexer.lex(src, opt), opt);
20266   } catch (e) {
20267     e.message += '\nPlease report this to https://github.com/chjj/marked.';
20268     if ((opt || marked.defaults).silent) {
20269       return '<p>An error occured:</p><pre>'
20270         + escape(e.message + '', true)
20271         + '</pre>';
20272     }
20273     throw e;
20274   }
20275 }
20276
20277 /**
20278  * Options
20279  */
20280
20281 marked.options =
20282 marked.setOptions = function(opt) {
20283   merge(marked.defaults, opt);
20284   return marked;
20285 };
20286
20287 marked.defaults = {
20288   gfm: true,
20289   tables: true,
20290   breaks: false,
20291   pedantic: false,
20292   sanitize: false,
20293   smartLists: false,
20294   silent: false,
20295   highlight: null,
20296   langPrefix: 'lang-'
20297 };
20298
20299 /**
20300  * Expose
20301  */
20302
20303 marked.Parser = Parser;
20304 marked.parser = Parser.parse;
20305
20306 marked.Lexer = Lexer;
20307 marked.lexer = Lexer.lex;
20308
20309 marked.InlineLexer = InlineLexer;
20310 marked.inlineLexer = InlineLexer.output;
20311
20312 marked.parse = marked;
20313
20314 if (typeof exports === 'object') {
20315   module.exports = marked;
20316 } else if (typeof define === 'function' && define.amd) {
20317   define(function() { return marked; });
20318 } else {
20319   this.marked = marked;
20320 }
20321
20322 }).call(function() {
20323   return this || (typeof window !== 'undefined' ? window : global);
20324 }());
20325 (function () {
20326 'use strict';
20327 window.iD = function () {
20328     window.locale.en = iD.data.en;
20329     window.locale.current('en');
20330
20331     var dispatch = d3.dispatch('enter', 'exit', 'change'),
20332         context = {};
20333
20334     // https://github.com/openstreetmap/iD/issues/772
20335     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
20336     var storage;
20337     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
20338     storage = storage || (function() {
20339         var s = {};
20340         return {
20341             getItem: function(k) { return s[k]; },
20342             setItem: function(k, v) { s[k] = v; },
20343             removeItem: function(k) { delete s[k]; }
20344         };
20345     })();
20346
20347     context.storage = function(k, v) {
20348         try {
20349             if (arguments.length === 1) return storage.getItem(k);
20350             else if (v === null) storage.removeItem(k);
20351             else storage.setItem(k, v);
20352         } catch(e) {
20353             // localstorage quota exceeded
20354             /* eslint-disable no-console */
20355             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
20356             /* eslint-enable no-console */
20357         }
20358     };
20359
20360
20361     /* Straight accessors. Avoid using these if you can. */
20362     var ui, connection, history;
20363     context.ui = function() { return ui; };
20364     context.connection = function() { return connection; };
20365     context.history = function() { return history; };
20366
20367
20368     /* Connection */
20369     function entitiesLoaded(err, result) {
20370         if (!err) history.merge(result.data, result.extent);
20371     }
20372
20373     context.preauth = function(options) {
20374         connection.switch(options);
20375         return context;
20376     };
20377
20378     context.loadTiles = function(projection, dimensions, callback) {
20379         function done(err, result) {
20380             entitiesLoaded(err, result);
20381             if (callback) callback(err, result);
20382         }
20383         connection.loadTiles(projection, dimensions, done);
20384     };
20385
20386     context.loadEntity = function(id, callback) {
20387         function done(err, result) {
20388             entitiesLoaded(err, result);
20389             if (callback) callback(err, result);
20390         }
20391         connection.loadEntity(id, done);
20392     };
20393
20394     context.zoomToEntity = function(id, zoomTo) {
20395         if (zoomTo !== false) {
20396             this.loadEntity(id, function(err, result) {
20397                 if (err) return;
20398                 var entity = _.find(result.data, function(e) { return e.id === id; });
20399                 if (entity) { map.zoomTo(entity); }
20400             });
20401         }
20402
20403         map.on('drawn.zoomToEntity', function() {
20404             if (!context.hasEntity(id)) return;
20405             map.on('drawn.zoomToEntity', null);
20406             context.on('enter.zoomToEntity', null);
20407             context.enter(iD.modes.Select(context, [id]));
20408         });
20409
20410         context.on('enter.zoomToEntity', function() {
20411             if (mode.id !== 'browse') {
20412                 map.on('drawn.zoomToEntity', null);
20413                 context.on('enter.zoomToEntity', null);
20414             }
20415         });
20416     };
20417
20418     var minEditableZoom = 16;
20419     context.minEditableZoom = function(_) {
20420         if (!arguments.length) return minEditableZoom;
20421         minEditableZoom = _;
20422         connection.tileZoom(_);
20423         return context;
20424     };
20425
20426
20427     /* History */
20428     var inIntro = false;
20429     context.inIntro = function(_) {
20430         if (!arguments.length) return inIntro;
20431         inIntro = _;
20432         return context;
20433     };
20434
20435     context.save = function() {
20436         if (inIntro || (mode && mode.id === 'save') || d3.select('.modal').size()) return;
20437         history.save();
20438         if (history.hasChanges()) return t('save.unsaved_changes');
20439     };
20440
20441     context.flush = function() {
20442         context.debouncedSave.cancel();
20443         connection.flush();
20444         features.reset();
20445         history.reset();
20446         _.each(iD.services, function(service) {
20447             var reset = service().reset;
20448             if (reset) reset(context);
20449         });
20450         return context;
20451     };
20452
20453
20454     /* Graph */
20455     context.hasEntity = function(id) {
20456         return history.graph().hasEntity(id);
20457     };
20458     context.entity = function(id) {
20459         return history.graph().entity(id);
20460     };
20461     context.childNodes = function(way) {
20462         return history.graph().childNodes(way);
20463     };
20464     context.geometry = function(id) {
20465         return context.entity(id).geometry(history.graph());
20466     };
20467
20468
20469     /* Modes */
20470     var mode;
20471     context.mode = function() {
20472         return mode;
20473     };
20474     context.enter = function(newMode) {
20475         if (mode) {
20476             mode.exit();
20477             dispatch.exit(mode);
20478         }
20479
20480         mode = newMode;
20481         mode.enter();
20482         dispatch.enter(mode);
20483     };
20484
20485     context.selectedIDs = function() {
20486         if (mode && mode.selectedIDs) {
20487             return mode.selectedIDs();
20488         } else {
20489             return [];
20490         }
20491     };
20492
20493
20494     /* Behaviors */
20495     context.install = function(behavior) {
20496         context.surface().call(behavior);
20497     };
20498     context.uninstall = function(behavior) {
20499         context.surface().call(behavior.off);
20500     };
20501
20502
20503     /* Copy/Paste */
20504     var copyIDs = [], copyGraph;
20505     context.copyGraph = function() { return copyGraph; };
20506     context.copyIDs = function(_) {
20507         if (!arguments.length) return copyIDs;
20508         copyIDs = _;
20509         copyGraph = history.graph();
20510         return context;
20511     };
20512
20513
20514     /* Background */
20515     var background;
20516     context.background = function() { return background; };
20517
20518
20519     /* Features */
20520     var features;
20521     context.features = function() { return features; };
20522     context.hasHiddenConnections = function(id) {
20523         var graph = history.graph(),
20524             entity = graph.entity(id);
20525         return features.hasHiddenConnections(entity, graph);
20526     };
20527
20528
20529     /* Map */
20530     var map;
20531     context.map = function() { return map; };
20532     context.layers = function() { return map.layers; };
20533     context.surface = function() { return map.surface; };
20534     context.editable = function() { return map.editable(); };
20535
20536     context.surfaceRect = function() {
20537         // Work around a bug in Firefox.
20538         //   http://stackoverflow.com/questions/18153989/
20539         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
20540         return context.surface().node().parentNode.getBoundingClientRect();
20541     };
20542
20543
20544     /* Debug */
20545     var debugFlags = {
20546         tile: false,
20547         collision: false,
20548         imagery: false,
20549         imperial: false,
20550         driveLeft: false
20551     };
20552     context.debugFlags = function() {
20553         return debugFlags;
20554     };
20555     context.setDebug = function(flag, val) {
20556         if (arguments.length === 1) val = true;
20557         debugFlags[flag] = val;
20558         dispatch.change();
20559         return context;
20560     };
20561     context.getDebug = function(flag) {
20562         return flag && debugFlags[flag];
20563     };
20564
20565
20566     /* Presets */
20567     var presets;
20568     context.presets = function(_) {
20569         if (!arguments.length) return presets;
20570         presets.load(_);
20571         iD.areaKeys = presets.areaKeys();
20572         return context;
20573     };
20574
20575
20576     /* Imagery */
20577     context.imagery = function(_) {
20578         background.load(_);
20579         return context;
20580     };
20581
20582
20583     /* Container */
20584     var container, embed;
20585     context.container = function(_) {
20586         if (!arguments.length) return container;
20587         container = _;
20588         container.classed('id-container', true);
20589         return context;
20590     };
20591     context.embed = function(_) {
20592         if (!arguments.length) return embed;
20593         embed = _;
20594         return context;
20595     };
20596
20597
20598     /* Taginfo */
20599     var taginfo;
20600     context.taginfo = function(_) {
20601         if (!arguments.length) return taginfo;
20602         taginfo = _;
20603         return context;
20604     };
20605
20606
20607     /* Assets */
20608     var assetPath = '';
20609     context.assetPath = function(_) {
20610         if (!arguments.length) return assetPath;
20611         assetPath = _;
20612         return context;
20613     };
20614
20615     var assetMap = {};
20616     context.assetMap = function(_) {
20617         if (!arguments.length) return assetMap;
20618         assetMap = _;
20619         return context;
20620     };
20621
20622     context.asset = function(_) {
20623         var filename = assetPath + _;
20624         return assetMap[filename] || filename;
20625     };
20626
20627     context.imagePath = function(_) {
20628         return context.asset('img/' + _);
20629     };
20630
20631     var locale, localePath;
20632     context.locale = function(loc, path) {
20633         locale = loc;
20634         localePath = path;
20635
20636         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
20637         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
20638             iD.detect().locale = loc;
20639         }
20640
20641         return context;
20642     };
20643
20644     context.loadLocale = function(cb) {
20645         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
20646             localePath = localePath || context.asset('locales/' + locale + '.json');
20647             d3.json(localePath, function(err, result) {
20648                 window.locale[locale] = result;
20649                 window.locale.current(locale);
20650                 cb();
20651             });
20652         } else {
20653             cb();
20654         }
20655     };
20656
20657
20658     /* Init */
20659
20660     context.projection = iD.geo.RawMercator();
20661
20662     locale = iD.detect().locale;
20663     if (locale && iD.data.locales.indexOf(locale) === -1) {
20664         locale = locale.split('-')[0];
20665     }
20666
20667     history = iD.History(context);
20668     context.graph = history.graph;
20669     context.changes = history.changes;
20670     context.intersects = history.intersects;
20671
20672     // Debounce save, since it's a synchronous localStorage write,
20673     // and history changes can happen frequently (e.g. when dragging).
20674     context.debouncedSave = _.debounce(context.save, 350);
20675     function withDebouncedSave(fn) {
20676         return function() {
20677             var result = fn.apply(history, arguments);
20678             context.debouncedSave();
20679             return result;
20680         };
20681     }
20682
20683     context.perform = withDebouncedSave(history.perform);
20684     context.replace = withDebouncedSave(history.replace);
20685     context.pop = withDebouncedSave(history.pop);
20686     context.overwrite = withDebouncedSave(history.overwrite);
20687     context.undo = withDebouncedSave(history.undo);
20688     context.redo = withDebouncedSave(history.redo);
20689
20690     ui = iD.ui(context);
20691
20692     connection = iD.Connection();
20693
20694     background = iD.Background(context);
20695
20696     features = iD.Features(context);
20697
20698     map = iD.Map(context);
20699     context.mouse = map.mouse;
20700     context.extent = map.extent;
20701     context.pan = map.pan;
20702     context.zoomIn = map.zoomIn;
20703     context.zoomOut = map.zoomOut;
20704     context.zoomInFurther = map.zoomInFurther;
20705     context.zoomOutFurther = map.zoomOutFurther;
20706     context.redrawEnable = map.redrawEnable;
20707
20708     presets = iD.presets();
20709
20710     return d3.rebind(context, dispatch, 'on');
20711 };
20712
20713
20714 iD.version = '1.9.7';
20715
20716 (function() {
20717     var detected = {};
20718
20719     var ua = navigator.userAgent,
20720         m = null;
20721
20722     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
20723     if (m !== null) {
20724         detected.browser = m[1];
20725         detected.version = m[2];
20726     }
20727     if (!detected.browser) {
20728         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
20729         if (m !== null) {
20730             detected.browser = 'msie';
20731             detected.version = m[1];
20732         }
20733     }
20734     if (!detected.browser) {
20735         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
20736         if (m !== null) {
20737             detected.browser = 'Opera';
20738             detected.version = m[2];
20739         }
20740     }
20741     if (!detected.browser) {
20742         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
20743         if (m !== null) {
20744             detected.browser = m[1];
20745             detected.version = m[2];
20746             m = ua.match(/version\/([\.\d]+)/i);
20747             if (m !== null) detected.version = m[1];
20748         }
20749     }
20750     if (!detected.browser) {
20751         detected.browser = navigator.appName;
20752         detected.version = navigator.appVersion;
20753     }
20754
20755     // keep major.minor version only..
20756     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
20757
20758     if (detected.browser.toLowerCase() === 'msie') {
20759         detected.ie = true;
20760         detected.browser = 'Internet Explorer';
20761         detected.support = parseFloat(detected.version) >= 11;
20762     } else {
20763         detected.ie = false;
20764         detected.support = true;
20765     }
20766
20767     // Added due to incomplete svg style support. See #715
20768     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
20769
20770     detected.locale = (navigator.languages && navigator.languages.length)
20771         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
20772
20773     detected.filedrop = (window.FileReader && 'ondrop' in window);
20774
20775     function nav(x) {
20776         return navigator.userAgent.indexOf(x) !== -1;
20777     }
20778
20779     if (nav('Win')) {
20780         detected.os = 'win';
20781         detected.platform = 'Windows';
20782     }
20783     else if (nav('Mac')) {
20784         detected.os = 'mac';
20785         detected.platform = 'Macintosh';
20786     }
20787     else if (nav('X11') || nav('Linux')) {
20788         detected.os = 'linux';
20789         detected.platform = 'Linux';
20790     }
20791     else {
20792         detected.os = 'win';
20793         detected.platform = 'Unknown';
20794     }
20795
20796     iD.detect = function() { return detected; };
20797 })();
20798 iD.services = {};
20799 iD.services.mapillary = function() {
20800     var mapillary = {},
20801         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
20802         apibase = 'https://a.mapillary.com/v2/',
20803         viewercss = 'https://npmcdn.com/mapillary-js@1.3.0/dist/mapillary-js.min.css',
20804         viewerjs = 'https://npmcdn.com/mapillary-js@1.3.0/dist/mapillary-js.min.js',
20805         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
20806         maxResults = 1000,
20807         maxPages = 10,
20808         tileZoom = 14;
20809
20810
20811     function loadSignStyles(context) {
20812         d3.select('head').selectAll('#traffico')
20813             .data([0])
20814             .enter()
20815             .append('link')
20816             .attr('id', 'traffico')
20817             .attr('rel', 'stylesheet')
20818             .attr('href', context.asset('traffico/stylesheets/traffico.css'));
20819     }
20820
20821     function loadSignDefs(context) {
20822         if (iD.services.mapillary.sign_defs) return;
20823         iD.services.mapillary.sign_defs = {};
20824
20825         _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
20826             d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
20827                 if (err) return;
20828                 if (region === 'de') region = 'eu';
20829                 iD.services.mapillary.sign_defs[region] = data;
20830             });
20831         });
20832     }
20833
20834     function loadViewer() {
20835         // mapillary-wrap
20836         var wrap = d3.select('#content').selectAll('.mapillary-wrap')
20837             .data([0]);
20838
20839         var enter = wrap.enter().append('div')
20840             .attr('class', 'mapillary-wrap')
20841             .classed('al', true)       // 'al'=left,  'ar'=right
20842             .classed('hidden', true);
20843
20844         enter.append('button')
20845             .attr('class', 'thumb-hide')
20846             .on('click', function () { mapillary.hideViewer(); })
20847             .append('div')
20848             .call(iD.svg.Icon('#icon-close'));
20849
20850         enter.append('div')
20851             .attr('id', 'mly')
20852             .attr('class', 'mly-wrapper')
20853             .classed('active', false);
20854
20855         // mapillary-viewercss
20856         d3.select('head').selectAll('#mapillary-viewercss')
20857             .data([0])
20858             .enter()
20859             .append('link')
20860             .attr('id', 'mapillary-viewercss')
20861             .attr('rel', 'stylesheet')
20862             .attr('href', viewercss);
20863
20864         // mapillary-viewerjs
20865         d3.select('head').selectAll('#mapillary-viewerjs')
20866             .data([0])
20867             .enter()
20868             .append('script')
20869             .attr('id', 'mapillary-viewerjs')
20870             .attr('src', viewerjs);
20871     }
20872
20873     function initViewer(imageKey, context) {
20874
20875         function nodeChanged(d) {
20876             var clicks = iD.services.mapillary.clicks;
20877             var index = clicks.indexOf(d.key);
20878             if (index > -1) {    // nodechange initiated from clicking on a marker..
20879                 clicks.splice(index, 1);
20880             } else {             // nodechange initiated from the Mapillary viewer controls..
20881                 var loc = d.apiNavImIm ? [d.apiNavImIm.lon, d.apiNavImIm.lat] : [d.latLon.lon, d.latLon.lat];
20882                 context.map().centerEase(loc);
20883                 mapillary.setSelectedImage(d.key, false);
20884             }
20885         }
20886
20887         if (Mapillary && imageKey) {
20888             var opts = {
20889                 baseImageSize: 320,
20890                 cover: false,
20891                 cache: true,
20892                 debug: false,
20893                 imagePlane: true,
20894                 loading: true,
20895                 sequence: true
20896             };
20897
20898             var viewer = new Mapillary.Viewer('mly', clientId, imageKey, opts);
20899             viewer.on('nodechanged', nodeChanged);
20900             viewer.on('loadingchanged', mapillary.setViewerLoading);
20901             iD.services.mapillary.viewer = viewer;
20902         }
20903     }
20904
20905     function abortRequest(i) {
20906         i.abort();
20907     }
20908
20909     function nearNullIsland(x, y, z) {
20910         if (z >= 7) {
20911             var center = Math.pow(2, z - 1),
20912                 width = Math.pow(2, z - 6),
20913                 min = center - (width / 2),
20914                 max = center + (width / 2) - 1;
20915             return x >= min && x <= max && y >= min && y <= max;
20916         }
20917         return false;
20918     }
20919
20920     function getTiles(projection, dimensions) {
20921         var s = projection.scale() * 2 * Math.PI,
20922             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
20923             ts = 256 * Math.pow(2, z - tileZoom),
20924             origin = [
20925                 s / 2 - projection.translate()[0],
20926                 s / 2 - projection.translate()[1]];
20927
20928         return d3.geo.tile()
20929             .scaleExtent([tileZoom, tileZoom])
20930             .scale(s)
20931             .size(dimensions)
20932             .translate(projection.translate())()
20933             .map(function(tile) {
20934                 var x = tile[0] * ts - origin[0],
20935                     y = tile[1] * ts - origin[1];
20936
20937                 return {
20938                     id: tile.toString(),
20939                     extent: iD.geo.Extent(
20940                         projection.invert([x, y + ts]),
20941                         projection.invert([x + ts, y]))
20942                 };
20943             });
20944     }
20945
20946
20947     function loadTiles(which, url, projection, dimensions) {
20948         var tiles = getTiles(projection, dimensions).filter(function(t) {
20949               var xyz = t.id.split(',');
20950               return !nearNullIsland(xyz[0], xyz[1], xyz[2]);
20951             });
20952
20953         _.filter(which.inflight, function(v, k) {
20954             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
20955             if (!wanted) delete which.inflight[k];
20956             return !wanted;
20957         }).map(abortRequest);
20958
20959         tiles.forEach(function(tile) {
20960             loadTilePage(which, url, tile, 0);
20961         });
20962     }
20963
20964     function loadTilePage(which, url, tile, page) {
20965         var cache = iD.services.mapillary.cache[which],
20966             id = tile.id + ',' + String(page),
20967             rect = tile.extent.rectangle();
20968
20969         if (cache.loaded[id] || cache.inflight[id]) return;
20970
20971         cache.inflight[id] = d3.json(url +
20972             iD.util.qsString({
20973                 geojson: 'true',
20974                 limit: maxResults,
20975                 page: page,
20976                 client_id: clientId,
20977                 min_lon: rect[0],
20978                 min_lat: rect[1],
20979                 max_lon: rect[2],
20980                 max_lat: rect[3]
20981             }), function(err, data) {
20982                 cache.loaded[id] = true;
20983                 delete cache.inflight[id];
20984                 if (err || !data.features || !data.features.length) return;
20985
20986                 var features = [],
20987                     nextPage = page + 1,
20988                     feature, loc, d;
20989
20990                 for (var i = 0; i < data.features.length; i++) {
20991                     feature = data.features[i];
20992                     loc = feature.geometry.coordinates;
20993                     d = { key: feature.properties.key, loc: loc };
20994                     if (which === 'images') d.ca = feature.properties.ca;
20995                     if (which === 'signs') d.signs = feature.properties.rects;
20996
20997                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
20998                 }
20999
21000                 cache.rtree.load(features);
21001
21002                 if (which === 'images') dispatch.loadedImages();
21003                 if (which === 'signs') dispatch.loadedSigns();
21004
21005                 if (data.features.length === maxResults && nextPage < maxPages) {
21006                     loadTilePage(which, url, tile, nextPage);
21007                 }
21008             }
21009         );
21010     }
21011
21012     mapillary.loadImages = function(projection, dimensions) {
21013         var url = apibase + 'search/im/geojson?';
21014         loadTiles('images', url, projection, dimensions);
21015     };
21016
21017     mapillary.loadSigns = function(context, projection, dimensions) {
21018         var url = apibase + 'search/im/geojson/or?';
21019         loadSignStyles(context);
21020         loadSignDefs(context);
21021         loadTiles('signs', url, projection, dimensions);
21022     };
21023
21024     mapillary.loadViewer = function() {
21025         loadViewer();
21026     };
21027
21028
21029     // partition viewport into `psize` x `psize` regions
21030     function partitionViewport(psize, projection, dimensions) {
21031         psize = psize || 16;
21032         var cols = d3.range(0, dimensions[0], psize),
21033             rows = d3.range(0, dimensions[1], psize),
21034             partitions = [];
21035
21036         rows.forEach(function(y) {
21037             cols.forEach(function(x) {
21038                 var min = [x, y + psize],
21039                     max = [x + psize, y];
21040                 partitions.push(
21041                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
21042             });
21043         });
21044
21045         return partitions;
21046     }
21047
21048     // no more than `limit` results per partition.
21049     function searchLimited(psize, limit, projection, dimensions, rtree) {
21050         limit = limit || 3;
21051
21052         var partitions = partitionViewport(psize, projection, dimensions);
21053         return _.flatten(_.compact(_.map(partitions, function(extent) {
21054             return rtree.search(extent.rectangle())
21055                 .slice(0, limit)
21056                 .map(function(d) { return d[4]; });
21057         })));
21058     }
21059
21060     mapillary.images = function(projection, dimensions) {
21061         var psize = 16, limit = 3;
21062         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
21063     };
21064
21065     mapillary.signs = function(projection, dimensions) {
21066         var psize = 32, limit = 3;
21067         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
21068     };
21069
21070     mapillary.signsSupported = function() {
21071         var detected = iD.detect();
21072         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
21073     };
21074
21075     mapillary.signHTML = function(d) {
21076         if (!iD.services.mapillary.sign_defs) return;
21077
21078         var detectionPackage = d.signs[0].package,
21079             type = d.signs[0].type,
21080             country = detectionPackage.split('_')[1];
21081
21082         return iD.services.mapillary.sign_defs[country][type];
21083     };
21084
21085     mapillary.showViewer = function() {
21086         d3.select('#content')
21087             .selectAll('.mapillary-wrap')
21088             .classed('hidden', false)
21089             .selectAll('.mly-wrapper')
21090             .classed('active', true);
21091
21092         return mapillary;
21093     };
21094
21095     mapillary.hideViewer = function() {
21096         d3.select('#content')
21097             .selectAll('.mapillary-wrap')
21098             .classed('hidden', true)
21099             .selectAll('.mly-wrapper')
21100             .classed('active', false);
21101
21102         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
21103             .classed('selected', false);
21104
21105         iD.services.mapillary.image = null;
21106
21107         return mapillary;
21108     };
21109
21110     mapillary.setViewerLoading = function(loading) {
21111         var canvas = d3.select('#content')
21112             .selectAll('.mly-wrapper canvas');
21113
21114         if (canvas.empty()) return;   // viewer not loaded yet
21115
21116         var cover = d3.select('#content')
21117             .selectAll('.mly-wrapper .Cover');
21118
21119         cover.classed('CoverDone', !loading);
21120
21121         var button = cover.selectAll('.CoverButton')
21122             .data(loading ? [0] : []);
21123
21124         button.enter()
21125             .append('div')
21126             .attr('class', 'CoverButton')
21127             .append('div')
21128             .attr('class', 'uil-ripple-css')
21129             .append('div');
21130
21131         button.exit()
21132             .remove();
21133
21134         return mapillary;
21135     };
21136
21137     mapillary.updateViewer = function(imageKey, context) {
21138         if (!iD.services.mapillary) return;
21139         if (!imageKey) return;
21140
21141         if (!iD.services.mapillary.viewer) {
21142             initViewer(imageKey, context);
21143         } else {
21144             iD.services.mapillary.viewer.moveToKey(imageKey);
21145         }
21146
21147         return mapillary;
21148     };
21149
21150     mapillary.getSelectedImage = function() {
21151         if (!iD.services.mapillary) return null;
21152         return iD.services.mapillary.image;
21153     };
21154
21155     mapillary.setSelectedImage = function(imageKey, fromClick) {
21156         if (!iD.services.mapillary) return null;
21157
21158         iD.services.mapillary.image = imageKey;
21159         if (fromClick) {
21160             iD.services.mapillary.clicks.push(imageKey);
21161         }
21162
21163         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
21164             .classed('selected', function(d) { return d.key === imageKey; });
21165
21166         return mapillary;
21167     };
21168
21169     mapillary.reset = function() {
21170         var cache = iD.services.mapillary.cache;
21171
21172         if (cache) {
21173             _.forEach(cache.images.inflight, abortRequest);
21174             _.forEach(cache.signs.inflight, abortRequest);
21175         }
21176
21177         iD.services.mapillary.cache = {
21178             images: { inflight: {}, loaded: {}, rtree: rbush() },
21179             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
21180         };
21181
21182         iD.services.mapillary.image = null;
21183         iD.services.mapillary.clicks = [];
21184
21185         return mapillary;
21186     };
21187
21188
21189     if (!iD.services.mapillary.cache) {
21190         mapillary.reset();
21191     }
21192
21193     return d3.rebind(mapillary, dispatch, 'on');
21194 };
21195 iD.services.nominatim  = function() {
21196     var nominatim = {},
21197         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
21198
21199
21200     nominatim.countryCode = function(location, callback) {
21201         var cache = iD.services.nominatim.cache,
21202             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
21203
21204         if (countryCodes.length > 0)
21205             return callback(null, countryCodes[0][4]);
21206
21207         d3.json(endpoint +
21208             iD.util.qsString({
21209                 format: 'json',
21210                 addressdetails: 1,
21211                 lat: location[1],
21212                 lon: location[0]
21213             }), function(err, result) {
21214                 if (err)
21215                     return callback(err);
21216                 else if (result && result.error)
21217                     return callback(result.error);
21218
21219                 var extent = iD.geo.Extent(location).padByMeters(1000);
21220
21221                 cache.insert(extent.rectangle().concat(result.address.country_code));
21222
21223                 callback(null, result.address.country_code);
21224             });
21225     };
21226
21227     nominatim.reset = function() {
21228         iD.services.nominatim.cache = rbush();
21229         return nominatim;
21230     };
21231
21232
21233     if (!iD.services.nominatim.cache) {
21234         nominatim.reset();
21235     }
21236
21237     return nominatim;
21238 };
21239 iD.services.taginfo = function() {
21240     var taginfo = {},
21241         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
21242         tag_sorts = {
21243             point: 'count_nodes',
21244             vertex: 'count_nodes',
21245             area: 'count_ways',
21246             line: 'count_ways'
21247         },
21248         tag_filters = {
21249             point: 'nodes',
21250             vertex: 'nodes',
21251             area: 'ways',
21252             line: 'ways'
21253         };
21254
21255
21256     function sets(parameters, n, o) {
21257         if (parameters.geometry && o[parameters.geometry]) {
21258             parameters[n] = o[parameters.geometry];
21259         }
21260         return parameters;
21261     }
21262
21263     function setFilter(parameters) {
21264         return sets(parameters, 'filter', tag_filters);
21265     }
21266
21267     function setSort(parameters) {
21268         return sets(parameters, 'sortname', tag_sorts);
21269     }
21270
21271     function clean(parameters) {
21272         return _.omit(parameters, 'geometry', 'debounce');
21273     }
21274
21275     function filterKeys(type) {
21276         var count_type = type ? 'count_' + type : 'count_all';
21277         return function(d) {
21278             return parseFloat(d[count_type]) > 2500 || d.in_wiki;
21279         };
21280     }
21281
21282     function filterMultikeys() {
21283         return function(d) {
21284             return (d.key.match(/:/g) || []).length === 1;  // exactly one ':'
21285         };
21286     }
21287
21288     function filterValues() {
21289         return function(d) {
21290             if (d.value.match(/[A-Z*;,]/) !== null) return false;  // exclude some punctuation, uppercase letters
21291             return parseFloat(d.fraction) > 0.0 || d.in_wiki;
21292         };
21293     }
21294
21295     function valKey(d) {
21296         return {
21297             value: d.key,
21298             title: d.key
21299         };
21300     }
21301
21302     function valKeyDescription(d) {
21303         return {
21304             value: d.value,
21305             title: d.description || d.value
21306         };
21307     }
21308
21309     // sort keys with ':' lower than keys without ':'
21310     function sortKeys(a, b) {
21311         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
21312             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
21313             : 0;
21314     }
21315
21316     var debounced = _.debounce(d3.json, 100, true);
21317
21318     function request(url, debounce, callback) {
21319         var cache = iD.services.taginfo.cache;
21320
21321         if (cache[url]) {
21322             callback(null, cache[url]);
21323         } else if (debounce) {
21324             debounced(url, done);
21325         } else {
21326             d3.json(url, done);
21327         }
21328
21329         function done(err, data) {
21330             if (!err) cache[url] = data;
21331             callback(err, data);
21332         }
21333     }
21334
21335     taginfo.keys = function(parameters, callback) {
21336         var debounce = parameters.debounce;
21337         parameters = clean(setSort(parameters));
21338         request(endpoint + 'keys/all?' +
21339             iD.util.qsString(_.extend({
21340                 rp: 10,
21341                 sortname: 'count_all',
21342                 sortorder: 'desc',
21343                 page: 1
21344             }, parameters)), debounce, function(err, d) {
21345                 if (err) return callback(err);
21346                 var f = filterKeys(parameters.filter);
21347                 callback(null, d.data.filter(f).sort(sortKeys).map(valKey));
21348             });
21349     };
21350
21351     taginfo.multikeys = function(parameters, callback) {
21352         var debounce = parameters.debounce;
21353         parameters = clean(setSort(parameters));
21354         request(endpoint + 'keys/all?' +
21355             iD.util.qsString(_.extend({
21356                 rp: 25,
21357                 sortname: 'count_all',
21358                 sortorder: 'desc',
21359                 page: 1
21360             }, parameters)), debounce, function(err, d) {
21361                 if (err) return callback(err);
21362                 var f = filterMultikeys();
21363                 callback(null, d.data.filter(f).map(valKey));
21364             });
21365     };
21366
21367     taginfo.values = function(parameters, callback) {
21368         var debounce = parameters.debounce;
21369         parameters = clean(setSort(setFilter(parameters)));
21370         request(endpoint + 'key/values?' +
21371             iD.util.qsString(_.extend({
21372                 rp: 25,
21373                 sortname: 'count_all',
21374                 sortorder: 'desc',
21375                 page: 1
21376             }, parameters)), debounce, function(err, d) {
21377                 if (err) return callback(err);
21378                 var f = filterValues();
21379                 callback(null, d.data.filter(f).map(valKeyDescription));
21380             });
21381     };
21382
21383     taginfo.docs = function(parameters, callback) {
21384         var debounce = parameters.debounce;
21385         parameters = clean(setSort(parameters));
21386
21387         var path = 'key/wiki_pages?';
21388         if (parameters.value) path = 'tag/wiki_pages?';
21389         else if (parameters.rtype) path = 'relation/wiki_pages?';
21390
21391         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
21392             if (err) return callback(err);
21393             callback(null, d.data);
21394         });
21395     };
21396
21397     taginfo.endpoint = function(_) {
21398         if (!arguments.length) return endpoint;
21399         endpoint = _;
21400         return taginfo;
21401     };
21402
21403     taginfo.reset = function() {
21404         iD.services.taginfo.cache = {};
21405         return taginfo;
21406     };
21407
21408
21409     if (!iD.services.taginfo.cache) {
21410         taginfo.reset();
21411     }
21412
21413     return taginfo;
21414 };
21415 iD.services.wikidata = function() {
21416     var wiki = {},
21417         endpoint = 'https://www.wikidata.org/w/api.php?';
21418
21419     // Given a Wikipedia language and article title, return an array of
21420     // corresponding Wikidata entities.
21421     wiki.itemsByTitle = function(lang, title, callback) {
21422         lang = lang || 'en';
21423         d3.jsonp(endpoint + iD.util.qsString({
21424             action: 'wbgetentities',
21425             format: 'json',
21426             sites: lang.replace(/-/g, '_') + 'wiki',
21427             titles: title,
21428             languages: 'en', // shrink response by filtering to one language
21429             callback: '{callback}'
21430         }), function(data) {
21431             callback(title, data.entities || {});
21432         });
21433     };
21434
21435     return wiki;
21436 };
21437 iD.services.wikipedia = function() {
21438     var wiki = {},
21439         endpoint = 'https://en.wikipedia.org/w/api.php?';
21440
21441     wiki.search = function(lang, query, callback) {
21442         lang = lang || 'en';
21443         d3.jsonp(endpoint.replace('en', lang) +
21444             iD.util.qsString({
21445                 action: 'query',
21446                 list: 'search',
21447                 srlimit: '10',
21448                 srinfo: 'suggestion',
21449                 format: 'json',
21450                 callback: '{callback}',
21451                 srsearch: query
21452             }), function(data) {
21453                 if (!data.query) return;
21454                 callback(query, data.query.search.map(function(d) {
21455                     return d.title;
21456                 }));
21457             });
21458     };
21459
21460     wiki.suggestions = function(lang, query, callback) {
21461         lang = lang || 'en';
21462         d3.jsonp(endpoint.replace('en', lang) +
21463             iD.util.qsString({
21464                 action: 'opensearch',
21465                 namespace: 0,
21466                 suggest: '',
21467                 format: 'json',
21468                 callback: '{callback}',
21469                 search: query
21470             }), function(d) {
21471                 callback(d[0], d[1]);
21472             });
21473     };
21474
21475     wiki.translations = function(lang, title, callback) {
21476         d3.jsonp(endpoint.replace('en', lang) +
21477             iD.util.qsString({
21478                 action: 'query',
21479                 prop: 'langlinks',
21480                 format: 'json',
21481                 callback: '{callback}',
21482                 lllimit: 500,
21483                 titles: title
21484             }), function(d) {
21485                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
21486                     translations = {};
21487                 if (list && list.langlinks) {
21488                     list.langlinks.forEach(function(d) {
21489                         translations[d.lang] = d['*'];
21490                     });
21491                     callback(translations);
21492                 }
21493             });
21494     };
21495
21496     return wiki;
21497 };
21498 iD.util = {};
21499
21500 iD.util.tagText = function(entity) {
21501     return d3.entries(entity.tags).map(function(e) {
21502         return e.key + '=' + e.value;
21503     }).join(', ');
21504 };
21505
21506 iD.util.entitySelector = function(ids) {
21507     return ids.length ? '.' + ids.join(',.') : 'nothing';
21508 };
21509
21510 iD.util.entityOrMemberSelector = function(ids, graph) {
21511     var s = iD.util.entitySelector(ids);
21512
21513     ids.forEach(function(id) {
21514         var entity = graph.hasEntity(id);
21515         if (entity && entity.type === 'relation') {
21516             entity.members.forEach(function(member) {
21517                 s += ',.' + member.id;
21518             });
21519         }
21520     });
21521
21522     return s;
21523 };
21524
21525 iD.util.displayName = function(entity) {
21526     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
21527     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
21528 };
21529
21530 iD.util.displayType = function(id) {
21531     return {
21532         n: t('inspector.node'),
21533         w: t('inspector.way'),
21534         r: t('inspector.relation')
21535     }[id.charAt(0)];
21536 };
21537
21538 iD.util.stringQs = function(str) {
21539     return str.split('&').reduce(function(obj, pair){
21540         var parts = pair.split('=');
21541         if (parts.length === 2) {
21542             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
21543         }
21544         return obj;
21545     }, {});
21546 };
21547
21548 iD.util.qsString = function(obj, noencode) {
21549     function softEncode(s) {
21550       // encode everything except special characters used in certain hash parameters:
21551       // "/" in map states, ":", ",", {" and "}" in background
21552       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
21553     }
21554     return Object.keys(obj).sort().map(function(key) {
21555         return encodeURIComponent(key) + '=' + (
21556             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
21557     }).join('&');
21558 };
21559
21560 iD.util.prefixDOMProperty = function(property) {
21561     var prefixes = ['webkit', 'ms', 'moz', 'o'],
21562         i = -1,
21563         n = prefixes.length,
21564         s = document.body;
21565
21566     if (property in s)
21567         return property;
21568
21569     property = property.substr(0, 1).toUpperCase() + property.substr(1);
21570
21571     while (++i < n)
21572         if (prefixes[i] + property in s)
21573             return prefixes[i] + property;
21574
21575     return false;
21576 };
21577
21578 iD.util.prefixCSSProperty = function(property) {
21579     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
21580         i = -1,
21581         n = prefixes.length,
21582         s = document.body.style;
21583
21584     if (property.toLowerCase() in s)
21585         return property.toLowerCase();
21586
21587     while (++i < n)
21588         if (prefixes[i] + property in s)
21589             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
21590
21591     return false;
21592 };
21593
21594
21595 iD.util.setTransform = function(el, x, y, scale) {
21596     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
21597         translate = iD.detect().opera ?
21598             'translate('   + x + 'px,' + y + 'px)' :
21599             'translate3d(' + x + 'px,' + y + 'px,0)';
21600     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
21601 };
21602
21603 iD.util.getStyle = function(selector) {
21604     for (var i = 0; i < document.styleSheets.length; i++) {
21605         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
21606         for (var k = 0; k < rules.length; k++) {
21607             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
21608             if (_.includes(selectorText, selector)) {
21609                 return rules[k];
21610             }
21611         }
21612     }
21613 };
21614
21615 iD.util.editDistance = function(a, b) {
21616     if (a.length === 0) return b.length;
21617     if (b.length === 0) return a.length;
21618     var matrix = [];
21619     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
21620     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
21621     for (i = 1; i <= b.length; i++) {
21622         for (j = 1; j <= a.length; j++) {
21623             if (b.charAt(i-1) === a.charAt(j-1)) {
21624                 matrix[i][j] = matrix[i-1][j-1];
21625             } else {
21626                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
21627                     Math.min(matrix[i][j-1] + 1, // insertion
21628                     matrix[i-1][j] + 1)); // deletion
21629             }
21630         }
21631     }
21632     return matrix[b.length][a.length];
21633 };
21634
21635 // a d3.mouse-alike which
21636 // 1. Only works on HTML elements, not SVG
21637 // 2. Does not cause style recalculation
21638 iD.util.fastMouse = function(container) {
21639     var rect = container.getBoundingClientRect(),
21640         rectLeft = rect.left,
21641         rectTop = rect.top,
21642         clientLeft = +container.clientLeft,
21643         clientTop = +container.clientTop;
21644     return function(e) {
21645         return [
21646             e.clientX - rectLeft - clientLeft,
21647             e.clientY - rectTop - clientTop];
21648     };
21649 };
21650
21651 /* eslint-disable no-proto */
21652 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
21653 /* eslint-enable no-proto */
21654
21655 iD.util.asyncMap = function(inputs, func, callback) {
21656     var remaining = inputs.length,
21657         results = [],
21658         errors = [];
21659
21660     inputs.forEach(function(d, i) {
21661         func(d, function done(err, data) {
21662             errors[i] = err;
21663             results[i] = data;
21664             remaining--;
21665             if (!remaining) callback(errors, results);
21666         });
21667     });
21668 };
21669
21670 // wraps an index to an interval [0..length-1]
21671 iD.util.wrap = function(index, length) {
21672     if (index < 0)
21673         index += Math.ceil(-index/length)*length;
21674     return index % length;
21675 };
21676 // A per-domain session mutex backed by a cookie and dead man's
21677 // switch. If the session crashes, the mutex will auto-release
21678 // after 5 seconds.
21679
21680 iD.util.SessionMutex = function(name) {
21681     var mutex = {},
21682         intervalID;
21683
21684     function renew() {
21685         var expires = new Date();
21686         expires.setSeconds(expires.getSeconds() + 5);
21687         document.cookie = name + '=1; expires=' + expires.toUTCString();
21688     }
21689
21690     mutex.lock = function() {
21691         if (intervalID) return true;
21692         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
21693         if (cookie) return false;
21694         renew();
21695         intervalID = window.setInterval(renew, 4000);
21696         return true;
21697     };
21698
21699     mutex.unlock = function() {
21700         if (!intervalID) return;
21701         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
21702         clearInterval(intervalID);
21703         intervalID = null;
21704     };
21705
21706     mutex.locked = function() {
21707         return !!intervalID;
21708     };
21709
21710     return mutex;
21711 };
21712 iD.util.SuggestNames = function(preset, suggestions) {
21713     preset = preset.id.split('/', 2);
21714     var k = preset[0],
21715         v = preset[1];
21716
21717     return function(value, callback) {
21718         var result = [];
21719         if (value && value.length > 2) {
21720             if (suggestions[k] && suggestions[k][v]) {
21721                 for (var sugg in suggestions[k][v]) {
21722                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
21723                     if (dist < 3) {
21724                         result.push({
21725                             title: sugg,
21726                             value: sugg,
21727                             dist: dist
21728                         });
21729                     }
21730                 }
21731             }
21732             result.sort(function(a, b) {
21733                 return a.dist - b.dist;
21734             });
21735         }
21736         result = result.slice(0,3);
21737         callback(result);
21738     };
21739 };
21740 iD.geo = {};
21741
21742 iD.geo.roundCoords = function(c) {
21743     return [Math.floor(c[0]), Math.floor(c[1])];
21744 };
21745
21746 iD.geo.interp = function(p1, p2, t) {
21747     return [p1[0] + (p2[0] - p1[0]) * t,
21748             p1[1] + (p2[1] - p1[1]) * t];
21749 };
21750
21751 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
21752 // Returns a positive value, if OAB makes a counter-clockwise turn,
21753 // negative for clockwise turn, and zero if the points are collinear.
21754 iD.geo.cross = function(o, a, b) {
21755     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
21756 };
21757
21758 // http://jsperf.com/id-dist-optimization
21759 iD.geo.euclideanDistance = function(a, b) {
21760     var x = a[0] - b[0], y = a[1] - b[1];
21761     return Math.sqrt((x * x) + (y * y));
21762 };
21763
21764 // using WGS84 polar radius (6356752.314245179 m)
21765 // const = 2 * PI * r / 360
21766 iD.geo.latToMeters = function(dLat) {
21767     return dLat * 110946.257617;
21768 };
21769
21770 // using WGS84 equatorial radius (6378137.0 m)
21771 // const = 2 * PI * r / 360
21772 iD.geo.lonToMeters = function(dLon, atLat) {
21773     return Math.abs(atLat) >= 90 ? 0 :
21774         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
21775 };
21776
21777 // using WGS84 polar radius (6356752.314245179 m)
21778 // const = 2 * PI * r / 360
21779 iD.geo.metersToLat = function(m) {
21780     return m / 110946.257617;
21781 };
21782
21783 // using WGS84 equatorial radius (6378137.0 m)
21784 // const = 2 * PI * r / 360
21785 iD.geo.metersToLon = function(m, atLat) {
21786     return Math.abs(atLat) >= 90 ? 0 :
21787         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
21788 };
21789
21790 iD.geo.offsetToMeters = function(offset) {
21791     var equatRadius = 6356752.314245179,
21792         polarRadius = 6378137.0,
21793         tileSize = 256;
21794
21795     return [
21796         offset[0] * 2 * Math.PI * equatRadius / tileSize,
21797         -offset[1] * 2 * Math.PI * polarRadius / tileSize
21798     ];
21799 };
21800
21801 iD.geo.metersToOffset = function(meters) {
21802     var equatRadius = 6356752.314245179,
21803         polarRadius = 6378137.0,
21804         tileSize = 256;
21805
21806     return [
21807         meters[0] * tileSize / (2 * Math.PI * equatRadius),
21808         -meters[1] * tileSize / (2 * Math.PI * polarRadius)
21809     ];
21810 };
21811
21812 // Equirectangular approximation of spherical distances on Earth
21813 iD.geo.sphericalDistance = function(a, b) {
21814     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
21815         y = iD.geo.latToMeters(a[1] - b[1]);
21816     return Math.sqrt((x * x) + (y * y));
21817 };
21818
21819 iD.geo.edgeEqual = function(a, b) {
21820     return (a[0] === b[0] && a[1] === b[1]) ||
21821         (a[0] === b[1] && a[1] === b[0]);
21822 };
21823
21824 // Return the counterclockwise angle in the range (-pi, pi)
21825 // between the positive X axis and the line intersecting a and b.
21826 iD.geo.angle = function(a, b, projection) {
21827     a = projection(a.loc);
21828     b = projection(b.loc);
21829     return Math.atan2(b[1] - a[1], b[0] - a[0]);
21830 };
21831
21832 // Choose the edge with the minimal distance from `point` to its orthogonal
21833 // projection onto that edge, if such a projection exists, or the distance to
21834 // the closest vertex on that edge. Returns an object with the `index` of the
21835 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
21836 iD.geo.chooseEdge = function(nodes, point, projection) {
21837     var dist = iD.geo.euclideanDistance,
21838         points = nodes.map(function(n) { return projection(n.loc); }),
21839         min = Infinity,
21840         idx, loc;
21841
21842     function dot(p, q) {
21843         return p[0] * q[0] + p[1] * q[1];
21844     }
21845
21846     for (var i = 0; i < points.length - 1; i++) {
21847         var o = points[i],
21848             s = [points[i + 1][0] - o[0],
21849                  points[i + 1][1] - o[1]],
21850             v = [point[0] - o[0],
21851                  point[1] - o[1]],
21852             proj = dot(v, s) / dot(s, s),
21853             p;
21854
21855         if (proj < 0) {
21856             p = o;
21857         } else if (proj > 1) {
21858             p = points[i + 1];
21859         } else {
21860             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
21861         }
21862
21863         var d = dist(p, point);
21864         if (d < min) {
21865             min = d;
21866             idx = i + 1;
21867             loc = projection.invert(p);
21868         }
21869     }
21870
21871     return {
21872         index: idx,
21873         distance: min,
21874         loc: loc
21875     };
21876 };
21877
21878 // Return the intersection point of 2 line segments.
21879 // From https://github.com/pgkelley4/line-segments-intersect
21880 // This uses the vector cross product approach described below:
21881 //  http://stackoverflow.com/a/565282/786339
21882 iD.geo.lineIntersection = function(a, b) {
21883     function subtractPoints(point1, point2) {
21884         return [point1[0] - point2[0], point1[1] - point2[1]];
21885     }
21886     function crossProduct(point1, point2) {
21887         return point1[0] * point2[1] - point1[1] * point2[0];
21888     }
21889
21890     var p = [a[0][0], a[0][1]],
21891         p2 = [a[1][0], a[1][1]],
21892         q = [b[0][0], b[0][1]],
21893         q2 = [b[1][0], b[1][1]],
21894         r = subtractPoints(p2, p),
21895         s = subtractPoints(q2, q),
21896         uNumerator = crossProduct(subtractPoints(q, p), r),
21897         denominator = crossProduct(r, s);
21898
21899     if (uNumerator && denominator) {
21900         var u = uNumerator / denominator,
21901             t = crossProduct(subtractPoints(q, p), s) / denominator;
21902
21903         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
21904             return iD.geo.interp(p, p2, t);
21905         }
21906     }
21907
21908     return null;
21909 };
21910
21911 iD.geo.pathIntersections = function(path1, path2) {
21912     var intersections = [];
21913     for (var i = 0; i < path1.length - 1; i++) {
21914         for (var j = 0; j < path2.length - 1; j++) {
21915             var a = [ path1[i], path1[i+1] ],
21916                 b = [ path2[j], path2[j+1] ],
21917                 hit = iD.geo.lineIntersection(a, b);
21918             if (hit) intersections.push(hit);
21919         }
21920     }
21921     return intersections;
21922 };
21923
21924 // Return whether point is contained in polygon.
21925 //
21926 // `point` should be a 2-item array of coordinates.
21927 // `polygon` should be an array of 2-item arrays of coordinates.
21928 //
21929 // From https://github.com/substack/point-in-polygon.
21930 // ray-casting algorithm based on
21931 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
21932 //
21933 iD.geo.pointInPolygon = function(point, polygon) {
21934     var x = point[0],
21935         y = point[1],
21936         inside = false;
21937
21938     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
21939         var xi = polygon[i][0], yi = polygon[i][1];
21940         var xj = polygon[j][0], yj = polygon[j][1];
21941
21942         var intersect = ((yi > y) !== (yj > y)) &&
21943             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
21944         if (intersect) inside = !inside;
21945     }
21946
21947     return inside;
21948 };
21949
21950 iD.geo.polygonContainsPolygon = function(outer, inner) {
21951     return _.every(inner, function(point) {
21952         return iD.geo.pointInPolygon(point, outer);
21953     });
21954 };
21955
21956 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
21957     function testSegments(outer, inner) {
21958         for (var i = 0; i < outer.length - 1; i++) {
21959             for (var j = 0; j < inner.length - 1; j++) {
21960                 var a = [ outer[i], outer[i+1] ],
21961                     b = [ inner[j], inner[j+1] ];
21962                 if (iD.geo.lineIntersection(a, b)) return true;
21963             }
21964         }
21965         return false;
21966     }
21967
21968     function testPoints(outer, inner) {
21969         return _.some(inner, function(point) {
21970             return iD.geo.pointInPolygon(point, outer);
21971         });
21972     }
21973
21974    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
21975 };
21976
21977 iD.geo.pathLength = function(path) {
21978     var length = 0,
21979         dx, dy;
21980     for (var i = 0; i < path.length - 1; i++) {
21981         dx = path[i][0] - path[i + 1][0];
21982         dy = path[i][1] - path[i + 1][1];
21983         length += Math.sqrt(dx * dx + dy * dy);
21984     }
21985     return length;
21986 };
21987 iD.geo.Extent = function geoExtent(min, max) {
21988     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
21989     if (min instanceof iD.geo.Extent) {
21990         return min;
21991     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
21992         this[0] = min[0];
21993         this[1] = min[1];
21994     } else {
21995         this[0] = min        || [ Infinity,  Infinity];
21996         this[1] = max || min || [-Infinity, -Infinity];
21997     }
21998 };
21999
22000 iD.geo.Extent.prototype = new Array(2);
22001
22002 _.extend(iD.geo.Extent.prototype, {
22003     equals: function (obj) {
22004         return this[0][0] === obj[0][0] &&
22005             this[0][1] === obj[0][1] &&
22006             this[1][0] === obj[1][0] &&
22007             this[1][1] === obj[1][1];
22008     },
22009
22010     extend: function(obj) {
22011         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22012         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
22013                               Math.min(obj[0][1], this[0][1])],
22014                              [Math.max(obj[1][0], this[1][0]),
22015                               Math.max(obj[1][1], this[1][1])]);
22016     },
22017
22018     _extend: function(extent) {
22019         this[0][0] = Math.min(extent[0][0], this[0][0]);
22020         this[0][1] = Math.min(extent[0][1], this[0][1]);
22021         this[1][0] = Math.max(extent[1][0], this[1][0]);
22022         this[1][1] = Math.max(extent[1][1], this[1][1]);
22023     },
22024
22025     area: function() {
22026         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
22027     },
22028
22029     center: function() {
22030         return [(this[0][0] + this[1][0]) / 2,
22031                 (this[0][1] + this[1][1]) / 2];
22032     },
22033
22034     rectangle: function() {
22035         return [this[0][0], this[0][1], this[1][0], this[1][1]];
22036     },
22037
22038     polygon: function() {
22039         return [
22040             [this[0][0], this[0][1]],
22041             [this[0][0], this[1][1]],
22042             [this[1][0], this[1][1]],
22043             [this[1][0], this[0][1]],
22044             [this[0][0], this[0][1]]
22045         ];
22046     },
22047
22048     contains: function(obj) {
22049         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22050         return obj[0][0] >= this[0][0] &&
22051                obj[0][1] >= this[0][1] &&
22052                obj[1][0] <= this[1][0] &&
22053                obj[1][1] <= this[1][1];
22054     },
22055
22056     intersects: function(obj) {
22057         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22058         return obj[0][0] <= this[1][0] &&
22059                obj[0][1] <= this[1][1] &&
22060                obj[1][0] >= this[0][0] &&
22061                obj[1][1] >= this[0][1];
22062     },
22063
22064     intersection: function(obj) {
22065         if (!this.intersects(obj)) return new iD.geo.Extent();
22066         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
22067                                   Math.max(obj[0][1], this[0][1])],
22068                                  [Math.min(obj[1][0], this[1][0]),
22069                                   Math.min(obj[1][1], this[1][1])]);
22070     },
22071
22072     percentContainedIn: function(obj) {
22073         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22074         var a1 = this.intersection(obj).area(),
22075             a2 = this.area();
22076
22077         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
22078             return 0;
22079         } else {
22080             return a1 / a2;
22081         }
22082     },
22083
22084     padByMeters: function(meters) {
22085         var dLat = iD.geo.metersToLat(meters),
22086             dLon = iD.geo.metersToLon(meters, this.center()[1]);
22087         return iD.geo.Extent(
22088                 [this[0][0] - dLon, this[0][1] - dLat],
22089                 [this[1][0] + dLon, this[1][1] + dLat]);
22090     },
22091
22092     toParam: function() {
22093         return this.rectangle().join(',');
22094     }
22095
22096 });
22097 iD.geo.Turn = function(turn) {
22098     if (!(this instanceof iD.geo.Turn))
22099         return new iD.geo.Turn(turn);
22100     _.extend(this, turn);
22101 };
22102
22103 iD.geo.Intersection = function(graph, vertexId) {
22104     var vertex = graph.entity(vertexId),
22105         parentWays = graph.parentWays(vertex),
22106         coincident = [],
22107         highways = {};
22108
22109     function addHighway(way, adjacentNodeId) {
22110         if (highways[adjacentNodeId]) {
22111             coincident.push(adjacentNodeId);
22112         } else {
22113             highways[adjacentNodeId] = way;
22114         }
22115     }
22116
22117     // Pre-split ways that would need to be split in
22118     // order to add a restriction. The real split will
22119     // happen when the restriction is added.
22120     parentWays.forEach(function(way) {
22121         if (!way.tags.highway || way.isArea() || way.isDegenerate())
22122             return;
22123
22124         var isFirst = (vertexId === way.first()),
22125             isLast = (vertexId === way.last()),
22126             isAffix = (isFirst || isLast),
22127             isClosingNode = (isFirst && isLast);
22128
22129         if (isAffix && !isClosingNode) {
22130             var index = (isFirst ? 1 : way.nodes.length - 2);
22131             addHighway(way, way.nodes[index]);
22132
22133         } else {
22134             var splitIndex, wayA, wayB, indexA, indexB;
22135             if (isClosingNode) {
22136                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
22137                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
22138                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
22139                 indexA = 1;
22140                 indexB = way.nodes.length - 2;
22141             } else {
22142                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
22143                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
22144                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
22145                 indexA = splitIndex - 1;
22146                 indexB = splitIndex + 1;
22147             }
22148             graph = graph.replace(wayA).replace(wayB);
22149             addHighway(wayA, way.nodes[indexA]);
22150             addHighway(wayB, way.nodes[indexB]);
22151         }
22152     });
22153
22154     // remove any ways from this intersection that are coincident
22155     // (i.e. any adjacent node used by more than one intersecting way)
22156     coincident.forEach(function (n) {
22157         delete highways[n];
22158     });
22159
22160
22161     var intersection = {
22162         highways: highways,
22163         ways: _.values(highways),
22164         graph: graph
22165     };
22166
22167     intersection.adjacentNodeId = function(fromWayId) {
22168         return _.find(_.keys(highways), function(k) {
22169             return highways[k].id === fromWayId;
22170         });
22171     };
22172
22173     intersection.turns = function(fromNodeId) {
22174         var start = highways[fromNodeId];
22175         if (!start)
22176             return [];
22177
22178         if (start.first() === vertex.id && start.tags.oneway === 'yes')
22179             return [];
22180         if (start.last() === vertex.id && start.tags.oneway === '-1')
22181             return [];
22182
22183         function withRestriction(turn) {
22184             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
22185                 if (relation.tags.type !== 'restriction')
22186                     return;
22187
22188                 var f = relation.memberByRole('from'),
22189                     t = relation.memberByRole('to'),
22190                     v = relation.memberByRole('via');
22191
22192                 if (f && f.id === turn.from.way &&
22193                     v && v.id === turn.via.node &&
22194                     t && t.id === turn.to.way) {
22195                     turn.restriction = relation.id;
22196                 } else if (/^only_/.test(relation.tags.restriction) &&
22197                     f && f.id === turn.from.way &&
22198                     v && v.id === turn.via.node &&
22199                     t && t.id !== turn.to.way) {
22200                     turn.restriction = relation.id;
22201                     turn.indirect_restriction = true;
22202                 }
22203             });
22204
22205             return iD.geo.Turn(turn);
22206         }
22207
22208         var from = {
22209                 node: fromNodeId,
22210                 way: start.id.split(/-(a|b)/)[0]
22211             },
22212             via = { node: vertex.id },
22213             turns = [];
22214
22215         _.each(highways, function(end, adjacentNodeId) {
22216             if (end === start)
22217                 return;
22218
22219             // backward
22220             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
22221                 turns.push(withRestriction({
22222                     from: from,
22223                     via: via,
22224                     to: {
22225                         node: adjacentNodeId,
22226                         way: end.id.split(/-(a|b)/)[0]
22227                     }
22228                 }));
22229             }
22230
22231             // forward
22232             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
22233                 turns.push(withRestriction({
22234                     from: from,
22235                     via: via,
22236                     to: {
22237                         node: adjacentNodeId,
22238                         way: end.id.split(/-(a|b)/)[0]
22239                     }
22240                 }));
22241             }
22242
22243         });
22244
22245         // U-turn
22246         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
22247             turns.push(withRestriction({
22248                 from: from,
22249                 via: via,
22250                 to: from,
22251                 u: true
22252             }));
22253         }
22254
22255         return turns;
22256     };
22257
22258     return intersection;
22259 };
22260
22261
22262 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
22263     var fromWay = graph.entity(from.way),
22264         fromNode = graph.entity(from.node),
22265         toWay = graph.entity(to.way),
22266         toNode = graph.entity(to.node),
22267         viaNode = graph.entity(via.node),
22268         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
22269             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
22270         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
22271             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
22272         angle = iD.geo.angle(viaNode, fromNode, projection) -
22273                 iD.geo.angle(viaNode, toNode, projection);
22274
22275     angle = angle * 180 / Math.PI;
22276
22277     while (angle < 0)
22278         angle += 360;
22279
22280     if (fromNode === toNode)
22281         return 'no_u_turn';
22282     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
22283         return 'no_u_turn';
22284     if (angle < 158)
22285         return 'no_right_turn';
22286     if (angle > 202)
22287         return 'no_left_turn';
22288
22289     return 'no_straight_on';
22290 };
22291 // For fixing up rendering of multipolygons with tags on the outer member.
22292 // https://github.com/openstreetmap/iD/issues/613
22293 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
22294     if (entity.type !== 'way')
22295         return false;
22296
22297     var parents = graph.parentRelations(entity);
22298     if (parents.length !== 1)
22299         return false;
22300
22301     var parent = parents[0];
22302     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
22303         return false;
22304
22305     var members = parent.members, member;
22306     for (var i = 0; i < members.length; i++) {
22307         member = members[i];
22308         if (member.id === entity.id && member.role && member.role !== 'outer')
22309             return false; // Not outer member
22310         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
22311             return false; // Not a simple multipolygon
22312     }
22313
22314     return parent;
22315 };
22316
22317 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
22318     if (entity.type !== 'way')
22319         return false;
22320
22321     var parents = graph.parentRelations(entity);
22322     if (parents.length !== 1)
22323         return false;
22324
22325     var parent = parents[0];
22326     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
22327         return false;
22328
22329     var members = parent.members, member, outerMember;
22330     for (var i = 0; i < members.length; i++) {
22331         member = members[i];
22332         if (!member.role || member.role === 'outer') {
22333             if (outerMember)
22334                 return false; // Not a simple multipolygon
22335             outerMember = member;
22336         }
22337     }
22338
22339     return outerMember && graph.hasEntity(outerMember.id);
22340 };
22341
22342 // Join `array` into sequences of connecting ways.
22343 //
22344 // Segments which share identical start/end nodes will, as much as possible,
22345 // be connected with each other.
22346 //
22347 // The return value is a nested array. Each constituent array contains elements
22348 // of `array` which have been determined to connect. Each consitituent array
22349 // also has a `nodes` property whose value is an ordered array of member nodes,
22350 // with appropriate order reversal and start/end coordinate de-duplication.
22351 //
22352 // Members of `array` must have, at minimum, `type` and `id` properties.
22353 // Thus either an array of `iD.Way`s or a relation member array may be
22354 // used.
22355 //
22356 // If an member has a `tags` property, its tags will be reversed via
22357 // `iD.actions.Reverse` in the output.
22358 //
22359 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
22360 // false) and non-way members are ignored.
22361 //
22362 iD.geo.joinWays = function(array, graph) {
22363     var joined = [], member, current, nodes, first, last, i, how, what;
22364
22365     array = array.filter(function(member) {
22366         return member.type === 'way' && graph.hasEntity(member.id);
22367     });
22368
22369     function resolve(member) {
22370         return graph.childNodes(graph.entity(member.id));
22371     }
22372
22373     function reverse(member) {
22374         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
22375     }
22376
22377     while (array.length) {
22378         member = array.shift();
22379         current = [member];
22380         current.nodes = nodes = resolve(member).slice();
22381         joined.push(current);
22382
22383         while (array.length && _.first(nodes) !== _.last(nodes)) {
22384             first = _.first(nodes);
22385             last  = _.last(nodes);
22386
22387             for (i = 0; i < array.length; i++) {
22388                 member = array[i];
22389                 what = resolve(member);
22390
22391                 if (last === _.first(what)) {
22392                     how  = nodes.push;
22393                     what = what.slice(1);
22394                     break;
22395                 } else if (last === _.last(what)) {
22396                     how  = nodes.push;
22397                     what = what.slice(0, -1).reverse();
22398                     member = reverse(member);
22399                     break;
22400                 } else if (first === _.last(what)) {
22401                     how  = nodes.unshift;
22402                     what = what.slice(0, -1);
22403                     break;
22404                 } else if (first === _.first(what)) {
22405                     how  = nodes.unshift;
22406                     what = what.slice(1).reverse();
22407                     member = reverse(member);
22408                     break;
22409                 } else {
22410                     what = how = null;
22411                 }
22412             }
22413
22414             if (!what)
22415                 break; // No more joinable ways.
22416
22417             how.apply(current, [member]);
22418             how.apply(nodes, what);
22419
22420             array.splice(i, 1);
22421         }
22422     }
22423
22424     return joined;
22425 };
22426 /*
22427     Bypasses features of D3's default projection stream pipeline that are unnecessary:
22428     * Antimeridian clipping
22429     * Spherical rotation
22430     * Resampling
22431 */
22432 iD.geo.RawMercator = function () {
22433     var project = d3.geo.mercator.raw,
22434         k = 512 / Math.PI, // scale
22435         x = 0, y = 0, // translate
22436         clipExtent = [[0, 0], [0, 0]];
22437
22438     function projection(point) {
22439         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
22440         return [point[0] * k + x, y - point[1] * k];
22441     }
22442
22443     projection.invert = function(point) {
22444         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
22445         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
22446     };
22447
22448     projection.scale = function(_) {
22449         if (!arguments.length) return k;
22450         k = +_;
22451         return projection;
22452     };
22453
22454     projection.translate = function(_) {
22455         if (!arguments.length) return [x, y];
22456         x = +_[0];
22457         y = +_[1];
22458         return projection;
22459     };
22460
22461     projection.clipExtent = function(_) {
22462         if (!arguments.length) return clipExtent;
22463         clipExtent = _;
22464         return projection;
22465     };
22466
22467     projection.stream = d3.geo.transform({
22468         point: function(x, y) {
22469             x = projection([x, y]);
22470             this.stream.point(x[0], x[1]);
22471         }
22472     }).stream;
22473
22474     return projection;
22475 };
22476 iD.actions = {};
22477 iD.actions.AddEntity = function(way) {
22478     return function(graph) {
22479         return graph.replace(way);
22480     };
22481 };
22482 iD.actions.AddMember = function(relationId, member, memberIndex) {
22483     return function(graph) {
22484         var relation = graph.entity(relationId);
22485
22486         if (isNaN(memberIndex) && member.type === 'way') {
22487             var members = relation.indexedMembers();
22488             members.push(member);
22489
22490             var joined = iD.geo.joinWays(members, graph);
22491             for (var i = 0; i < joined.length; i++) {
22492                 var segment = joined[i];
22493                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
22494                     if (segment[j] !== member)
22495                         continue;
22496
22497                     if (j === 0) {
22498                         memberIndex = segment[j + 1].index;
22499                     } else if (j === segment.length - 1) {
22500                         memberIndex = segment[j - 1].index + 1;
22501                     } else {
22502                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
22503                     }
22504                 }
22505             }
22506         }
22507
22508         return graph.replace(relation.addMember(member, memberIndex));
22509     };
22510 };
22511 iD.actions.AddMidpoint = function(midpoint, node) {
22512     return function(graph) {
22513         graph = graph.replace(node.move(midpoint.loc));
22514
22515         var parents = _.intersection(
22516             graph.parentWays(graph.entity(midpoint.edge[0])),
22517             graph.parentWays(graph.entity(midpoint.edge[1])));
22518
22519         parents.forEach(function(way) {
22520             for (var i = 0; i < way.nodes.length - 1; i++) {
22521                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
22522                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
22523
22524                     // Add only one midpoint on doubled-back segments,
22525                     // turning them into self-intersections.
22526                     return;
22527                 }
22528             }
22529         });
22530
22531         return graph;
22532     };
22533 };
22534 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
22535 iD.actions.AddVertex = function(wayId, nodeId, index) {
22536     return function(graph) {
22537         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
22538     };
22539 };
22540 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
22541     return function(graph) {
22542         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
22543     };
22544 };
22545 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
22546     return function(graph) {
22547         var entity = graph.entity(entityId),
22548             geometry = entity.geometry(graph),
22549             tags = entity.tags;
22550
22551         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
22552         if (newPreset) tags = newPreset.applyTags(tags, geometry);
22553
22554         return graph.replace(entity.update({tags: tags}));
22555     };
22556 };
22557 iD.actions.ChangeTags = function(entityId, tags) {
22558     return function(graph) {
22559         var entity = graph.entity(entityId);
22560         return graph.replace(entity.update({tags: tags}));
22561     };
22562 };
22563 iD.actions.Circularize = function(wayId, projection, maxAngle) {
22564     maxAngle = (maxAngle || 20) * Math.PI / 180;
22565
22566     var action = function(graph) {
22567         var way = graph.entity(wayId);
22568
22569         if (!way.isConvex(graph)) {
22570             graph = action.makeConvex(graph);
22571         }
22572
22573         var nodes = _.uniq(graph.childNodes(way)),
22574             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
22575             points = nodes.map(function(n) { return projection(n.loc); }),
22576             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
22577             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
22578             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
22579             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
22580             ids;
22581
22582         // we need atleast two key nodes for the algorithm to work
22583         if (!keyNodes.length) {
22584             keyNodes = [nodes[0]];
22585             keyPoints = [points[0]];
22586         }
22587
22588         if (keyNodes.length === 1) {
22589             var index = nodes.indexOf(keyNodes[0]),
22590                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
22591
22592             keyNodes.push(nodes[oppositeIndex]);
22593             keyPoints.push(points[oppositeIndex]);
22594         }
22595
22596         // key points and nodes are those connected to the ways,
22597         // they are projected onto the circle, inbetween nodes are moved
22598         // to constant intervals between key nodes, extra inbetween nodes are
22599         // added if necessary.
22600         for (var i = 0; i < keyPoints.length; i++) {
22601             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
22602                 startNode = keyNodes[i],
22603                 endNode = keyNodes[nextKeyNodeIndex],
22604                 startNodeIndex = nodes.indexOf(startNode),
22605                 endNodeIndex = nodes.indexOf(endNode),
22606                 numberNewPoints = -1,
22607                 indexRange = endNodeIndex - startNodeIndex,
22608                 distance, totalAngle, eachAngle, startAngle, endAngle,
22609                 angle, loc, node, j,
22610                 inBetweenNodes = [];
22611
22612             if (indexRange < 0) {
22613                 indexRange += nodes.length;
22614             }
22615
22616             // position this key node
22617             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
22618             if (distance === 0) { distance = 1e-4; }
22619             keyPoints[i] = [
22620                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
22621                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
22622             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
22623
22624             // figure out the between delta angle we want to match to
22625             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
22626             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
22627             totalAngle = endAngle - startAngle;
22628
22629             // detects looping around -pi/pi
22630             if (totalAngle * sign > 0) {
22631                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
22632             }
22633
22634             do {
22635                 numberNewPoints++;
22636                 eachAngle = totalAngle / (indexRange + numberNewPoints);
22637             } while (Math.abs(eachAngle) > maxAngle);
22638
22639             // move existing points
22640             for (j = 1; j < indexRange; j++) {
22641                 angle = startAngle + j * eachAngle;
22642                 loc = projection.invert([
22643                     centroid[0] + Math.cos(angle)*radius,
22644                     centroid[1] + Math.sin(angle)*radius]);
22645
22646                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
22647                 graph = graph.replace(node);
22648             }
22649
22650             // add new inbetween nodes if necessary
22651             for (j = 0; j < numberNewPoints; j++) {
22652                 angle = startAngle + (indexRange + j) * eachAngle;
22653                 loc = projection.invert([
22654                     centroid[0] + Math.cos(angle) * radius,
22655                     centroid[1] + Math.sin(angle) * radius]);
22656
22657                 node = iD.Node({loc: loc});
22658                 graph = graph.replace(node);
22659
22660                 nodes.splice(endNodeIndex + j, 0, node);
22661                 inBetweenNodes.push(node.id);
22662             }
22663
22664             // Check for other ways that share these keyNodes..
22665             // If keyNodes are adjacent in both ways,
22666             // we can add inBetween nodes to that shared way too..
22667             if (indexRange === 1 && inBetweenNodes.length) {
22668                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
22669                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
22670                     wayDirection1 = (endIndex1 - startIndex1);
22671                 if (wayDirection1 < -1) { wayDirection1 = 1; }
22672
22673                 /* eslint-disable no-loop-func */
22674                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
22675                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
22676                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
22677                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
22678                             wayDirection2 = (endIndex2 - startIndex2),
22679                             insertAt = endIndex2;
22680                         if (wayDirection2 < -1) { wayDirection2 = 1; }
22681
22682                         if (wayDirection1 !== wayDirection2) {
22683                             inBetweenNodes.reverse();
22684                             insertAt = startIndex2;
22685                         }
22686                         for (j = 0; j < inBetweenNodes.length; j++) {
22687                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
22688                         }
22689                         graph = graph.replace(sharedWay);
22690                     }
22691                 });
22692                 /* eslint-enable no-loop-func */
22693             }
22694
22695         }
22696
22697         // update the way to have all the new nodes
22698         ids = nodes.map(function(n) { return n.id; });
22699         ids.push(ids[0]);
22700
22701         way = way.update({nodes: ids});
22702         graph = graph.replace(way);
22703
22704         return graph;
22705     };
22706
22707     action.makeConvex = function(graph) {
22708         var way = graph.entity(wayId),
22709             nodes = _.uniq(graph.childNodes(way)),
22710             points = nodes.map(function(n) { return projection(n.loc); }),
22711             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
22712             hull = d3.geom.hull(points);
22713
22714         // D3 convex hulls go counterclockwise..
22715         if (sign === -1) {
22716             nodes.reverse();
22717             points.reverse();
22718         }
22719
22720         for (var i = 0; i < hull.length - 1; i++) {
22721             var startIndex = points.indexOf(hull[i]),
22722                 endIndex = points.indexOf(hull[i+1]),
22723                 indexRange = (endIndex - startIndex);
22724
22725             if (indexRange < 0) {
22726                 indexRange += nodes.length;
22727             }
22728
22729             // move interior nodes to the surface of the convex hull..
22730             for (var j = 1; j < indexRange; j++) {
22731                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
22732                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22733                 graph = graph.replace(node);
22734             }
22735         }
22736         return graph;
22737     };
22738
22739     action.disabled = function(graph) {
22740         if (!graph.entity(wayId).isClosed())
22741             return 'not_closed';
22742     };
22743
22744     return action;
22745 };
22746 // Connect the ways at the given nodes.
22747 //
22748 // The last node will survive. All other nodes will be replaced with
22749 // the surviving node in parent ways, and then removed.
22750 //
22751 // Tags and relation memberships of of non-surviving nodes are merged
22752 // to the survivor.
22753 //
22754 // This is the inverse of `iD.actions.Disconnect`.
22755 //
22756 // Reference:
22757 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22758 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22759 //
22760 iD.actions.Connect = function(nodeIds) {
22761     return function(graph) {
22762         var survivor = graph.entity(_.last(nodeIds));
22763
22764         for (var i = 0; i < nodeIds.length - 1; i++) {
22765             var node = graph.entity(nodeIds[i]);
22766
22767             /* eslint-disable no-loop-func */
22768             graph.parentWays(node).forEach(function(parent) {
22769                 if (!parent.areAdjacent(node.id, survivor.id)) {
22770                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
22771                 }
22772             });
22773
22774             graph.parentRelations(node).forEach(function(parent) {
22775                 graph = graph.replace(parent.replaceMember(node, survivor));
22776             });
22777             /* eslint-enable no-loop-func */
22778
22779             survivor = survivor.mergeTags(node.tags);
22780             graph = iD.actions.DeleteNode(node.id)(graph);
22781         }
22782
22783         graph = graph.replace(survivor);
22784
22785         return graph;
22786     };
22787 };
22788 iD.actions.CopyEntities = function(ids, fromGraph) {
22789     var copies = {};
22790
22791     var action = function(graph) {
22792         ids.forEach(function(id) {
22793             fromGraph.entity(id).copy(fromGraph, copies);
22794         });
22795
22796         for (var id in copies) {
22797             graph = graph.replace(copies[id]);
22798         }
22799
22800         return graph;
22801     };
22802
22803     action.copies = function() {
22804         return copies;
22805     };
22806
22807     return action;
22808 };
22809 iD.actions.DeleteMember = function(relationId, memberIndex) {
22810     return function(graph) {
22811         var relation = graph.entity(relationId)
22812             .removeMember(memberIndex);
22813
22814         graph = graph.replace(relation);
22815
22816         if (relation.isDegenerate())
22817             graph = iD.actions.DeleteRelation(relation.id)(graph);
22818
22819         return graph;
22820     };
22821 };
22822 iD.actions.DeleteMultiple = function(ids) {
22823     var actions = {
22824         way: iD.actions.DeleteWay,
22825         node: iD.actions.DeleteNode,
22826         relation: iD.actions.DeleteRelation
22827     };
22828
22829     var action = function(graph) {
22830         ids.forEach(function(id) {
22831             if (graph.hasEntity(id)) { // It may have been deleted aready.
22832                 graph = actions[graph.entity(id).type](id)(graph);
22833             }
22834         });
22835
22836         return graph;
22837     };
22838
22839     action.disabled = function(graph) {
22840         for (var i = 0; i < ids.length; i++) {
22841             var id = ids[i],
22842                 disabled = actions[graph.entity(id).type](id).disabled(graph);
22843             if (disabled) return disabled;
22844         }
22845     };
22846
22847     return action;
22848 };
22849 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22850 iD.actions.DeleteNode = function(nodeId) {
22851     var action = function(graph) {
22852         var node = graph.entity(nodeId);
22853
22854         graph.parentWays(node)
22855             .forEach(function(parent) {
22856                 parent = parent.removeNode(nodeId);
22857                 graph = graph.replace(parent);
22858
22859                 if (parent.isDegenerate()) {
22860                     graph = iD.actions.DeleteWay(parent.id)(graph);
22861                 }
22862             });
22863
22864         graph.parentRelations(node)
22865             .forEach(function(parent) {
22866                 parent = parent.removeMembersWithID(nodeId);
22867                 graph = graph.replace(parent);
22868
22869                 if (parent.isDegenerate()) {
22870                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22871                 }
22872             });
22873
22874         return graph.remove(node);
22875     };
22876
22877     action.disabled = function() {
22878         return false;
22879     };
22880
22881     return action;
22882 };
22883 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22884 iD.actions.DeleteRelation = function(relationId) {
22885     function deleteEntity(entity, graph) {
22886         return !graph.parentWays(entity).length &&
22887             !graph.parentRelations(entity).length &&
22888             !entity.hasInterestingTags();
22889     }
22890
22891     var action = function(graph) {
22892         var relation = graph.entity(relationId);
22893
22894         graph.parentRelations(relation)
22895             .forEach(function(parent) {
22896                 parent = parent.removeMembersWithID(relationId);
22897                 graph = graph.replace(parent);
22898
22899                 if (parent.isDegenerate()) {
22900                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22901                 }
22902             });
22903
22904         _.uniq(_.map(relation.members, 'id')).forEach(function(memberId) {
22905             graph = graph.replace(relation.removeMembersWithID(memberId));
22906
22907             var entity = graph.entity(memberId);
22908             if (deleteEntity(entity, graph)) {
22909                 graph = iD.actions.DeleteMultiple([memberId])(graph);
22910             }
22911         });
22912
22913         return graph.remove(relation);
22914     };
22915
22916     action.disabled = function(graph) {
22917         if (!graph.entity(relationId).isComplete(graph))
22918             return 'incomplete_relation';
22919     };
22920
22921     return action;
22922 };
22923 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22924 iD.actions.DeleteWay = function(wayId) {
22925     function deleteNode(node, graph) {
22926         return !graph.parentWays(node).length &&
22927             !graph.parentRelations(node).length &&
22928             !node.hasInterestingTags();
22929     }
22930
22931     var action = function(graph) {
22932         var way = graph.entity(wayId);
22933
22934         graph.parentRelations(way)
22935             .forEach(function(parent) {
22936                 parent = parent.removeMembersWithID(wayId);
22937                 graph = graph.replace(parent);
22938
22939                 if (parent.isDegenerate()) {
22940                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22941                 }
22942             });
22943
22944         _.uniq(way.nodes).forEach(function(nodeId) {
22945             graph = graph.replace(way.removeNode(nodeId));
22946
22947             var node = graph.entity(nodeId);
22948             if (deleteNode(node, graph)) {
22949                 graph = graph.remove(node);
22950             }
22951         });
22952
22953         return graph.remove(way);
22954     };
22955
22956     action.disabled = function(graph) {
22957         var disabled = false;
22958
22959         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
22960             var type = parent.tags.type,
22961                 role = parent.memberById(wayId).role || 'outer';
22962             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
22963                 disabled = 'part_of_relation';
22964             }
22965         });
22966
22967         return disabled;
22968     };
22969
22970     return action;
22971 };
22972 iD.actions.DeprecateTags = function(entityId) {
22973     return function(graph) {
22974         var entity = graph.entity(entityId),
22975             newtags = _.clone(entity.tags),
22976             change = false,
22977             rule;
22978
22979         // This handles deprecated tags with a single condition
22980         for (var i = 0; i < iD.data.deprecated.length; i++) {
22981
22982             rule = iD.data.deprecated[i];
22983             var match = _.toPairs(rule.old)[0],
22984                 replacements = rule.replace ? _.toPairs(rule.replace) : null;
22985
22986             if (entity.tags[match[0]] && match[1] === '*') {
22987
22988                 var value = entity.tags[match[0]];
22989                 if (replacements && !newtags[replacements[0][0]]) {
22990                     newtags[replacements[0][0]] = value;
22991                 }
22992                 delete newtags[match[0]];
22993                 change = true;
22994
22995             } else if (entity.tags[match[0]] === match[1]) {
22996                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
22997                 change = true;
22998             }
22999         }
23000
23001         if (change) {
23002             return graph.replace(entity.update({tags: newtags}));
23003         } else {
23004             return graph;
23005         }
23006     };
23007 };
23008 iD.actions.DiscardTags = function(difference) {
23009     return function(graph) {
23010         function discardTags(entity) {
23011             if (!_.isEmpty(entity.tags)) {
23012                 var tags = {};
23013                 _.each(entity.tags, function(v, k) {
23014                     if (v) tags[k] = v;
23015                 });
23016
23017                 graph = graph.replace(entity.update({
23018                     tags: _.omit(tags, iD.data.discarded)
23019                 }));
23020             }
23021         }
23022
23023         difference.modified().forEach(discardTags);
23024         difference.created().forEach(discardTags);
23025
23026         return graph;
23027     };
23028 };
23029 // Disconect the ways at the given node.
23030 //
23031 // Optionally, disconnect only the given ways.
23032 //
23033 // For testing convenience, accepts an ID to assign to the (first) new node.
23034 // Normally, this will be undefined and the way will automatically
23035 // be assigned a new ID.
23036 //
23037 // This is the inverse of `iD.actions.Connect`.
23038 //
23039 // Reference:
23040 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
23041 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
23042 //
23043 iD.actions.Disconnect = function(nodeId, newNodeId) {
23044     var wayIds;
23045
23046     var action = function(graph) {
23047         var node = graph.entity(nodeId),
23048             connections = action.connections(graph);
23049
23050         connections.forEach(function(connection) {
23051             var way = graph.entity(connection.wayID),
23052                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
23053
23054             graph = graph.replace(newNode);
23055             if (connection.index === 0 && way.isArea()) {
23056                 // replace shared node with shared node..
23057                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
23058             } else {
23059                 // replace shared node with multiple new nodes..
23060                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
23061             }
23062         });
23063
23064         return graph;
23065     };
23066
23067     action.connections = function(graph) {
23068         var candidates = [],
23069             keeping = false,
23070             parentWays = graph.parentWays(graph.entity(nodeId));
23071
23072         parentWays.forEach(function(way) {
23073             if (wayIds && wayIds.indexOf(way.id) === -1) {
23074                 keeping = true;
23075                 return;
23076             }
23077             if (way.isArea() && (way.nodes[0] === nodeId)) {
23078                 candidates.push({wayID: way.id, index: 0});
23079             } else {
23080                 way.nodes.forEach(function(waynode, index) {
23081                     if (waynode === nodeId) {
23082                         candidates.push({wayID: way.id, index: index});
23083                     }
23084                 });
23085             }
23086         });
23087
23088         return keeping ? candidates : candidates.slice(1);
23089     };
23090
23091     action.disabled = function(graph) {
23092         var connections = action.connections(graph);
23093         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
23094             return 'not_connected';
23095
23096         var parentWays = graph.parentWays(graph.entity(nodeId)),
23097             seenRelationIds = {},
23098             sharedRelation;
23099
23100         parentWays.forEach(function(way) {
23101             if (wayIds && wayIds.indexOf(way.id) === -1)
23102                 return;
23103
23104             var relations = graph.parentRelations(way);
23105             relations.forEach(function(relation) {
23106                 if (relation.id in seenRelationIds) {
23107                     sharedRelation = relation;
23108                 } else {
23109                     seenRelationIds[relation.id] = true;
23110                 }
23111             });
23112         });
23113
23114         if (sharedRelation)
23115             return 'relation';
23116     };
23117
23118     action.limitWays = function(_) {
23119         if (!arguments.length) return wayIds;
23120         wayIds = _;
23121         return action;
23122     };
23123
23124     return action;
23125 };
23126 // Join ways at the end node they share.
23127 //
23128 // This is the inverse of `iD.actions.Split`.
23129 //
23130 // Reference:
23131 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
23132 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
23133 //
23134 iD.actions.Join = function(ids) {
23135
23136     function groupEntitiesByGeometry(graph) {
23137         var entities = ids.map(function(id) { return graph.entity(id); });
23138         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
23139     }
23140
23141     var action = function(graph) {
23142         var ways = ids.map(graph.entity, graph),
23143             survivor = ways[0];
23144
23145         // Prefer to keep an existing way.
23146         for (var i = 0; i < ways.length; i++) {
23147             if (!ways[i].isNew()) {
23148                 survivor = ways[i];
23149                 break;
23150             }
23151         }
23152
23153         var joined = iD.geo.joinWays(ways, graph)[0];
23154
23155         survivor = survivor.update({nodes: _.map(joined.nodes, 'id')});
23156         graph = graph.replace(survivor);
23157
23158         joined.forEach(function(way) {
23159             if (way.id === survivor.id)
23160                 return;
23161
23162             graph.parentRelations(way).forEach(function(parent) {
23163                 graph = graph.replace(parent.replaceMember(way, survivor));
23164             });
23165
23166             survivor = survivor.mergeTags(way.tags);
23167
23168             graph = graph.replace(survivor);
23169             graph = iD.actions.DeleteWay(way.id)(graph);
23170         });
23171
23172         return graph;
23173     };
23174
23175     action.disabled = function(graph) {
23176         var geometries = groupEntitiesByGeometry(graph);
23177         if (ids.length < 2 || ids.length !== geometries.line.length)
23178             return 'not_eligible';
23179
23180         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
23181         if (joined.length > 1)
23182             return 'not_adjacent';
23183
23184         var nodeIds = _.map(joined[0].nodes, 'id').slice(1, -1),
23185             relation,
23186             tags = {},
23187             conflicting = false;
23188
23189         joined[0].forEach(function(way) {
23190             var parents = graph.parentRelations(way);
23191             parents.forEach(function(parent) {
23192                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
23193                     relation = parent;
23194             });
23195
23196             for (var k in way.tags) {
23197                 if (!(k in tags)) {
23198                     tags[k] = way.tags[k];
23199                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
23200                     conflicting = true;
23201                 }
23202             }
23203         });
23204
23205         if (relation)
23206             return 'restriction';
23207
23208         if (conflicting)
23209             return 'conflicting_tags';
23210     };
23211
23212     return action;
23213 };
23214 iD.actions.Merge = function(ids) {
23215     function groupEntitiesByGeometry(graph) {
23216         var entities = ids.map(function(id) { return graph.entity(id); });
23217         return _.extend({point: [], area: [], line: [], relation: []},
23218             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
23219     }
23220
23221     var action = function(graph) {
23222         var geometries = groupEntitiesByGeometry(graph),
23223             target = geometries.area[0] || geometries.line[0],
23224             points = geometries.point;
23225
23226         points.forEach(function(point) {
23227             target = target.mergeTags(point.tags);
23228
23229             graph.parentRelations(point).forEach(function(parent) {
23230                 graph = graph.replace(parent.replaceMember(point, target));
23231             });
23232
23233             graph = graph.remove(point);
23234         });
23235
23236         graph = graph.replace(target);
23237
23238         return graph;
23239     };
23240
23241     action.disabled = function(graph) {
23242         var geometries = groupEntitiesByGeometry(graph);
23243         if (geometries.point.length === 0 ||
23244             (geometries.area.length + geometries.line.length) !== 1 ||
23245             geometries.relation.length !== 0)
23246             return 'not_eligible';
23247     };
23248
23249     return action;
23250 };
23251 iD.actions.MergePolygon = function(ids, newRelationId) {
23252
23253     function groupEntities(graph) {
23254         var entities = ids.map(function (id) { return graph.entity(id); });
23255         return _.extend({
23256                 closedWay: [],
23257                 multipolygon: [],
23258                 other: []
23259             }, _.groupBy(entities, function(entity) {
23260                 if (entity.type === 'way' && entity.isClosed()) {
23261                     return 'closedWay';
23262                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
23263                     return 'multipolygon';
23264                 } else {
23265                     return 'other';
23266                 }
23267             }));
23268     }
23269
23270     var action = function(graph) {
23271         var entities = groupEntities(graph);
23272
23273         // An array representing all the polygons that are part of the multipolygon.
23274         //
23275         // Each element is itself an array of objects with an id property, and has a
23276         // locs property which is an array of the locations forming the polygon.
23277         var polygons = entities.multipolygon.reduce(function(polygons, m) {
23278             return polygons.concat(iD.geo.joinWays(m.members, graph));
23279         }, []).concat(entities.closedWay.map(function(d) {
23280             var member = [{id: d.id}];
23281             member.nodes = graph.childNodes(d);
23282             return member;
23283         }));
23284
23285         // contained is an array of arrays of boolean values,
23286         // where contained[j][k] is true iff the jth way is
23287         // contained by the kth way.
23288         var contained = polygons.map(function(w, i) {
23289             return polygons.map(function(d, n) {
23290                 if (i === n) return null;
23291                 return iD.geo.polygonContainsPolygon(
23292                     _.map(d.nodes, 'loc'),
23293                     _.map(w.nodes, 'loc'));
23294             });
23295         });
23296
23297         // Sort all polygons as either outer or inner ways
23298         var members = [],
23299             outer = true;
23300
23301         while (polygons.length) {
23302             extractUncontained(polygons);
23303             polygons = polygons.filter(isContained);
23304             contained = contained.filter(isContained).map(filterContained);
23305         }
23306
23307         function isContained(d, i) {
23308             return _.some(contained[i]);
23309         }
23310
23311         function filterContained(d) {
23312             return d.filter(isContained);
23313         }
23314
23315         function extractUncontained(polygons) {
23316             polygons.forEach(function(d, i) {
23317                 if (!isContained(d, i)) {
23318                     d.forEach(function(member) {
23319                         members.push({
23320                             type: 'way',
23321                             id: member.id,
23322                             role: outer ? 'outer' : 'inner'
23323                         });
23324                     });
23325                 }
23326             });
23327             outer = !outer;
23328         }
23329
23330         // Move all tags to one relation
23331         var relation = entities.multipolygon[0] ||
23332             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
23333
23334         entities.multipolygon.slice(1).forEach(function(m) {
23335             relation = relation.mergeTags(m.tags);
23336             graph = graph.remove(m);
23337         });
23338
23339         entities.closedWay.forEach(function(way) {
23340             function isThisOuter(m) {
23341                 return m.id === way.id && m.role !== 'inner';
23342             }
23343             if (members.some(isThisOuter)) {
23344                 relation = relation.mergeTags(way.tags);
23345                 graph = graph.replace(way.update({ tags: {} }));
23346             }
23347         });
23348
23349         return graph.replace(relation.update({
23350             members: members,
23351             tags: _.omit(relation.tags, 'area')
23352         }));
23353     };
23354
23355     action.disabled = function(graph) {
23356         var entities = groupEntities(graph);
23357         if (entities.other.length > 0 ||
23358             entities.closedWay.length + entities.multipolygon.length < 2)
23359             return 'not_eligible';
23360         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
23361             return 'incomplete_relation';
23362     };
23363
23364     return action;
23365 };
23366 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
23367     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
23368         conflicts = [];
23369
23370     function user(d) {
23371         return _.isFunction(formatUser) ? formatUser(d) : d;
23372     }
23373
23374
23375     function mergeLocation(remote, target) {
23376         function pointEqual(a, b) {
23377             var epsilon = 1e-6;
23378             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
23379         }
23380
23381         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
23382             return target;
23383         }
23384         if (option === 'force_remote') {
23385             return target.update({loc: remote.loc});
23386         }
23387
23388         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
23389         return target;
23390     }
23391
23392
23393     function mergeNodes(base, remote, target) {
23394         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
23395             return target;
23396         }
23397         if (option === 'force_remote') {
23398             return target.update({nodes: remote.nodes});
23399         }
23400
23401         var ccount = conflicts.length,
23402             o = base.nodes || [],
23403             a = target.nodes || [],
23404             b = remote.nodes || [],
23405             nodes = [],
23406             hunks = Diff3.diff3_merge(a, o, b, true);
23407
23408         for (var i = 0; i < hunks.length; i++) {
23409             var hunk = hunks[i];
23410             if (hunk.ok) {
23411                 nodes.push.apply(nodes, hunk.ok);
23412             } else {
23413                 // for all conflicts, we can assume c.a !== c.b
23414                 // because `diff3_merge` called with `true` option to exclude false conflicts..
23415                 var c = hunk.conflict;
23416                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
23417                     nodes.push.apply(nodes, c.b);
23418                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
23419                     nodes.push.apply(nodes, c.a);
23420                 } else {       // changed both locally and remotely
23421                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
23422                     break;
23423                 }
23424             }
23425         }
23426
23427         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
23428     }
23429
23430
23431     function mergeChildren(targetWay, children, updates, graph) {
23432         function isUsed(node, targetWay) {
23433             var parentWays = _.map(graph.parentWays(node), 'id');
23434             return node.hasInterestingTags() ||
23435                 _.without(parentWays, targetWay.id).length > 0 ||
23436                 graph.parentRelations(node).length > 0;
23437         }
23438
23439         var ccount = conflicts.length;
23440
23441         for (var i = 0; i < children.length; i++) {
23442             var id = children[i],
23443                 node = graph.hasEntity(id);
23444
23445             // remove unused childNodes..
23446             if (targetWay.nodes.indexOf(id) === -1) {
23447                 if (node && !isUsed(node, targetWay)) {
23448                     updates.removeIds.push(id);
23449                 }
23450                 continue;
23451             }
23452
23453             // restore used childNodes..
23454             var local = localGraph.hasEntity(id),
23455                 remote = remoteGraph.hasEntity(id),
23456                 target;
23457
23458             if (option === 'force_remote' && remote && remote.visible) {
23459                 updates.replacements.push(remote);
23460
23461             } else if (option === 'force_local' && local) {
23462                 target = iD.Entity(local);
23463                 if (remote) {
23464                     target = target.update({ version: remote.version });
23465                 }
23466                 updates.replacements.push(target);
23467
23468             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
23469                 target = iD.Entity(local, { version: remote.version });
23470                 if (remote.visible) {
23471                     target = mergeLocation(remote, target);
23472                 } else {
23473                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
23474                 }
23475
23476                 if (conflicts.length !== ccount) break;
23477                 updates.replacements.push(target);
23478             }
23479         }
23480
23481         return targetWay;
23482     }
23483
23484
23485     function updateChildren(updates, graph) {
23486         for (var i = 0; i < updates.replacements.length; i++) {
23487             graph = graph.replace(updates.replacements[i]);
23488         }
23489         if (updates.removeIds.length) {
23490             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
23491         }
23492         return graph;
23493     }
23494
23495
23496     function mergeMembers(remote, target) {
23497         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
23498             return target;
23499         }
23500         if (option === 'force_remote') {
23501             return target.update({members: remote.members});
23502         }
23503
23504         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
23505         return target;
23506     }
23507
23508
23509     function mergeTags(base, remote, target) {
23510         function ignoreKey(k) {
23511             return _.includes(iD.data.discarded, k);
23512         }
23513
23514         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
23515             return target;
23516         }
23517         if (option === 'force_remote') {
23518             return target.update({tags: remote.tags});
23519         }
23520
23521         var ccount = conflicts.length,
23522             o = base.tags || {},
23523             a = target.tags || {},
23524             b = remote.tags || {},
23525             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
23526             tags = _.clone(a),
23527             changed = false;
23528
23529         for (var i = 0; i < keys.length; i++) {
23530             var k = keys[i];
23531
23532             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
23533                 if (o[k] !== a[k]) {      // changed locally..
23534                     conflicts.push(t('merge_remote_changes.conflict.tags',
23535                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
23536
23537                 } else {                  // unchanged locally, accept remote change..
23538                     if (b.hasOwnProperty(k)) {
23539                         tags[k] = b[k];
23540                     } else {
23541                         delete tags[k];
23542                     }
23543                     changed = true;
23544                 }
23545             }
23546         }
23547
23548         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
23549     }
23550
23551
23552     //  `graph.base()` is the common ancestor of the two graphs.
23553     //  `localGraph` contains user's edits up to saving
23554     //  `remoteGraph` contains remote edits to modified nodes
23555     //  `graph` must be a descendent of `localGraph` and may include
23556     //      some conflict resolution actions performed on it.
23557     //
23558     //                  --- ... --- `localGraph` -- ... -- `graph`
23559     //                 /
23560     //  `graph.base()` --- ... --- `remoteGraph`
23561     //
23562     var action = function(graph) {
23563         var updates = { replacements: [], removeIds: [] },
23564             base = graph.base().entities[id],
23565             local = localGraph.entity(id),
23566             remote = remoteGraph.entity(id),
23567             target = iD.Entity(local, { version: remote.version });
23568
23569         // delete/undelete
23570         if (!remote.visible) {
23571             if (option === 'force_remote') {
23572                 return iD.actions.DeleteMultiple([id])(graph);
23573
23574             } else if (option === 'force_local') {
23575                 if (target.type === 'way') {
23576                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
23577                     graph = updateChildren(updates, graph);
23578                 }
23579                 return graph.replace(target);
23580
23581             } else {
23582                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
23583                 return graph;  // do nothing
23584             }
23585         }
23586
23587         // merge
23588         if (target.type === 'node') {
23589             target = mergeLocation(remote, target);
23590
23591         } else if (target.type === 'way') {
23592             // pull in any child nodes that may not be present locally..
23593             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
23594             target = mergeNodes(base, remote, target);
23595             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
23596
23597         } else if (target.type === 'relation') {
23598             target = mergeMembers(remote, target);
23599         }
23600
23601         target = mergeTags(base, remote, target);
23602
23603         if (!conflicts.length) {
23604             graph = updateChildren(updates, graph).replace(target);
23605         }
23606
23607         return graph;
23608     };
23609
23610     action.withOption = function(opt) {
23611         option = opt;
23612         return action;
23613     };
23614
23615     action.conflicts = function() {
23616         return conflicts;
23617     };
23618
23619     return action;
23620 };
23621 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
23622 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
23623 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
23624     var delta = tryDelta;
23625
23626     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
23627     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
23628
23629     function setupCache(graph) {
23630         function canMove(nodeId) {
23631             var parents = _.map(graph.parentWays(graph.entity(nodeId)), 'id');
23632             if (parents.length < 3) return true;
23633
23634             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
23635             var parentsMoving = _.every(parents, function(id) { return cache.moving[id]; });
23636             if (!parentsMoving) delete cache.moving[nodeId];
23637
23638             return parentsMoving;
23639         }
23640
23641         function cacheEntities(ids) {
23642             _.each(ids, function(id) {
23643                 if (cache.moving[id]) return;
23644                 cache.moving[id] = true;
23645
23646                 var entity = graph.hasEntity(id);
23647                 if (!entity) return;
23648
23649                 if (entity.type === 'node') {
23650                     cache.nodes.push(id);
23651                     cache.startLoc[id] = entity.loc;
23652                 } else if (entity.type === 'way') {
23653                     cache.ways.push(id);
23654                     cacheEntities(entity.nodes);
23655                 } else {
23656                     cacheEntities(_.map(entity.members, 'id'));
23657                 }
23658             });
23659         }
23660
23661         function cacheIntersections(ids) {
23662             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
23663
23664             _.each(ids, function(id) {
23665                 // consider only intersections with 1 moved and 1 unmoved way.
23666                 _.each(graph.childNodes(graph.entity(id)), function(node) {
23667                     var parents = graph.parentWays(node);
23668                     if (parents.length !== 2) return;
23669
23670                     var moved = graph.entity(id),
23671                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
23672                     if (!unmoved) return;
23673
23674                     // exclude ways that are overly connected..
23675                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
23676
23677                     if (moved.isArea() || unmoved.isArea()) return;
23678
23679                     cache.intersection[node.id] = {
23680                         nodeId: node.id,
23681                         movedId: moved.id,
23682                         unmovedId: unmoved.id,
23683                         movedIsEP: isEndpoint(moved, node.id),
23684                         unmovedIsEP: isEndpoint(unmoved, node.id)
23685                     };
23686                 });
23687             });
23688         }
23689
23690
23691         if (!cache) {
23692             cache = {};
23693         }
23694         if (!cache.ok) {
23695             cache.moving = {};
23696             cache.intersection = {};
23697             cache.replacedVertex = {};
23698             cache.startLoc = {};
23699             cache.nodes = [];
23700             cache.ways = [];
23701
23702             cacheEntities(moveIds);
23703             cacheIntersections(cache.ways);
23704             cache.nodes = _.filter(cache.nodes, canMove);
23705
23706             cache.ok = true;
23707         }
23708     }
23709
23710
23711     // Place a vertex where the moved vertex used to be, to preserve way shape..
23712     function replaceMovedVertex(nodeId, wayId, graph, delta) {
23713         var way = graph.entity(wayId),
23714             moved = graph.entity(nodeId),
23715             movedIndex = way.nodes.indexOf(nodeId),
23716             len, prevIndex, nextIndex;
23717
23718         if (way.isClosed()) {
23719             len = way.nodes.length - 1;
23720             prevIndex = (movedIndex + len - 1) % len;
23721             nextIndex = (movedIndex + len + 1) % len;
23722         } else {
23723             len = way.nodes.length;
23724             prevIndex = movedIndex - 1;
23725             nextIndex = movedIndex + 1;
23726         }
23727
23728         var prev = graph.hasEntity(way.nodes[prevIndex]),
23729             next = graph.hasEntity(way.nodes[nextIndex]);
23730
23731         // Don't add orig vertex at endpoint..
23732         if (!prev || !next) return graph;
23733
23734         var key = wayId + '_' + nodeId,
23735             orig = cache.replacedVertex[key];
23736         if (!orig) {
23737             orig = iD.Node();
23738             cache.replacedVertex[key] = orig;
23739             cache.startLoc[orig.id] = cache.startLoc[nodeId];
23740         }
23741
23742         var start, end;
23743         if (delta) {
23744             start = projection(cache.startLoc[nodeId]);
23745             end = projection.invert(vecAdd(start, delta));
23746         } else {
23747             end = cache.startLoc[nodeId];
23748         }
23749         orig = orig.move(end);
23750
23751         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
23752                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
23753
23754         // Don't add orig vertex if it would just make a straight line..
23755         if (angle > 175 && angle < 185) return graph;
23756
23757         // Don't add orig vertex if another point is already nearby (within 10m)
23758         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
23759             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
23760
23761         // moving forward or backward along way?
23762         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
23763             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
23764             d1 = iD.geo.pathLength(p1),
23765             d2 = iD.geo.pathLength(p2),
23766             insertAt = (d1 < d2) ? movedIndex : nextIndex;
23767
23768         // moving around closed loop?
23769         if (way.isClosed() && insertAt === 0) insertAt = len;
23770
23771         way = way.addNode(orig.id, insertAt);
23772         return graph.replace(orig).replace(way);
23773     }
23774
23775     // Reorder nodes around intersections that have moved..
23776     function unZorroIntersection(intersection, graph) {
23777         var vertex = graph.entity(intersection.nodeId),
23778             way1 = graph.entity(intersection.movedId),
23779             way2 = graph.entity(intersection.unmovedId),
23780             isEP1 = intersection.movedIsEP,
23781             isEP2 = intersection.unmovedIsEP;
23782
23783         // don't move the vertex if it is the endpoint of both ways.
23784         if (isEP1 && isEP2) return graph;
23785
23786         var nodes1 = _.without(graph.childNodes(way1), vertex),
23787             nodes2 = _.without(graph.childNodes(way2), vertex);
23788
23789         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
23790         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
23791
23792         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
23793             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
23794             loc;
23795
23796         // snap vertex to nearest edge (or some point between them)..
23797         if (!isEP1 && !isEP2) {
23798             var epsilon = 1e-4, maxIter = 10;
23799             for (var i = 0; i < maxIter; i++) {
23800                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
23801                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
23802                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
23803                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
23804             }
23805         } else if (!isEP1) {
23806             loc = edge1.loc;
23807         } else {
23808             loc = edge2.loc;
23809         }
23810
23811         graph = graph.replace(vertex.move(loc));
23812
23813         // if zorro happened, reorder nodes..
23814         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
23815             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
23816             graph = graph.replace(way1);
23817         }
23818         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
23819             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
23820             graph = graph.replace(way2);
23821         }
23822
23823         return graph;
23824     }
23825
23826
23827     function cleanupIntersections(graph) {
23828         _.each(cache.intersection, function(obj) {
23829             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
23830             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
23831             graph = unZorroIntersection(obj, graph);
23832         });
23833
23834         return graph;
23835     }
23836
23837     // check if moving way endpoint can cross an unmoved way, if so limit delta..
23838     function limitDelta(graph) {
23839         _.each(cache.intersection, function(obj) {
23840             if (!obj.movedIsEP) return;
23841
23842             var node = graph.entity(obj.nodeId),
23843                 start = projection(node.loc),
23844                 end = vecAdd(start, delta),
23845                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
23846                 movedPath = _.map(_.map(movedNodes, 'loc'),
23847                     function(loc) { return vecAdd(projection(loc), delta); }),
23848                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
23849                 unmovedPath = _.map(_.map(unmovedNodes, 'loc'), projection),
23850                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
23851
23852             for (var i = 0; i < hits.length; i++) {
23853                 if (_.isEqual(hits[i], end)) continue;
23854                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
23855                 delta = vecSub(projection(edge.loc), start);
23856             }
23857         });
23858     }
23859
23860
23861     var action = function(graph) {
23862         if (delta[0] === 0 && delta[1] === 0) return graph;
23863
23864         setupCache(graph);
23865
23866         if (!_.isEmpty(cache.intersection)) {
23867             limitDelta(graph);
23868         }
23869
23870         _.each(cache.nodes, function(id) {
23871             var node = graph.entity(id),
23872                 start = projection(node.loc),
23873                 end = vecAdd(start, delta);
23874             graph = graph.replace(node.move(projection.invert(end)));
23875         });
23876
23877         if (!_.isEmpty(cache.intersection)) {
23878             graph = cleanupIntersections(graph);
23879         }
23880
23881         return graph;
23882     };
23883
23884     action.disabled = function(graph) {
23885         function incompleteRelation(id) {
23886             var entity = graph.entity(id);
23887             return entity.type === 'relation' && !entity.isComplete(graph);
23888         }
23889
23890         if (_.some(moveIds, incompleteRelation))
23891             return 'incomplete_relation';
23892     };
23893
23894     action.delta = function() {
23895         return delta;
23896     };
23897
23898     return action;
23899 };
23900 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
23901 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
23902 iD.actions.MoveNode = function(nodeId, loc) {
23903     return function(graph) {
23904         return graph.replace(graph.entity(nodeId).move(loc));
23905     };
23906 };
23907 iD.actions.Noop = function() {
23908     return function(graph) {
23909         return graph;
23910     };
23911 };
23912 /*
23913  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
23914  */
23915
23916 iD.actions.Orthogonalize = function(wayId, projection) {
23917     var threshold = 12, // degrees within right or straight to alter
23918         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
23919         upperThreshold = Math.cos(threshold * Math.PI / 180);
23920
23921     var action = function(graph) {
23922         var way = graph.entity(wayId),
23923             nodes = graph.childNodes(way),
23924             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
23925             corner = {i: 0, dotp: 1},
23926             epsilon = 1e-4,
23927             i, j, score, motions;
23928
23929         if (nodes.length === 4) {
23930             for (i = 0; i < 1000; i++) {
23931                 motions = points.map(calcMotion);
23932                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
23933                 score = corner.dotp;
23934                 if (score < epsilon) {
23935                     break;
23936                 }
23937             }
23938
23939             graph = graph.replace(graph.entity(nodes[corner.i].id)
23940                 .move(projection.invert(points[corner.i])));
23941         } else {
23942             var best,
23943                 originalPoints = _.clone(points);
23944             score = Infinity;
23945
23946             for (i = 0; i < 1000; i++) {
23947                 motions = points.map(calcMotion);
23948                 for (j = 0; j < motions.length; j++) {
23949                     points[j] = addPoints(points[j],motions[j]);
23950                 }
23951                 var newScore = squareness(points);
23952                 if (newScore < score) {
23953                     best = _.clone(points);
23954                     score = newScore;
23955                 }
23956                 if (score < epsilon) {
23957                     break;
23958                 }
23959             }
23960
23961             points = best;
23962
23963             for (i = 0; i < points.length; i++) {
23964                 // only move the points that actually moved
23965                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
23966                     graph = graph.replace(graph.entity(nodes[i].id)
23967                         .move(projection.invert(points[i])));
23968                 }
23969             }
23970
23971             // remove empty nodes on straight sections
23972             for (i = 0; i < points.length; i++) {
23973                 var node = nodes[i];
23974
23975                 if (graph.parentWays(node).length > 1 ||
23976                     graph.parentRelations(node).length ||
23977                     node.hasInterestingTags()) {
23978
23979                     continue;
23980                 }
23981
23982                 var dotp = normalizedDotProduct(i, points);
23983                 if (dotp < -1 + epsilon) {
23984                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
23985                 }
23986             }
23987         }
23988
23989         return graph;
23990
23991         function calcMotion(b, i, array) {
23992             var a = array[(i - 1 + array.length) % array.length],
23993                 c = array[(i + 1) % array.length],
23994                 p = subtractPoints(a, b),
23995                 q = subtractPoints(c, b),
23996                 scale, dotp;
23997
23998             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
23999             p = normalizePoint(p, 1.0);
24000             q = normalizePoint(q, 1.0);
24001
24002             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
24003
24004             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
24005             if (array.length > 3) {
24006                 if (dotp < -0.707106781186547) {
24007                     dotp += 1.0;
24008                 }
24009             } else if (dotp && Math.abs(dotp) < corner.dotp) {
24010                 corner.i = i;
24011                 corner.dotp = Math.abs(dotp);
24012             }
24013
24014             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
24015         }
24016     };
24017
24018     function squareness(points) {
24019         return points.reduce(function(sum, val, i, array) {
24020             var dotp = normalizedDotProduct(i, array);
24021
24022             dotp = filterDotProduct(dotp);
24023             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
24024         }, 0);
24025     }
24026
24027     function normalizedDotProduct(i, points) {
24028         var a = points[(i - 1 + points.length) % points.length],
24029             b = points[i],
24030             c = points[(i + 1) % points.length],
24031             p = subtractPoints(a, b),
24032             q = subtractPoints(c, b);
24033
24034         p = normalizePoint(p, 1.0);
24035         q = normalizePoint(q, 1.0);
24036
24037         return p[0] * q[0] + p[1] * q[1];
24038     }
24039
24040     function subtractPoints(a, b) {
24041         return [a[0] - b[0], a[1] - b[1]];
24042     }
24043
24044     function addPoints(a, b) {
24045         return [a[0] + b[0], a[1] + b[1]];
24046     }
24047
24048     function normalizePoint(point, scale) {
24049         var vector = [0, 0];
24050         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
24051         if (length !== 0) {
24052             vector[0] = point[0] / length;
24053             vector[1] = point[1] / length;
24054         }
24055
24056         vector[0] *= scale;
24057         vector[1] *= scale;
24058
24059         return vector;
24060     }
24061
24062     function filterDotProduct(dotp) {
24063         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
24064             return dotp;
24065         }
24066
24067         return 0;
24068     }
24069
24070     action.disabled = function(graph) {
24071         var way = graph.entity(wayId),
24072             nodes = graph.childNodes(way),
24073             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
24074
24075         if (squareness(points)) {
24076             return false;
24077         }
24078
24079         return 'not_squarish';
24080     };
24081
24082     return action;
24083 };
24084 // Create a restriction relation for `turn`, which must have the following structure:
24085 //
24086 //     {
24087 //         from: { node: <node ID>, way: <way ID> },
24088 //         via:  { node: <node ID> },
24089 //         to:   { node: <node ID>, way: <way ID> },
24090 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
24091 //     }
24092 //
24093 // This specifies a restriction of type `restriction` when traveling from
24094 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
24095 // (The action does not check that these entities form a valid intersection.)
24096 //
24097 // If `restriction` is not provided, it is automatically determined by
24098 // iD.geo.inferRestriction.
24099 //
24100 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
24101 // and `to.node` are used to determine which portion of the split ways become
24102 // members of the restriction.
24103 //
24104 // For testing convenience, accepts an ID to assign to the new relation.
24105 // Normally, this will be undefined and the relation will automatically
24106 // be assigned a new ID.
24107 //
24108 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
24109     return function(graph) {
24110         var from = graph.entity(turn.from.way),
24111             via  = graph.entity(turn.via.node),
24112             to   = graph.entity(turn.to.way);
24113
24114         function isClosingNode(way, nodeId) {
24115             return nodeId === way.first() && nodeId === way.last();
24116         }
24117
24118         function split(toOrFrom) {
24119             var newID = toOrFrom.newID || iD.Way().id;
24120             graph = iD.actions.Split(via.id, [newID])
24121                 .limitWays([toOrFrom.way])(graph);
24122
24123             var a = graph.entity(newID),
24124                 b = graph.entity(toOrFrom.way);
24125
24126             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
24127                 return [a, b];
24128             } else {
24129                 return [b, a];
24130             }
24131         }
24132
24133         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
24134             if (turn.from.node === turn.to.node) {
24135                 // U-turn
24136                 from = to = split(turn.from)[0];
24137             } else if (turn.from.way === turn.to.way) {
24138                 // Straight-on or circular
24139                 var s = split(turn.from);
24140                 from = s[0];
24141                 to   = s[1];
24142             } else {
24143                 // Other
24144                 from = split(turn.from)[0];
24145             }
24146         }
24147
24148         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
24149             to = split(turn.to)[0];
24150         }
24151
24152         return graph.replace(iD.Relation({
24153             id: restrictionId,
24154             tags: {
24155                 type: 'restriction',
24156                 restriction: turn.restriction ||
24157                     iD.geo.inferRestriction(
24158                         graph,
24159                         turn.from,
24160                         turn.via,
24161                         turn.to,
24162                         projection)
24163             },
24164             members: [
24165                 {id: from.id, type: 'way',  role: 'from'},
24166                 {id: via.id,  type: 'node', role: 'via'},
24167                 {id: to.id,   type: 'way',  role: 'to'}
24168             ]
24169         }));
24170     };
24171 };
24172 /*
24173   Order the nodes of a way in reverse order and reverse any direction dependent tags
24174   other than `oneway`. (We assume that correcting a backwards oneway is the primary
24175   reason for reversing a way.)
24176
24177   The following transforms are performed:
24178
24179     Keys:
24180           *:right=* ⟺ *:left=*
24181         *:forward=* ⟺ *:backward=*
24182        direction=up ⟺ direction=down
24183          incline=up ⟺ incline=down
24184             *=right ⟺ *=left
24185
24186     Relation members:
24187        role=forward ⟺ role=backward
24188          role=north ⟺ role=south
24189           role=east ⟺ role=west
24190
24191    In addition, numeric-valued `incline` tags are negated.
24192
24193    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
24194    or adjusted tags that don't seem to be used in practice were omitted.
24195
24196    References:
24197       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
24198       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
24199       http://wiki.openstreetmap.org/wiki/Key:incline
24200       http://wiki.openstreetmap.org/wiki/Route#Members
24201       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
24202  */
24203 iD.actions.Reverse = function(wayId, options) {
24204     var replacements = [
24205             [/:right$/, ':left'], [/:left$/, ':right'],
24206             [/:forward$/, ':backward'], [/:backward$/, ':forward']
24207         ],
24208         numeric = /^([+\-]?)(?=[\d.])/,
24209         roleReversals = {
24210             forward: 'backward',
24211             backward: 'forward',
24212             north: 'south',
24213             south: 'north',
24214             east: 'west',
24215             west: 'east'
24216         };
24217
24218     function reverseKey(key) {
24219         for (var i = 0; i < replacements.length; ++i) {
24220             var replacement = replacements[i];
24221             if (replacement[0].test(key)) {
24222                 return key.replace(replacement[0], replacement[1]);
24223             }
24224         }
24225         return key;
24226     }
24227
24228     function reverseValue(key, value) {
24229         if (key === 'incline' && numeric.test(value)) {
24230             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
24231         } else if (key === 'incline' || key === 'direction') {
24232             return {up: 'down', down: 'up'}[value] || value;
24233         } else if (options && options.reverseOneway && key === 'oneway') {
24234             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
24235         } else {
24236             return {left: 'right', right: 'left'}[value] || value;
24237         }
24238     }
24239
24240     return function(graph) {
24241         var way = graph.entity(wayId),
24242             nodes = way.nodes.slice().reverse(),
24243             tags = {}, key, role;
24244
24245         for (key in way.tags) {
24246             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
24247         }
24248
24249         graph.parentRelations(way).forEach(function(relation) {
24250             relation.members.forEach(function(member, index) {
24251                 if (member.id === way.id && (role = roleReversals[member.role])) {
24252                     relation = relation.updateMember({role: role}, index);
24253                     graph = graph.replace(relation);
24254                 }
24255             });
24256         });
24257
24258         return graph.replace(way.update({nodes: nodes, tags: tags}));
24259     };
24260 };
24261 iD.actions.Revert = function(id) {
24262
24263     var action = function(graph) {
24264         var entity = graph.hasEntity(id),
24265             base = graph.base().entities[id];
24266
24267         if (entity && !base) {    // entity will be removed..
24268             if (entity.type === 'node') {
24269                 graph.parentWays(entity)
24270                     .forEach(function(parent) {
24271                         parent = parent.removeNode(id);
24272                         graph = graph.replace(parent);
24273
24274                         if (parent.isDegenerate()) {
24275                             graph = iD.actions.DeleteWay(parent.id)(graph);
24276                         }
24277                     });
24278             }
24279
24280             graph.parentRelations(entity)
24281                 .forEach(function(parent) {
24282                     parent = parent.removeMembersWithID(id);
24283                     graph = graph.replace(parent);
24284
24285                     if (parent.isDegenerate()) {
24286                         graph = iD.actions.DeleteRelation(parent.id)(graph);
24287                     }
24288                 });
24289         }
24290
24291         return graph.revert(id);
24292     };
24293
24294     return action;
24295 };
24296 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
24297     return function(graph) {
24298         return graph.update(function(graph) {
24299             var way = graph.entity(wayId);
24300
24301             _.uniq(way.nodes).forEach(function(id) {
24302
24303                 var node = graph.entity(id),
24304                     point = projection(node.loc),
24305                     radial = [0,0];
24306
24307                 radial[0] = point[0] - pivot[0];
24308                 radial[1] = point[1] - pivot[1];
24309
24310                 point = [
24311                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
24312                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
24313                 ];
24314
24315                 graph = graph.replace(node.move(projection.invert(point)));
24316
24317             });
24318
24319         });
24320     };
24321 };
24322 // Split a way at the given node.
24323 //
24324 // Optionally, split only the given ways, if multiple ways share
24325 // the given node.
24326 //
24327 // This is the inverse of `iD.actions.Join`.
24328 //
24329 // For testing convenience, accepts an ID to assign to the new way.
24330 // Normally, this will be undefined and the way will automatically
24331 // be assigned a new ID.
24332 //
24333 // Reference:
24334 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
24335 //
24336 iD.actions.Split = function(nodeId, newWayIds) {
24337     var wayIds;
24338
24339     // if the way is closed, we need to search for a partner node
24340     // to split the way at.
24341     //
24342     // The following looks for a node that is both far away from
24343     // the initial node in terms of way segment length and nearby
24344     // in terms of beeline-distance. This assures that areas get
24345     // split on the most "natural" points (independent of the number
24346     // of nodes).
24347     // For example: bone-shaped areas get split across their waist
24348     // line, circles across the diameter.
24349     function splitArea(nodes, idxA, graph) {
24350         var lengths = new Array(nodes.length),
24351             length,
24352             i,
24353             best = 0,
24354             idxB;
24355
24356         function wrap(index) {
24357             return iD.util.wrap(index, nodes.length);
24358         }
24359
24360         function dist(nA, nB) {
24361             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
24362         }
24363
24364         // calculate lengths
24365         length = 0;
24366         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
24367             length += dist(nodes[i], nodes[wrap(i-1)]);
24368             lengths[i] = length;
24369         }
24370
24371         length = 0;
24372         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
24373             length += dist(nodes[i], nodes[wrap(i+1)]);
24374             if (length < lengths[i])
24375                 lengths[i] = length;
24376         }
24377
24378         // determine best opposite node to split
24379         for (i = 0; i < nodes.length; i++) {
24380             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
24381             if (cost > best) {
24382                 idxB = i;
24383                 best = cost;
24384             }
24385         }
24386
24387         return idxB;
24388     }
24389
24390     function split(graph, wayA, newWayId) {
24391         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
24392             nodesA,
24393             nodesB,
24394             isArea = wayA.isArea(),
24395             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
24396
24397         if (wayA.isClosed()) {
24398             var nodes = wayA.nodes.slice(0, -1),
24399                 idxA = _.indexOf(nodes, nodeId),
24400                 idxB = splitArea(nodes, idxA, graph);
24401
24402             if (idxB < idxA) {
24403                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
24404                 nodesB = nodes.slice(idxB, idxA + 1);
24405             } else {
24406                 nodesA = nodes.slice(idxA, idxB + 1);
24407                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
24408             }
24409         } else {
24410             var idx = _.indexOf(wayA.nodes, nodeId, 1);
24411             nodesA = wayA.nodes.slice(0, idx + 1);
24412             nodesB = wayA.nodes.slice(idx);
24413         }
24414
24415         wayA = wayA.update({nodes: nodesA});
24416         wayB = wayB.update({nodes: nodesB});
24417
24418         graph = graph.replace(wayA);
24419         graph = graph.replace(wayB);
24420
24421         graph.parentRelations(wayA).forEach(function(relation) {
24422             if (relation.isRestriction()) {
24423                 var via = relation.memberByRole('via');
24424                 if (via && wayB.contains(via.id)) {
24425                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
24426                     graph = graph.replace(relation);
24427                 }
24428             } else {
24429                 if (relation === isOuter) {
24430                     graph = graph.replace(relation.mergeTags(wayA.tags));
24431                     graph = graph.replace(wayA.update({tags: {}}));
24432                     graph = graph.replace(wayB.update({tags: {}}));
24433                 }
24434
24435                 var member = {
24436                     id: wayB.id,
24437                     type: 'way',
24438                     role: relation.memberById(wayA.id).role
24439                 };
24440
24441                 graph = iD.actions.AddMember(relation.id, member)(graph);
24442             }
24443         });
24444
24445         if (!isOuter && isArea) {
24446             var multipolygon = iD.Relation({
24447                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
24448                 members: [
24449                     {id: wayA.id, role: 'outer', type: 'way'},
24450                     {id: wayB.id, role: 'outer', type: 'way'}
24451                 ]});
24452
24453             graph = graph.replace(multipolygon);
24454             graph = graph.replace(wayA.update({tags: {}}));
24455             graph = graph.replace(wayB.update({tags: {}}));
24456         }
24457
24458         return graph;
24459     }
24460
24461     var action = function(graph) {
24462         var candidates = action.ways(graph);
24463         for (var i = 0; i < candidates.length; i++) {
24464             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
24465         }
24466         return graph;
24467     };
24468
24469     action.ways = function(graph) {
24470         var node = graph.entity(nodeId),
24471             parents = graph.parentWays(node),
24472             hasLines = _.some(parents, function(parent) { return parent.geometry(graph) === 'line'; });
24473
24474         return parents.filter(function(parent) {
24475             if (wayIds && wayIds.indexOf(parent.id) === -1)
24476                 return false;
24477
24478             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
24479                 return false;
24480
24481             if (parent.isClosed()) {
24482                 return true;
24483             }
24484
24485             for (var i = 1; i < parent.nodes.length - 1; i++) {
24486                 if (parent.nodes[i] === nodeId) {
24487                     return true;
24488                 }
24489             }
24490
24491             return false;
24492         });
24493     };
24494
24495     action.disabled = function(graph) {
24496         var candidates = action.ways(graph);
24497         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
24498             return 'not_eligible';
24499     };
24500
24501     action.limitWays = function(_) {
24502         if (!arguments.length) return wayIds;
24503         wayIds = _;
24504         return action;
24505     };
24506
24507     return action;
24508 };
24509 /*
24510  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
24511  */
24512
24513 iD.actions.Straighten = function(wayId, projection) {
24514     function positionAlongWay(n, s, e) {
24515         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
24516                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
24517     }
24518
24519     var action = function(graph) {
24520         var way = graph.entity(wayId),
24521             nodes = graph.childNodes(way),
24522             points = nodes.map(function(n) { return projection(n.loc); }),
24523             startPoint = points[0],
24524             endPoint = points[points.length-1],
24525             toDelete = [],
24526             i;
24527
24528         for (i = 1; i < points.length-1; i++) {
24529             var node = nodes[i],
24530                 point = points[i];
24531
24532             if (graph.parentWays(node).length > 1 ||
24533                 graph.parentRelations(node).length ||
24534                 node.hasInterestingTags()) {
24535
24536                 var u = positionAlongWay(point, startPoint, endPoint),
24537                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
24538                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
24539
24540                 graph = graph.replace(graph.entity(node.id)
24541                     .move(projection.invert([p0, p1])));
24542             } else {
24543                 // safe to delete
24544                 if (toDelete.indexOf(node) === -1) {
24545                     toDelete.push(node);
24546                 }
24547             }
24548         }
24549
24550         for (i = 0; i < toDelete.length; i++) {
24551             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
24552         }
24553
24554         return graph;
24555     };
24556
24557     action.disabled = function(graph) {
24558         // check way isn't too bendy
24559         var way = graph.entity(wayId),
24560             nodes = graph.childNodes(way),
24561             points = nodes.map(function(n) { return projection(n.loc); }),
24562             startPoint = points[0],
24563             endPoint = points[points.length-1],
24564             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
24565             i;
24566
24567         if (threshold === 0) {
24568             return 'too_bendy';
24569         }
24570
24571         for (i = 1; i < points.length-1; i++) {
24572             var point = points[i],
24573                 u = positionAlongWay(point, startPoint, endPoint),
24574                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
24575                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
24576                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
24577
24578             // to bendy if point is off by 20% of total start/end distance in projected space
24579             if (isNaN(dist) || dist > threshold) {
24580                 return 'too_bendy';
24581             }
24582         }
24583     };
24584
24585     return action;
24586 };
24587 // Remove the effects of `turn.restriction` on `turn`, which must have the
24588 // following structure:
24589 //
24590 //     {
24591 //         from: { node: <node ID>, way: <way ID> },
24592 //         via:  { node: <node ID> },
24593 //         to:   { node: <node ID>, way: <way ID> },
24594 //         restriction: <relation ID>
24595 //     }
24596 //
24597 // In the simple case, `restriction` is a reference to a `no_*` restriction
24598 // on the turn itself. In this case, it is simply deleted.
24599 //
24600 // The more complex case is where `restriction` references an `only_*`
24601 // restriction on a different turn in the same intersection. In that case,
24602 // that restriction is also deleted, but at the same time restrictions on
24603 // the turns other than the first two are created.
24604 //
24605 iD.actions.UnrestrictTurn = function(turn) {
24606     return function(graph) {
24607         return iD.actions.DeleteRelation(turn.restriction)(graph);
24608     };
24609 };
24610 iD.behavior = {};
24611 iD.behavior.AddWay = function(context) {
24612     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
24613         draw = iD.behavior.Draw(context);
24614
24615     var addWay = function(surface) {
24616         draw.on('click', event.start)
24617             .on('clickWay', event.startFromWay)
24618             .on('clickNode', event.startFromNode)
24619             .on('cancel', addWay.cancel)
24620             .on('finish', addWay.cancel);
24621
24622         context.map()
24623             .dblclickEnable(false);
24624
24625         surface.call(draw);
24626     };
24627
24628     addWay.off = function(surface) {
24629         surface.call(draw.off);
24630     };
24631
24632     addWay.cancel = function() {
24633         window.setTimeout(function() {
24634             context.map().dblclickEnable(true);
24635         }, 1000);
24636
24637         context.enter(iD.modes.Browse(context));
24638     };
24639
24640     addWay.tail = function(text) {
24641         draw.tail(text);
24642         return addWay;
24643     };
24644
24645     return d3.rebind(addWay, event, 'on');
24646 };
24647 iD.behavior.Breathe = function() {
24648     var duration = 800,
24649         selector = '.selected.shadow, .selected .shadow',
24650         selected = d3.select(null),
24651         classed = '',
24652         params = {},
24653         done;
24654
24655     function reset(selection) {
24656         selection
24657             .style('stroke-opacity', null)
24658             .style('stroke-width', null)
24659             .style('fill-opacity', null)
24660             .style('r', null);
24661     }
24662
24663     function setAnimationParams(transition, fromTo) {
24664         transition
24665             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
24666             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
24667             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
24668             .style('r', function(d) { return params[d.id][fromTo].width; });
24669     }
24670
24671     function calcAnimationParams(selection) {
24672         selection
24673             .call(reset)
24674             .each(function(d) {
24675                 var s = d3.select(this),
24676                     tag = s.node().tagName,
24677                     p = {'from': {}, 'to': {}},
24678                     opacity, width;
24679
24680                 // determine base opacity and width
24681                 if (tag === 'circle') {
24682                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
24683                     width = parseFloat(s.style('r') || 15.5);
24684                 } else {
24685                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
24686                     width = parseFloat(s.style('stroke-width') || 10);
24687                 }
24688
24689                 // calculate from/to interpolation params..
24690                 p.tag = tag;
24691                 p.from.opacity = opacity * 0.6;
24692                 p.to.opacity = opacity * 1.25;
24693                 p.from.width = width * 0.9;
24694                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
24695                 params[d.id] = p;
24696             });
24697     }
24698
24699     function run(surface, fromTo) {
24700         var toFrom = (fromTo === 'from' ? 'to': 'from'),
24701             currSelected = surface.selectAll(selector),
24702             currClassed = surface.attr('class'),
24703             n = 0;
24704
24705         if (done || currSelected.empty()) {
24706             selected.call(reset);
24707             return;
24708         }
24709
24710         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
24711             selected.call(reset);
24712             classed = currClassed;
24713             selected = currSelected.call(calcAnimationParams);
24714         }
24715
24716         selected
24717             .transition()
24718             .call(setAnimationParams, fromTo)
24719             .duration(duration)
24720             .each(function() { ++n; })
24721             .each('end', function() {
24722                 if (!--n) {  // call once
24723                     surface.call(run, toFrom);
24724                 }
24725             });
24726     }
24727
24728     var breathe = function(surface) {
24729         done = false;
24730         d3.timer(function() {
24731             if (done) return true;
24732
24733             var currSelected = surface.selectAll(selector);
24734             if (currSelected.empty()) return false;
24735
24736             surface.call(run, 'from');
24737             return true;
24738         }, 200);
24739     };
24740
24741     breathe.off = function() {
24742         done = true;
24743         d3.timer.flush();
24744         selected
24745             .transition()
24746             .call(reset)
24747             .duration(0);
24748     };
24749
24750     return breathe;
24751 };
24752 iD.behavior.Copy = function(context) {
24753     var keybinding = d3.keybinding('copy');
24754
24755     function groupEntities(ids, graph) {
24756         var entities = ids.map(function (id) { return graph.entity(id); });
24757         return _.extend({relation: [], way: [], node: []},
24758             _.groupBy(entities, function(entity) { return entity.type; }));
24759     }
24760
24761     function getDescendants(id, graph, descendants) {
24762         var entity = graph.entity(id),
24763             i, children;
24764
24765         descendants = descendants || {};
24766
24767         if (entity.type === 'relation') {
24768             children = _.map(entity.members, 'id');
24769         } else if (entity.type === 'way') {
24770             children = entity.nodes;
24771         } else {
24772             children = [];
24773         }
24774
24775         for (i = 0; i < children.length; i++) {
24776             if (!descendants[children[i]]) {
24777                 descendants[children[i]] = true;
24778                 descendants = getDescendants(children[i], graph, descendants);
24779             }
24780         }
24781
24782         return descendants;
24783     }
24784
24785     function doCopy() {
24786         d3.event.preventDefault();
24787         if (context.inIntro()) return;
24788
24789         var graph = context.graph(),
24790             selected = groupEntities(context.selectedIDs(), graph),
24791             canCopy = [],
24792             skip = {},
24793             i, entity;
24794
24795         for (i = 0; i < selected.relation.length; i++) {
24796             entity = selected.relation[i];
24797             if (!skip[entity.id] && entity.isComplete(graph)) {
24798                 canCopy.push(entity.id);
24799                 skip = getDescendants(entity.id, graph, skip);
24800             }
24801         }
24802         for (i = 0; i < selected.way.length; i++) {
24803             entity = selected.way[i];
24804             if (!skip[entity.id]) {
24805                 canCopy.push(entity.id);
24806                 skip = getDescendants(entity.id, graph, skip);
24807             }
24808         }
24809         for (i = 0; i < selected.node.length; i++) {
24810             entity = selected.node[i];
24811             if (!skip[entity.id]) {
24812                 canCopy.push(entity.id);
24813             }
24814         }
24815
24816         context.copyIDs(canCopy);
24817     }
24818
24819     function copy() {
24820         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
24821         d3.select(document).call(keybinding);
24822         return copy;
24823     }
24824
24825     copy.off = function() {
24826         d3.select(document).call(keybinding.off);
24827     };
24828
24829     return copy;
24830 };
24831 /*
24832     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
24833
24834     * The `origin` function is expected to return an [x, y] tuple rather than an
24835       {x, y} object.
24836     * The events are `start`, `move`, and `end`.
24837       (https://github.com/mbostock/d3/issues/563)
24838     * The `start` event is not dispatched until the first cursor movement occurs.
24839       (https://github.com/mbostock/d3/pull/368)
24840     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
24841       than `x`, `y`, `dx`, and `dy` properties.
24842     * The `end` event is not dispatched if no movement occurs.
24843     * An `off` function is available that unbinds the drag's internal event handlers.
24844     * Delegation is supported via the `delegate` function.
24845
24846  */
24847 iD.behavior.drag = function() {
24848     function d3_eventCancel() {
24849       d3.event.stopPropagation();
24850       d3.event.preventDefault();
24851     }
24852
24853     var event = d3.dispatch('start', 'move', 'end'),
24854         origin = null,
24855         selector = '',
24856         filter = null,
24857         event_, target, surface;
24858
24859     event.of = function(thiz, argumentz) {
24860       return function(e1) {
24861         var e0 = e1.sourceEvent = d3.event;
24862         e1.target = drag;
24863         d3.event = e1;
24864         try {
24865           event[e1.type].apply(thiz, argumentz);
24866         } finally {
24867           d3.event = e0;
24868         }
24869       };
24870     };
24871
24872     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
24873         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
24874             function () {
24875                 var selection = d3.selection(),
24876                     select = selection.style(d3_event_userSelectProperty);
24877                 selection.style(d3_event_userSelectProperty, 'none');
24878                 return function () {
24879                     selection.style(d3_event_userSelectProperty, select);
24880                 };
24881             } :
24882             function (type) {
24883                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
24884                 return function () {
24885                     w.on('selectstart.' + type, null);
24886                 };
24887             };
24888
24889     function mousedown() {
24890         target = this;
24891         event_ = event.of(target, arguments);
24892         var eventTarget = d3.event.target,
24893             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
24894             offset,
24895             origin_ = point(),
24896             started = false,
24897             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
24898
24899         var w = d3.select(window)
24900             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
24901             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
24902
24903         if (origin) {
24904             offset = origin.apply(target, arguments);
24905             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
24906         } else {
24907             offset = [0, 0];
24908         }
24909
24910         if (touchId === null) d3.event.stopPropagation();
24911
24912         function point() {
24913             var p = target.parentNode || surface;
24914             return touchId !== null ? d3.touches(p).filter(function(p) {
24915                 return p.identifier === touchId;
24916             })[0] : d3.mouse(p);
24917         }
24918
24919         function dragmove() {
24920
24921             var p = point(),
24922                 dx = p[0] - origin_[0],
24923                 dy = p[1] - origin_[1];
24924             
24925             if (dx === 0 && dy === 0)
24926                 return;
24927
24928             if (!started) {
24929                 started = true;
24930                 event_({
24931                     type: 'start'
24932                 });
24933             }
24934
24935             origin_ = p;
24936             d3_eventCancel();
24937
24938             event_({
24939                 type: 'move',
24940                 point: [p[0] + offset[0],  p[1] + offset[1]],
24941                 delta: [dx, dy]
24942             });
24943         }
24944
24945         function dragend() {
24946             if (started) {
24947                 event_({
24948                     type: 'end'
24949                 });
24950
24951                 d3_eventCancel();
24952                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
24953             }
24954
24955             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
24956                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
24957             selectEnable();
24958         }
24959
24960         function click() {
24961             d3_eventCancel();
24962             w.on('click.drag', null);
24963         }
24964     }
24965
24966     function drag(selection) {
24967         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
24968             delegate = mousedown;
24969
24970         if (selector) {
24971             delegate = function() {
24972                 var root = this,
24973                     target = d3.event.target;
24974                 for (; target && target !== root; target = target.parentNode) {
24975                     if (target[matchesSelector](selector) &&
24976                             (!filter || filter(target.__data__))) {
24977                         return mousedown.call(target, target.__data__);
24978                     }
24979                 }
24980             };
24981         }
24982
24983         selection.on('mousedown.drag' + selector, delegate)
24984             .on('touchstart.drag' + selector, delegate);
24985     }
24986
24987     drag.off = function(selection) {
24988         selection.on('mousedown.drag' + selector, null)
24989             .on('touchstart.drag' + selector, null);
24990     };
24991
24992     drag.delegate = function(_) {
24993         if (!arguments.length) return selector;
24994         selector = _;
24995         return drag;
24996     };
24997
24998     drag.filter = function(_) {
24999         if (!arguments.length) return origin;
25000         filter = _;
25001         return drag;
25002     };
25003
25004     drag.origin = function (_) {
25005         if (!arguments.length) return origin;
25006         origin = _;
25007         return drag;
25008     };
25009
25010     drag.cancel = function() {
25011         d3.select(window)
25012             .on('mousemove.drag', null)
25013             .on('mouseup.drag', null);
25014         return drag;
25015     };
25016
25017     drag.target = function() {
25018         if (!arguments.length) return target;
25019         target = arguments[0];
25020         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
25021         return drag;
25022     };
25023
25024     drag.surface = function() {
25025         if (!arguments.length) return surface;
25026         surface = arguments[0];
25027         return drag;
25028     };
25029
25030     return d3.rebind(drag, event, 'on');
25031 };
25032 iD.behavior.Draw = function(context) {
25033     var event = d3.dispatch('move', 'click', 'clickWay',
25034             'clickNode', 'undo', 'cancel', 'finish'),
25035         keybinding = d3.keybinding('draw'),
25036         hover = iD.behavior.Hover(context)
25037             .altDisables(true)
25038             .on('hover', context.ui().sidebar.hover),
25039         tail = iD.behavior.Tail(),
25040         edit = iD.behavior.Edit(context),
25041         closeTolerance = 4,
25042         tolerance = 12,
25043         mouseLeave = false,
25044         lastMouse = null,
25045         cached = iD.behavior.Draw;
25046
25047     function datum() {
25048         if (d3.event.altKey) return {};
25049
25050         if (d3.event.type === 'keydown') {
25051             return (lastMouse && lastMouse.target.__data__) || {};
25052         } else {
25053             return d3.event.target.__data__ || {};
25054         }
25055     }
25056
25057     function mousedown() {
25058
25059         function point() {
25060             var p = context.container().node();
25061             return touchId !== null ? d3.touches(p).filter(function(p) {
25062                 return p.identifier === touchId;
25063             })[0] : d3.mouse(p);
25064         }
25065
25066         var element = d3.select(this),
25067             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
25068             t1 = +new Date(),
25069             p1 = point();
25070
25071         element.on('mousemove.draw', null);
25072
25073         d3.select(window).on('mouseup.draw', function() {
25074             var t2 = +new Date(),
25075                 p2 = point(),
25076                 dist = iD.geo.euclideanDistance(p1, p2);
25077
25078             element.on('mousemove.draw', mousemove);
25079             d3.select(window).on('mouseup.draw', null);
25080
25081             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
25082                 // Prevent a quick second click
25083                 d3.select(window).on('click.draw-block', function() {
25084                     d3.event.stopPropagation();
25085                 }, true);
25086
25087                 context.map().dblclickEnable(false);
25088
25089                 window.setTimeout(function() {
25090                     context.map().dblclickEnable(true);
25091                     d3.select(window).on('click.draw-block', null);
25092                 }, 500);
25093
25094                 click();
25095             }
25096         });
25097     }
25098
25099     function mousemove() {
25100         lastMouse = d3.event;
25101         event.move(datum());
25102     }
25103
25104     function mouseenter() {
25105         mouseLeave = false;
25106     }
25107
25108     function mouseleave() {
25109         mouseLeave = true;
25110     }
25111
25112     function click() {
25113         var d = datum();
25114         if (d.type === 'way') {
25115             var dims = context.map().dimensions(),
25116                 mouse = context.mouse(),
25117                 pad = 5,
25118                 trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
25119                     mouse[1] > pad && mouse[1] < dims[1] - pad;
25120
25121             if (trySnap) {
25122                 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
25123                     edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
25124                 event.clickWay(choice.loc, edge);
25125             } else {
25126                 event.click(context.map().mouseCoordinates());
25127             }
25128
25129         } else if (d.type === 'node') {
25130             event.clickNode(d);
25131
25132         } else {
25133             event.click(context.map().mouseCoordinates());
25134         }
25135     }
25136
25137     function space() {
25138         var currSpace = context.mouse();
25139         if (cached.disableSpace && cached.lastSpace) {
25140             var dist = iD.geo.euclideanDistance(cached.lastSpace, currSpace);
25141             if (dist > tolerance) {
25142                 cached.disableSpace = false;
25143             }
25144         }
25145
25146         if (cached.disableSpace || mouseLeave || !lastMouse) return;
25147
25148         // user must move mouse or release space bar to allow another click
25149         cached.lastSpace = currSpace;
25150         cached.disableSpace = true;
25151
25152         d3.select(window).on('keyup.space-block', function() {
25153             cached.disableSpace = false;
25154             d3.select(window).on('keyup.space-block', null);
25155         });
25156
25157         d3.event.preventDefault();
25158         click();
25159     }
25160
25161     function backspace() {
25162         d3.event.preventDefault();
25163         event.undo();
25164     }
25165
25166     function del() {
25167         d3.event.preventDefault();
25168         event.cancel();
25169     }
25170
25171     function ret() {
25172         d3.event.preventDefault();
25173         event.finish();
25174     }
25175
25176     function draw(selection) {
25177         context.install(hover);
25178         context.install(edit);
25179
25180         if (!context.inIntro() && !cached.usedTails[tail.text()]) {
25181             context.install(tail);
25182         }
25183
25184         keybinding
25185             .on('⌫', backspace)
25186             .on('⌦', del)
25187             .on('⎋', ret)
25188             .on('↩', ret)
25189             .on('space', space)
25190             .on('⌥space', space);
25191
25192         selection
25193             .on('mouseenter.draw', mouseenter)
25194             .on('mouseleave.draw', mouseleave)
25195             .on('mousedown.draw', mousedown)
25196             .on('mousemove.draw', mousemove);
25197
25198         d3.select(document)
25199             .call(keybinding);
25200
25201         return draw;
25202     }
25203
25204     draw.off = function(selection) {
25205         context.ui().sidebar.hover.cancel();
25206         context.uninstall(hover);
25207         context.uninstall(edit);
25208
25209         if (!context.inIntro() && !cached.usedTails[tail.text()]) {
25210             context.uninstall(tail);
25211             cached.usedTails[tail.text()] = true;
25212         }
25213
25214         selection
25215             .on('mouseenter.draw', null)
25216             .on('mouseleave.draw', null)
25217             .on('mousedown.draw', null)
25218             .on('mousemove.draw', null);
25219
25220         d3.select(window)
25221             .on('mouseup.draw', null);
25222             // note: keyup.space-block, click.draw-block should remain
25223
25224         d3.select(document)
25225             .call(keybinding.off);
25226     };
25227
25228     draw.tail = function(_) {
25229         tail.text(_);
25230         return draw;
25231     };
25232
25233     return d3.rebind(draw, event, 'on');
25234 };
25235
25236 iD.behavior.Draw.usedTails = {};
25237 iD.behavior.Draw.disableSpace = false;
25238 iD.behavior.Draw.lastSpace = null;
25239 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
25240     var way = context.entity(wayId),
25241         isArea = context.geometry(wayId) === 'area',
25242         finished = false,
25243         annotation = t((way.isDegenerate() ?
25244             'operations.start.annotation.' :
25245             'operations.continue.annotation.') + context.geometry(wayId)),
25246         draw = iD.behavior.Draw(context);
25247
25248     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
25249         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
25250         end = iD.Node({loc: context.map().mouseCoordinates()}),
25251         segment = iD.Way({
25252             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
25253             tags: _.clone(way.tags)
25254         });
25255
25256     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
25257     if (isArea) {
25258         f(iD.actions.AddEntity(end),
25259             iD.actions.AddVertex(wayId, end.id, index));
25260     } else {
25261         f(iD.actions.AddEntity(start),
25262             iD.actions.AddEntity(end),
25263             iD.actions.AddEntity(segment));
25264     }
25265
25266     function move(datum) {
25267         var loc;
25268
25269         if (datum.type === 'node' && datum.id !== end.id) {
25270             loc = datum.loc;
25271
25272         } else if (datum.type === 'way' && datum.id !== segment.id) {
25273             var dims = context.map().dimensions(),
25274                 mouse = context.mouse(),
25275                 pad = 5,
25276                 trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
25277                     mouse[1] > pad && mouse[1] < dims[1] - pad;
25278
25279             if (trySnap) {
25280                 loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
25281             }
25282         }
25283
25284         if (!loc) {
25285             loc = context.map().mouseCoordinates();
25286         }
25287
25288         context.replace(iD.actions.MoveNode(end.id, loc));
25289     }
25290
25291     function undone() {
25292         finished = true;
25293         context.enter(iD.modes.Browse(context));
25294     }
25295
25296     function setActiveElements() {
25297         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
25298         context.surface().selectAll(iD.util.entitySelector(active))
25299             .classed('active', true);
25300     }
25301
25302     var drawWay = function(surface) {
25303         draw.on('move', move)
25304             .on('click', drawWay.add)
25305             .on('clickWay', drawWay.addWay)
25306             .on('clickNode', drawWay.addNode)
25307             .on('undo', context.undo)
25308             .on('cancel', drawWay.cancel)
25309             .on('finish', drawWay.finish);
25310
25311         context.map()
25312             .dblclickEnable(false)
25313             .on('drawn.draw', setActiveElements);
25314
25315         setActiveElements();
25316
25317         surface.call(draw);
25318
25319         context.history()
25320             .on('undone.draw', undone);
25321     };
25322
25323     drawWay.off = function(surface) {
25324         if (!finished)
25325             context.pop();
25326
25327         context.map()
25328             .on('drawn.draw', null);
25329
25330         surface.call(draw.off)
25331             .selectAll('.active')
25332             .classed('active', false);
25333
25334         context.history()
25335             .on('undone.draw', null);
25336     };
25337
25338     function ReplaceTemporaryNode(newNode) {
25339         return function(graph) {
25340             if (isArea) {
25341                 return graph
25342                     .replace(way.addNode(newNode.id, index))
25343                     .remove(end);
25344
25345             } else {
25346                 return graph
25347                     .replace(graph.entity(wayId).addNode(newNode.id, index))
25348                     .remove(end)
25349                     .remove(segment)
25350                     .remove(start);
25351             }
25352         };
25353     }
25354
25355     // Accept the current position of the temporary node and continue drawing.
25356     drawWay.add = function(loc) {
25357
25358         // prevent duplicate nodes
25359         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
25360         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
25361
25362         var newNode = iD.Node({loc: loc});
25363
25364         context.replace(
25365             iD.actions.AddEntity(newNode),
25366             ReplaceTemporaryNode(newNode),
25367             annotation);
25368
25369         finished = true;
25370         context.enter(mode);
25371     };
25372
25373     // Connect the way to an existing way.
25374     drawWay.addWay = function(loc, edge) {
25375         var previousEdge = startIndex ?
25376             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
25377             [way.nodes[0], way.nodes[1]];
25378
25379         // Avoid creating duplicate segments
25380         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
25381             return;
25382
25383         var newNode = iD.Node({ loc: loc });
25384
25385         context.perform(
25386             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
25387             ReplaceTemporaryNode(newNode),
25388             annotation);
25389
25390         finished = true;
25391         context.enter(mode);
25392     };
25393
25394     // Connect the way to an existing node and continue drawing.
25395     drawWay.addNode = function(node) {
25396
25397         // Avoid creating duplicate segments
25398         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
25399
25400         context.perform(
25401             ReplaceTemporaryNode(node),
25402             annotation);
25403
25404         finished = true;
25405         context.enter(mode);
25406     };
25407
25408     // Finish the draw operation, removing the temporary node. If the way has enough
25409     // nodes to be valid, it's selected. Otherwise, return to browse mode.
25410     drawWay.finish = function() {
25411         context.pop();
25412         finished = true;
25413
25414         window.setTimeout(function() {
25415             context.map().dblclickEnable(true);
25416         }, 1000);
25417
25418         if (context.hasEntity(wayId)) {
25419             context.enter(
25420                 iD.modes.Select(context, [wayId])
25421                     .suppressMenu(true)
25422                     .newFeature(true));
25423         } else {
25424             context.enter(iD.modes.Browse(context));
25425         }
25426     };
25427
25428     // Cancel the draw operation and return to browse, deleting everything drawn.
25429     drawWay.cancel = function() {
25430         context.perform(
25431             d3.functor(baseGraph),
25432             t('operations.cancel_draw.annotation'));
25433
25434         window.setTimeout(function() {
25435             context.map().dblclickEnable(true);
25436         }, 1000);
25437
25438         finished = true;
25439         context.enter(iD.modes.Browse(context));
25440     };
25441
25442     drawWay.tail = function(text) {
25443         draw.tail(text);
25444         return drawWay;
25445     };
25446
25447     return drawWay;
25448 };
25449 iD.behavior.Edit = function(context) {
25450     function edit() {
25451         context.map()
25452             .minzoom(context.minEditableZoom());
25453     }
25454
25455     edit.off = function() {
25456         context.map()
25457             .minzoom(0);
25458     };
25459
25460     return edit;
25461 };
25462 iD.behavior.Hash = function(context) {
25463     var s0 = null, // cached location.hash
25464         lat = 90 - 1e-8; // allowable latitude range
25465
25466     var parser = function(map, s) {
25467         var q = iD.util.stringQs(s);
25468         var args = (q.map || '').split('/').map(Number);
25469         if (args.length < 3 || args.some(isNaN)) {
25470             return true; // replace bogus hash
25471         } else if (s !== formatter(map).slice(1)) {
25472             map.centerZoom([args[1],
25473                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
25474         }
25475     };
25476
25477     var formatter = function(map) {
25478         var mode = context.mode(),
25479             center = map.center(),
25480             zoom = map.zoom(),
25481             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
25482             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
25483             newParams = {};
25484
25485         if (mode && mode.id === 'browse') {
25486             delete q.id;
25487         } else {
25488             var selected = context.selectedIDs().filter(function(id) {
25489                 return !context.entity(id).isNew();
25490             });
25491             if (selected.length) {
25492                 newParams.id = selected.join(',');
25493             }
25494         }
25495
25496         newParams.map = zoom.toFixed(2) +
25497                 '/' + center[0].toFixed(precision) +
25498                 '/' + center[1].toFixed(precision);
25499
25500         return '#' + iD.util.qsString(_.assign(q, newParams), true);
25501     };
25502
25503     function update() {
25504         if (context.inIntro()) return;
25505         var s1 = formatter(context.map());
25506         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
25507     }
25508
25509     var throttledUpdate = _.throttle(update, 500);
25510
25511     function hashchange() {
25512         if (location.hash === s0) return; // ignore spurious hashchange events
25513         if (parser(context.map(), (s0 = location.hash).substring(1))) {
25514             update(); // replace bogus hash
25515         }
25516     }
25517
25518     function hash() {
25519         context.map()
25520             .on('move.hash', throttledUpdate);
25521
25522         context
25523             .on('enter.hash', throttledUpdate);
25524
25525         d3.select(window)
25526             .on('hashchange.hash', hashchange);
25527
25528         if (location.hash) {
25529             var q = iD.util.stringQs(location.hash.substring(1));
25530             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
25531             if (q.comment) context.storage('comment', q.comment);
25532             hashchange();
25533             if (q.map) hash.hadHash = true;
25534         }
25535     }
25536
25537     hash.off = function() {
25538         throttledUpdate.cancel();
25539
25540         context.map()
25541             .on('move.hash', null);
25542
25543         context
25544             .on('enter.hash', null);
25545
25546         d3.select(window)
25547             .on('hashchange.hash', null);
25548
25549         location.hash = '';
25550     };
25551
25552     return hash;
25553 };
25554 /*
25555    The hover behavior adds the `.hover` class on mouseover to all elements to which
25556    the identical datum is bound, and removes it on mouseout.
25557
25558    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
25559    representation may consist of several elements scattered throughout the DOM hierarchy.
25560    Only one of these elements can have the :hover pseudo-class, but all of them will
25561    have the .hover class.
25562  */
25563 iD.behavior.Hover = function() {
25564     var dispatch = d3.dispatch('hover'),
25565         selection,
25566         altDisables,
25567         target;
25568
25569     function keydown() {
25570         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
25571             dispatch.hover(null);
25572             selection.selectAll('.hover')
25573                 .classed('hover-suppressed', true)
25574                 .classed('hover', false);
25575         }
25576     }
25577
25578     function keyup() {
25579         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
25580             dispatch.hover(target ? target.id : null);
25581             selection.selectAll('.hover-suppressed')
25582                 .classed('hover-suppressed', false)
25583                 .classed('hover', true);
25584         }
25585     }
25586
25587     var hover = function(__) {
25588         selection = __;
25589
25590         function enter(d) {
25591             if (d === target) return;
25592
25593             target = d;
25594
25595             selection.selectAll('.hover')
25596                 .classed('hover', false);
25597             selection.selectAll('.hover-suppressed')
25598                 .classed('hover-suppressed', false);
25599
25600             if (target instanceof iD.Entity) {
25601                 var selector = '.' + target.id;
25602
25603                 if (target.type === 'relation') {
25604                     target.members.forEach(function(member) {
25605                         selector += ', .' + member.id;
25606                     });
25607                 }
25608
25609                 var suppressed = altDisables && d3.event && d3.event.altKey;
25610
25611                 selection.selectAll(selector)
25612                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
25613
25614                 dispatch.hover(target.id);
25615             } else {
25616                 dispatch.hover(null);
25617             }
25618         }
25619
25620         var down;
25621
25622         function mouseover() {
25623             if (down) return;
25624             var target = d3.event.target;
25625             enter(target ? target.__data__ : null);
25626         }
25627
25628         function mouseout() {
25629             if (down) return;
25630             var target = d3.event.relatedTarget;
25631             enter(target ? target.__data__ : null);
25632         }
25633
25634         function mousedown() {
25635             down = true;
25636             d3.select(window)
25637                 .on('mouseup.hover', mouseup);
25638         }
25639
25640         function mouseup() {
25641             down = false;
25642         }
25643
25644         selection
25645             .on('mouseover.hover', mouseover)
25646             .on('mouseout.hover', mouseout)
25647             .on('mousedown.hover', mousedown)
25648             .on('mouseup.hover', mouseup);
25649
25650         d3.select(window)
25651             .on('keydown.hover', keydown)
25652             .on('keyup.hover', keyup);
25653     };
25654
25655     hover.off = function(selection) {
25656         selection.selectAll('.hover')
25657             .classed('hover', false);
25658         selection.selectAll('.hover-suppressed')
25659             .classed('hover-suppressed', false);
25660
25661         selection
25662             .on('mouseover.hover', null)
25663             .on('mouseout.hover', null)
25664             .on('mousedown.hover', null)
25665             .on('mouseup.hover', null);
25666
25667         d3.select(window)
25668             .on('keydown.hover', null)
25669             .on('keyup.hover', null)
25670             .on('mouseup.hover', null);
25671     };
25672
25673     hover.altDisables = function(_) {
25674         if (!arguments.length) return altDisables;
25675         altDisables = _;
25676         return hover;
25677     };
25678
25679     return d3.rebind(hover, dispatch, 'on');
25680 };
25681 iD.behavior.Lasso = function(context) {
25682
25683     var behavior = function(selection) {
25684         var lasso;
25685
25686         function mousedown() {
25687             var button = 0;  // left
25688             if (d3.event.button === button && d3.event.shiftKey === true) {
25689                 lasso = null;
25690
25691                 selection
25692                     .on('mousemove.lasso', mousemove)
25693                     .on('mouseup.lasso', mouseup);
25694
25695                 d3.event.stopPropagation();
25696             }
25697         }
25698
25699         function mousemove() {
25700             if (!lasso) {
25701                 lasso = iD.ui.Lasso(context);
25702                 context.surface().call(lasso);
25703             }
25704
25705             lasso.p(context.mouse());
25706         }
25707
25708         function normalize(a, b) {
25709             return [
25710                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
25711                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
25712         }
25713
25714         function lassoed() {
25715             if (!lasso) return [];
25716
25717             var graph = context.graph(),
25718                 bounds = lasso.extent().map(context.projection.invert),
25719                 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
25720
25721             return _.map(context.intersects(extent).filter(function(entity) {
25722                 return entity.type === 'node' &&
25723                     iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
25724                     !context.features().isHidden(entity, graph, entity.geometry(graph));
25725             }), 'id');
25726         }
25727
25728         function mouseup() {
25729             selection
25730                 .on('mousemove.lasso', null)
25731                 .on('mouseup.lasso', null);
25732
25733             if (!lasso) return;
25734
25735             var ids = lassoed();
25736             lasso.close();
25737
25738             if (ids.length) {
25739                 context.enter(iD.modes.Select(context, ids));
25740             }
25741         }
25742
25743         selection
25744             .on('mousedown.lasso', mousedown);
25745     };
25746
25747     behavior.off = function(selection) {
25748         selection.on('mousedown.lasso', null);
25749     };
25750
25751     return behavior;
25752 };
25753 iD.behavior.Paste = function(context) {
25754     var keybinding = d3.keybinding('paste');
25755
25756     function omitTag(v, k) {
25757         return (
25758             k === 'phone' ||
25759             k === 'fax' ||
25760             k === 'email' ||
25761             k === 'website' ||
25762             k === 'url' ||
25763             k === 'note' ||
25764             k === 'description' ||
25765             k.indexOf('name') !== -1 ||
25766             k.indexOf('wiki') === 0 ||
25767             k.indexOf('addr:') === 0 ||
25768             k.indexOf('contact:') === 0
25769         );
25770     }
25771
25772     function doPaste() {
25773         d3.event.preventDefault();
25774         if (context.inIntro()) return;
25775
25776         var baseGraph = context.graph(),
25777             mouse = context.mouse(),
25778             projection = context.projection,
25779             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
25780
25781         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
25782
25783         var extent = iD.geo.Extent(),
25784             oldIDs = context.copyIDs(),
25785             oldGraph = context.copyGraph(),
25786             newIDs = [];
25787
25788         if (!oldIDs.length) return;
25789
25790         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
25791         context.perform(action);
25792
25793         var copies = action.copies();
25794         for (var id in copies) {
25795             var oldEntity = oldGraph.entity(id),
25796                 newEntity = copies[id];
25797
25798             extent._extend(oldEntity.extent(oldGraph));
25799             newIDs.push(newEntity.id);
25800             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
25801         }
25802
25803         // Put pasted objects where mouse pointer is..
25804         var center = projection(extent.center()),
25805             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
25806
25807         context.perform(iD.actions.Move(newIDs, delta, projection));
25808         context.enter(iD.modes.Move(context, newIDs, baseGraph));
25809     }
25810
25811     function paste() {
25812         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
25813         d3.select(document).call(keybinding);
25814         return paste;
25815     }
25816
25817     paste.off = function() {
25818         d3.select(document).call(keybinding.off);
25819     };
25820
25821     return paste;
25822 };
25823 iD.behavior.Select = function(context) {
25824     function keydown() {
25825         if (d3.event && d3.event.shiftKey) {
25826             context.surface()
25827                 .classed('behavior-multiselect', true);
25828         }
25829     }
25830
25831     function keyup() {
25832         if (!d3.event || !d3.event.shiftKey) {
25833             context.surface()
25834                 .classed('behavior-multiselect', false);
25835         }
25836     }
25837
25838     function click() {
25839         var datum = d3.event.target.__data__,
25840             lasso = d3.select('#surface .lasso').node(),
25841             mode = context.mode();
25842
25843         if (!(datum instanceof iD.Entity)) {
25844             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
25845                 context.enter(iD.modes.Browse(context));
25846
25847         } else if (!d3.event.shiftKey && !lasso) {
25848             // Avoid re-entering Select mode with same entity.
25849             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
25850                 context.enter(iD.modes.Select(context, [datum.id]));
25851             } else {
25852                 mode.suppressMenu(false).reselect();
25853             }
25854         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
25855             var selectedIDs = _.without(context.selectedIDs(), datum.id);
25856             context.enter(selectedIDs.length ?
25857                 iD.modes.Select(context, selectedIDs) :
25858                 iD.modes.Browse(context));
25859
25860         } else {
25861             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
25862         }
25863     }
25864
25865     var behavior = function(selection) {
25866         d3.select(window)
25867             .on('keydown.select', keydown)
25868             .on('keyup.select', keyup);
25869
25870         selection.on('click.select', click);
25871
25872         keydown();
25873     };
25874
25875     behavior.off = function(selection) {
25876         d3.select(window)
25877             .on('keydown.select', null)
25878             .on('keyup.select', null);
25879
25880         selection.on('click.select', null);
25881
25882         keyup();
25883     };
25884
25885     return behavior;
25886 };
25887 iD.behavior.Tail = function() {
25888     var text,
25889         container,
25890         xmargin = 25,
25891         tooltipSize = [0, 0],
25892         selectionSize = [0, 0];
25893
25894     function tail(selection) {
25895         if (!text) return;
25896
25897         d3.select(window)
25898             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
25899
25900         function show() {
25901             container.style('display', 'block');
25902             tooltipSize = container.dimensions();
25903         }
25904
25905         function mousemove() {
25906             if (container.style('display') === 'none') show();
25907             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
25908                 -tooltipSize[0] - xmargin : xmargin;
25909             container.classed('left', xoffset > 0);
25910             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
25911         }
25912
25913         function mouseleave() {
25914             if (d3.event.relatedTarget !== container.node()) {
25915                 container.style('display', 'none');
25916             }
25917         }
25918
25919         function mouseenter() {
25920             if (d3.event.relatedTarget !== container.node()) {
25921                 show();
25922             }
25923         }
25924
25925         container = d3.select(document.body)
25926             .append('div')
25927             .style('display', 'none')
25928             .attr('class', 'tail tooltip-inner');
25929
25930         container.append('div')
25931             .text(text);
25932
25933         selection
25934             .on('mousemove.tail', mousemove)
25935             .on('mouseenter.tail', mouseenter)
25936             .on('mouseleave.tail', mouseleave);
25937
25938         container
25939             .on('mousemove.tail', mousemove);
25940
25941         tooltipSize = container.dimensions();
25942         selectionSize = selection.dimensions();
25943     }
25944
25945     tail.off = function(selection) {
25946         if (!text) return;
25947
25948         container
25949             .on('mousemove.tail', null)
25950             .remove();
25951
25952         selection
25953             .on('mousemove.tail', null)
25954             .on('mouseenter.tail', null)
25955             .on('mouseleave.tail', null);
25956
25957         d3.select(window)
25958             .on('resize.tail', null);
25959     };
25960
25961     tail.text = function(_) {
25962         if (!arguments.length) return text;
25963         text = _;
25964         return tail;
25965     };
25966
25967     return tail;
25968 };
25969 iD.modes = {};
25970 iD.modes.AddArea = function(context) {
25971     var mode = {
25972         id: 'add-area',
25973         button: 'area',
25974         title: t('modes.add_area.title'),
25975         description: t('modes.add_area.description'),
25976         key: '3'
25977     };
25978
25979     var behavior = iD.behavior.AddWay(context)
25980             .tail(t('modes.add_area.tail'))
25981             .on('start', start)
25982             .on('startFromWay', startFromWay)
25983             .on('startFromNode', startFromNode),
25984         defaultTags = {area: 'yes'};
25985
25986     function start(loc) {
25987         var graph = context.graph(),
25988             node = iD.Node({loc: loc}),
25989             way = iD.Way({tags: defaultTags});
25990
25991         context.perform(
25992             iD.actions.AddEntity(node),
25993             iD.actions.AddEntity(way),
25994             iD.actions.AddVertex(way.id, node.id),
25995             iD.actions.AddVertex(way.id, node.id));
25996
25997         context.enter(iD.modes.DrawArea(context, way.id, graph));
25998     }
25999
26000     function startFromWay(loc, edge) {
26001         var graph = context.graph(),
26002             node = iD.Node({loc: loc}),
26003             way = iD.Way({tags: defaultTags});
26004
26005         context.perform(
26006             iD.actions.AddEntity(node),
26007             iD.actions.AddEntity(way),
26008             iD.actions.AddVertex(way.id, node.id),
26009             iD.actions.AddVertex(way.id, node.id),
26010             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
26011
26012         context.enter(iD.modes.DrawArea(context, way.id, graph));
26013     }
26014
26015     function startFromNode(node) {
26016         var graph = context.graph(),
26017             way = iD.Way({tags: defaultTags});
26018
26019         context.perform(
26020             iD.actions.AddEntity(way),
26021             iD.actions.AddVertex(way.id, node.id),
26022             iD.actions.AddVertex(way.id, node.id));
26023
26024         context.enter(iD.modes.DrawArea(context, way.id, graph));
26025     }
26026
26027     mode.enter = function() {
26028         context.install(behavior);
26029     };
26030
26031     mode.exit = function() {
26032         context.uninstall(behavior);
26033     };
26034
26035     return mode;
26036 };
26037 iD.modes.AddLine = function(context) {
26038     var mode = {
26039         id: 'add-line',
26040         button: 'line',
26041         title: t('modes.add_line.title'),
26042         description: t('modes.add_line.description'),
26043         key: '2'
26044     };
26045
26046     var behavior = iD.behavior.AddWay(context)
26047         .tail(t('modes.add_line.tail'))
26048         .on('start', start)
26049         .on('startFromWay', startFromWay)
26050         .on('startFromNode', startFromNode);
26051
26052     function start(loc) {
26053         var baseGraph = context.graph(),
26054             node = iD.Node({loc: loc}),
26055             way = iD.Way();
26056
26057         context.perform(
26058             iD.actions.AddEntity(node),
26059             iD.actions.AddEntity(way),
26060             iD.actions.AddVertex(way.id, node.id));
26061
26062         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
26063     }
26064
26065     function startFromWay(loc, edge) {
26066         var baseGraph = context.graph(),
26067             node = iD.Node({loc: loc}),
26068             way = iD.Way();
26069
26070         context.perform(
26071             iD.actions.AddEntity(node),
26072             iD.actions.AddEntity(way),
26073             iD.actions.AddVertex(way.id, node.id),
26074             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
26075
26076         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
26077     }
26078
26079     function startFromNode(node) {
26080         var baseGraph = context.graph(),
26081             way = iD.Way();
26082
26083         context.perform(
26084             iD.actions.AddEntity(way),
26085             iD.actions.AddVertex(way.id, node.id));
26086
26087         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
26088     }
26089
26090     mode.enter = function() {
26091         context.install(behavior);
26092     };
26093
26094     mode.exit = function() {
26095         context.uninstall(behavior);
26096     };
26097
26098     return mode;
26099 };
26100 iD.modes.AddPoint = function(context) {
26101     var mode = {
26102         id: 'add-point',
26103         button: 'point',
26104         title: t('modes.add_point.title'),
26105         description: t('modes.add_point.description'),
26106         key: '1'
26107     };
26108
26109     var behavior = iD.behavior.Draw(context)
26110         .tail(t('modes.add_point.tail'))
26111         .on('click', add)
26112         .on('clickWay', addWay)
26113         .on('clickNode', addNode)
26114         .on('cancel', cancel)
26115         .on('finish', cancel);
26116
26117     function add(loc) {
26118         var node = iD.Node({loc: loc});
26119
26120         context.perform(
26121             iD.actions.AddEntity(node),
26122             t('operations.add.annotation.point'));
26123
26124         context.enter(
26125             iD.modes.Select(context, [node.id])
26126                 .suppressMenu(true)
26127                 .newFeature(true));
26128     }
26129
26130     function addWay(loc) {
26131         add(loc);
26132     }
26133
26134     function addNode(node) {
26135         add(node.loc);
26136     }
26137
26138     function cancel() {
26139         context.enter(iD.modes.Browse(context));
26140     }
26141
26142     mode.enter = function() {
26143         context.install(behavior);
26144     };
26145
26146     mode.exit = function() {
26147         context.uninstall(behavior);
26148     };
26149
26150     return mode;
26151 };
26152 iD.modes.Browse = function(context) {
26153     var mode = {
26154         button: 'browse',
26155         id: 'browse',
26156         title: t('modes.browse.title'),
26157         description: t('modes.browse.description')
26158     }, sidebar;
26159
26160     var behaviors = [
26161         iD.behavior.Paste(context),
26162         iD.behavior.Hover(context)
26163             .on('hover', context.ui().sidebar.hover),
26164         iD.behavior.Select(context),
26165         iD.behavior.Lasso(context),
26166         iD.modes.DragNode(context).behavior];
26167
26168     mode.enter = function() {
26169         behaviors.forEach(function(behavior) {
26170             context.install(behavior);
26171         });
26172
26173         // Get focus on the body.
26174         if (document.activeElement && document.activeElement.blur) {
26175             document.activeElement.blur();
26176         }
26177
26178         if (sidebar) {
26179             context.ui().sidebar.show(sidebar);
26180         } else {
26181             context.ui().sidebar.select(null);
26182         }
26183     };
26184
26185     mode.exit = function() {
26186         context.ui().sidebar.hover.cancel();
26187         behaviors.forEach(function(behavior) {
26188             context.uninstall(behavior);
26189         });
26190
26191         if (sidebar) {
26192             context.ui().sidebar.hide();
26193         }
26194     };
26195
26196     mode.sidebar = function(_) {
26197         if (!arguments.length) return sidebar;
26198         sidebar = _;
26199         return mode;
26200     };
26201
26202     return mode;
26203 };
26204 iD.modes.DragNode = function(context) {
26205     var mode = {
26206         id: 'drag-node',
26207         button: 'browse'
26208     };
26209
26210     var nudgeInterval,
26211         activeIDs,
26212         wasMidpoint,
26213         cancelled,
26214         selectedIDs = [],
26215         hover = iD.behavior.Hover(context)
26216             .altDisables(true)
26217             .on('hover', context.ui().sidebar.hover),
26218         edit = iD.behavior.Edit(context);
26219
26220     function edge(point, size) {
26221         var pad = [30, 100, 30, 100];
26222         if (point[0] > size[0] - pad[0]) return [-10, 0];
26223         else if (point[0] < pad[2]) return [10, 0];
26224         else if (point[1] > size[1] - pad[1]) return [0, -10];
26225         else if (point[1] < pad[3]) return [0, 10];
26226         return null;
26227     }
26228
26229     function startNudge(nudge) {
26230         if (nudgeInterval) window.clearInterval(nudgeInterval);
26231         nudgeInterval = window.setInterval(function() {
26232             context.pan(nudge);
26233         }, 50);
26234     }
26235
26236     function stopNudge() {
26237         if (nudgeInterval) window.clearInterval(nudgeInterval);
26238         nudgeInterval = null;
26239     }
26240
26241     function moveAnnotation(entity) {
26242         return t('operations.move.annotation.' + entity.geometry(context.graph()));
26243     }
26244
26245     function connectAnnotation(entity) {
26246         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
26247     }
26248
26249     function origin(entity) {
26250         return context.projection(entity.loc);
26251     }
26252
26253     function start(entity) {
26254         cancelled = d3.event.sourceEvent.shiftKey ||
26255             context.features().hasHiddenConnections(entity, context.graph());
26256
26257         if (cancelled) return behavior.cancel();
26258
26259         wasMidpoint = entity.type === 'midpoint';
26260         if (wasMidpoint) {
26261             var midpoint = entity;
26262             entity = iD.Node();
26263             context.perform(iD.actions.AddMidpoint(midpoint, entity));
26264
26265              var vertex = context.surface()
26266                 .selectAll('.' + entity.id);
26267              behavior.target(vertex.node(), entity);
26268
26269         } else {
26270             context.perform(
26271                 iD.actions.Noop());
26272         }
26273
26274         activeIDs = _.map(context.graph().parentWays(entity), 'id');
26275         activeIDs.push(entity.id);
26276
26277         context.enter(mode);
26278     }
26279
26280     function datum() {
26281         if (d3.event.sourceEvent.altKey) {
26282             return {};
26283         }
26284
26285         return d3.event.sourceEvent.target.__data__ || {};
26286     }
26287
26288     // via https://gist.github.com/shawnbot/4166283
26289     function childOf(p, c) {
26290         if (p === c) return false;
26291         while (c && c !== p) c = c.parentNode;
26292         return c === p;
26293     }
26294
26295     function move(entity) {
26296         if (cancelled) return;
26297         d3.event.sourceEvent.stopPropagation();
26298
26299         var nudge = childOf(context.container().node(),
26300             d3.event.sourceEvent.toElement) &&
26301             edge(d3.event.point, context.map().dimensions());
26302
26303         if (nudge) startNudge(nudge);
26304         else stopNudge();
26305
26306         var loc = context.projection.invert(d3.event.point);
26307
26308         var d = datum();
26309         if (d.type === 'node' && d.id !== entity.id) {
26310             loc = d.loc;
26311         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
26312             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
26313         }
26314
26315         context.replace(
26316             iD.actions.MoveNode(entity.id, loc),
26317             moveAnnotation(entity));
26318     }
26319
26320     function end(entity) {
26321         if (cancelled) return;
26322
26323         var d = datum();
26324
26325         if (d.type === 'way') {
26326             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
26327             context.replace(
26328                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
26329                 connectAnnotation(d));
26330
26331         } else if (d.type === 'node' && d.id !== entity.id) {
26332             context.replace(
26333                 iD.actions.Connect([d.id, entity.id]),
26334                 connectAnnotation(d));
26335
26336         } else if (wasMidpoint) {
26337             context.replace(
26338                 iD.actions.Noop(),
26339                 t('operations.add.annotation.vertex'));
26340
26341         } else {
26342             context.replace(
26343                 iD.actions.Noop(),
26344                 moveAnnotation(entity));
26345         }
26346
26347         var reselection = selectedIDs.filter(function(id) {
26348             return context.graph().hasEntity(id);
26349         });
26350
26351         if (reselection.length) {
26352             context.enter(
26353                 iD.modes.Select(context, reselection)
26354                     .suppressMenu(true));
26355         } else {
26356             context.enter(iD.modes.Browse(context));
26357         }
26358     }
26359
26360     function cancel() {
26361         behavior.cancel();
26362         context.enter(iD.modes.Browse(context));
26363     }
26364
26365     function setActiveElements() {
26366         context.surface().selectAll(iD.util.entitySelector(activeIDs))
26367             .classed('active', true);
26368     }
26369
26370     var behavior = iD.behavior.drag()
26371         .delegate('g.node, g.point, g.midpoint')
26372         .surface(context.surface().node())
26373         .origin(origin)
26374         .on('start', start)
26375         .on('move', move)
26376         .on('end', end);
26377
26378     mode.enter = function() {
26379         context.install(hover);
26380         context.install(edit);
26381
26382         context.history()
26383             .on('undone.drag-node', cancel);
26384
26385         context.map()
26386             .on('drawn.drag-node', setActiveElements);
26387
26388         setActiveElements();
26389     };
26390
26391     mode.exit = function() {
26392         context.ui().sidebar.hover.cancel();
26393         context.uninstall(hover);
26394         context.uninstall(edit);
26395
26396         context.history()
26397             .on('undone.drag-node', null);
26398
26399         context.map()
26400             .on('drawn.drag-node', null);
26401
26402         context.surface()
26403             .selectAll('.active')
26404             .classed('active', false);
26405
26406         stopNudge();
26407     };
26408
26409     mode.selectedIDs = function(_) {
26410         if (!arguments.length) return selectedIDs;
26411         selectedIDs = _;
26412         return mode;
26413     };
26414
26415     mode.behavior = behavior;
26416
26417     return mode;
26418 };
26419 iD.modes.DrawArea = function(context, wayId, baseGraph) {
26420     var mode = {
26421         button: 'area',
26422         id: 'draw-area'
26423     };
26424
26425     var behavior;
26426
26427     mode.enter = function() {
26428         var way = context.entity(wayId),
26429             headId = way.nodes[way.nodes.length - 2],
26430             tailId = way.first();
26431
26432         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
26433             .tail(t('modes.draw_area.tail'));
26434
26435         var addNode = behavior.addNode;
26436
26437         behavior.addNode = function(node) {
26438             if (node.id === headId || node.id === tailId) {
26439                 behavior.finish();
26440             } else {
26441                 addNode(node);
26442             }
26443         };
26444
26445         context.install(behavior);
26446     };
26447
26448     mode.exit = function() {
26449         context.uninstall(behavior);
26450     };
26451
26452     mode.selectedIDs = function() {
26453         return [wayId];
26454     };
26455
26456     return mode;
26457 };
26458 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
26459     var mode = {
26460         button: 'line',
26461         id: 'draw-line'
26462     };
26463
26464     var behavior;
26465
26466     mode.enter = function() {
26467         var way = context.entity(wayId),
26468             index = (affix === 'prefix') ? 0 : undefined,
26469             headId = (affix === 'prefix') ? way.first() : way.last();
26470
26471         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
26472             .tail(t('modes.draw_line.tail'));
26473
26474         var addNode = behavior.addNode;
26475
26476         behavior.addNode = function(node) {
26477             if (node.id === headId) {
26478                 behavior.finish();
26479             } else {
26480                 addNode(node);
26481             }
26482         };
26483
26484         context.install(behavior);
26485     };
26486
26487     mode.exit = function() {
26488         context.uninstall(behavior);
26489     };
26490
26491     mode.selectedIDs = function() {
26492         return [wayId];
26493     };
26494
26495     return mode;
26496 };
26497 iD.modes.Move = function(context, entityIDs, baseGraph) {
26498     var mode = {
26499         id: 'move',
26500         button: 'browse'
26501     };
26502
26503     var keybinding = d3.keybinding('move'),
26504         edit = iD.behavior.Edit(context),
26505         annotation = entityIDs.length === 1 ?
26506             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
26507             t('operations.move.annotation.multiple'),
26508         cache,
26509         origin,
26510         nudgeInterval;
26511
26512     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
26513
26514     function edge(point, size) {
26515         var pad = [30, 100, 30, 100];
26516         if (point[0] > size[0] - pad[0]) return [-10, 0];
26517         else if (point[0] < pad[2]) return [10, 0];
26518         else if (point[1] > size[1] - pad[1]) return [0, -10];
26519         else if (point[1] < pad[3]) return [0, 10];
26520         return null;
26521     }
26522
26523     function startNudge(nudge) {
26524         if (nudgeInterval) window.clearInterval(nudgeInterval);
26525         nudgeInterval = window.setInterval(function() {
26526             context.pan(nudge);
26527
26528             var currMouse = context.mouse(),
26529                 origMouse = context.projection(origin),
26530                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
26531                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
26532
26533             context.overwrite(action, annotation);
26534
26535         }, 50);
26536     }
26537
26538     function stopNudge() {
26539         if (nudgeInterval) window.clearInterval(nudgeInterval);
26540         nudgeInterval = null;
26541     }
26542
26543     function move() {
26544         var currMouse = context.mouse(),
26545             origMouse = context.projection(origin),
26546             delta = vecSub(currMouse, origMouse),
26547             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
26548
26549         context.overwrite(action, annotation);
26550
26551         var nudge = edge(currMouse, context.map().dimensions());
26552         if (nudge) startNudge(nudge);
26553         else stopNudge();
26554     }
26555
26556     function finish() {
26557         d3.event.stopPropagation();
26558         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
26559         stopNudge();
26560     }
26561
26562     function cancel() {
26563         if (baseGraph) {
26564             while (context.graph() !== baseGraph) context.pop();
26565             context.enter(iD.modes.Browse(context));
26566         } else {
26567             context.pop();
26568             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
26569         }
26570         stopNudge();
26571     }
26572
26573     function undone() {
26574         context.enter(iD.modes.Browse(context));
26575     }
26576
26577     mode.enter = function() {
26578         origin = context.map().mouseCoordinates();
26579         cache = {};
26580
26581         context.install(edit);
26582
26583         context.perform(
26584             iD.actions.Noop(),
26585             annotation);
26586
26587         context.surface()
26588             .on('mousemove.move', move)
26589             .on('click.move', finish);
26590
26591         context.history()
26592             .on('undone.move', undone);
26593
26594         keybinding
26595             .on('⎋', cancel)
26596             .on('↩', finish);
26597
26598         d3.select(document)
26599             .call(keybinding);
26600     };
26601
26602     mode.exit = function() {
26603         stopNudge();
26604
26605         context.uninstall(edit);
26606
26607         context.surface()
26608             .on('mousemove.move', null)
26609             .on('click.move', null);
26610
26611         context.history()
26612             .on('undone.move', null);
26613
26614         keybinding.off();
26615     };
26616
26617     return mode;
26618 };
26619 iD.modes.RotateWay = function(context, wayId) {
26620     var mode = {
26621         id: 'rotate-way',
26622         button: 'browse'
26623     };
26624
26625     var keybinding = d3.keybinding('rotate-way'),
26626         edit = iD.behavior.Edit(context);
26627
26628     mode.enter = function() {
26629         context.install(edit);
26630
26631         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
26632             way = context.graph().entity(wayId),
26633             nodes = _.uniq(context.graph().childNodes(way)),
26634             points = nodes.map(function(n) { return context.projection(n.loc); }),
26635             pivot = d3.geom.polygon(points).centroid(),
26636             angle;
26637
26638         context.perform(
26639             iD.actions.Noop(),
26640             annotation);
26641
26642         function rotate() {
26643
26644             var mousePoint = context.mouse(),
26645                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
26646
26647             if (typeof angle === 'undefined') angle = newAngle;
26648
26649             context.replace(
26650                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
26651                 annotation);
26652
26653             angle = newAngle;
26654         }
26655
26656         function finish() {
26657             d3.event.stopPropagation();
26658             context.enter(iD.modes.Select(context, [wayId])
26659                 .suppressMenu(true));
26660         }
26661
26662         function cancel() {
26663             context.pop();
26664             context.enter(iD.modes.Select(context, [wayId])
26665                 .suppressMenu(true));
26666         }
26667
26668         function undone() {
26669             context.enter(iD.modes.Browse(context));
26670         }
26671
26672         context.surface()
26673             .on('mousemove.rotate-way', rotate)
26674             .on('click.rotate-way', finish);
26675
26676         context.history()
26677             .on('undone.rotate-way', undone);
26678
26679         keybinding
26680             .on('⎋', cancel)
26681             .on('↩', finish);
26682
26683         d3.select(document)
26684             .call(keybinding);
26685     };
26686
26687     mode.exit = function() {
26688         context.uninstall(edit);
26689
26690         context.surface()
26691             .on('mousemove.rotate-way', null)
26692             .on('click.rotate-way', null);
26693
26694         context.history()
26695             .on('undone.rotate-way', null);
26696
26697         keybinding.off();
26698     };
26699
26700     return mode;
26701 };
26702 iD.modes.Save = function(context) {
26703     var ui = iD.ui.Commit(context)
26704             .on('cancel', cancel)
26705             .on('save', save);
26706
26707     function cancel() {
26708         context.enter(iD.modes.Browse(context));
26709     }
26710
26711     function save(e, tryAgain) {
26712         function withChildNodes(ids, graph) {
26713             return _.uniq(_.reduce(ids, function(result, id) {
26714                 var e = graph.entity(id);
26715                 if (e.type === 'way') {
26716                     try {
26717                         var cn = graph.childNodes(e);
26718                         result.push.apply(result, _.map(_.filter(cn, 'version'), 'id'));
26719                     } catch(err) {
26720                         /* eslint-disable no-console */
26721                         if (typeof console !== 'undefined') console.error(err);
26722                         /* eslint-enable no-console */
26723                     }
26724                 }
26725                 return result;
26726             }, _.clone(ids)));
26727         }
26728
26729         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
26730             history = context.history(),
26731             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
26732             localGraph = context.graph(),
26733             remoteGraph = iD.Graph(history.base(), true),
26734             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
26735             toCheck = _.map(_.map(modified, 'entity'), 'id'),
26736             toLoad = withChildNodes(toCheck, localGraph),
26737             conflicts = [],
26738             errors = [];
26739
26740         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
26741         context.container().call(loading);
26742
26743         if (toCheck.length) {
26744             context.connection().loadMultiple(toLoad, loaded);
26745         } else {
26746             finalize();
26747         }
26748
26749
26750         // Reload modified entities into an alternate graph and check for conflicts..
26751         function loaded(err, result) {
26752             if (errors.length) return;
26753
26754             if (err) {
26755                 errors.push({
26756                     msg: err.responseText,
26757                     details: [ t('save.status_code', { code: err.status }) ]
26758                 });
26759                 showErrors();
26760
26761             } else {
26762                 var loadMore = [];
26763                 _.each(result.data, function(entity) {
26764                     remoteGraph.replace(entity);
26765                     toLoad = _.without(toLoad, entity.id);
26766
26767                     // Because loadMultiple doesn't download /full like loadEntity,
26768                     // need to also load children that aren't already being checked..
26769                     if (!entity.visible) return;
26770                     if (entity.type === 'way') {
26771                         loadMore.push.apply(loadMore,
26772                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
26773                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
26774                         loadMore.push.apply(loadMore,
26775                             _.difference(_.map(entity.members, 'id'), toCheck, toLoad, loadMore));
26776                     }
26777                 });
26778
26779                 if (loadMore.length) {
26780                     toLoad.push.apply(toLoad, loadMore);
26781                     context.connection().loadMultiple(loadMore, loaded);
26782                 }
26783
26784                 if (!toLoad.length) {
26785                     checkConflicts();
26786                 }
26787             }
26788         }
26789
26790
26791         function checkConflicts() {
26792             function choice(id, text, action) {
26793                 return { id: id, text: text, action: function() { history.replace(action); } };
26794             }
26795             function formatUser(d) {
26796                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
26797             }
26798             function entityName(entity) {
26799                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
26800             }
26801
26802             function compareVersions(local, remote) {
26803                 if (local.version !== remote.version) return false;
26804
26805                 if (local.type === 'way') {
26806                     var children = _.union(local.nodes, remote.nodes);
26807
26808                     for (var i = 0; i < children.length; i++) {
26809                         var a = localGraph.hasEntity(children[i]),
26810                             b = remoteGraph.hasEntity(children[i]);
26811
26812                         if (a && b && a.version !== b.version) return false;
26813                     }
26814                 }
26815
26816                 return true;
26817             }
26818
26819             _.each(toCheck, function(id) {
26820                 var local = localGraph.entity(id),
26821                     remote = remoteGraph.entity(id);
26822
26823                 if (compareVersions(local, remote)) return;
26824
26825                 var action = iD.actions.MergeRemoteChanges,
26826                     merge = action(id, localGraph, remoteGraph, formatUser);
26827
26828                 history.replace(merge);
26829
26830                 var mergeConflicts = merge.conflicts();
26831                 if (!mergeConflicts.length) return;  // merged safely
26832
26833                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
26834                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
26835                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
26836                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
26837
26838                 conflicts.push({
26839                     id: id,
26840                     name: entityName(local),
26841                     details: mergeConflicts,
26842                     chosen: 1,
26843                     choices: [
26844                         choice(id, keepMine, forceLocal),
26845                         choice(id, keepTheirs, forceRemote)
26846                     ]
26847                 });
26848             });
26849
26850             finalize();
26851         }
26852
26853
26854         function finalize() {
26855             if (conflicts.length) {
26856                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
26857                 showConflicts();
26858             } else if (errors.length) {
26859                 showErrors();
26860             } else {
26861                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
26862                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
26863                     context.connection().putChangeset(
26864                         changes,
26865                         e.comment,
26866                         history.imageryUsed(),
26867                         function(err, changeset_id) {
26868                             if (err) {
26869                                 errors.push({
26870                                     msg: err.responseText,
26871                                     details: [ t('save.status_code', { code: err.status }) ]
26872                                 });
26873                                 showErrors();
26874                             } else {
26875                                 history.clearSaved();
26876                                 success(e, changeset_id);
26877                                 // Add delay to allow for postgres replication #1646 #2678
26878                                 window.setTimeout(function() {
26879                                     loading.close();
26880                                     context.flush();
26881                                 }, 2500);
26882                             }
26883                         });
26884                 } else {        // changes were insignificant or reverted by user
26885                     loading.close();
26886                     context.flush();
26887                     cancel();
26888                 }
26889             }
26890         }
26891
26892
26893         function showConflicts() {
26894             var selection = context.container()
26895                 .select('#sidebar')
26896                 .append('div')
26897                 .attr('class','sidebar-component');
26898
26899             loading.close();
26900
26901             selection.call(iD.ui.Conflicts(context)
26902                 .list(conflicts)
26903                 .on('download', function() {
26904                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
26905                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
26906                     win.focus();
26907                 })
26908                 .on('cancel', function() {
26909                     history.pop();
26910                     selection.remove();
26911                 })
26912                 .on('save', function() {
26913                     for (var i = 0; i < conflicts.length; i++) {
26914                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
26915                             var entity = context.hasEntity(conflicts[i].id);
26916                             if (entity && entity.type === 'way') {
26917                                 var children = _.uniq(entity.nodes);
26918                                 for (var j = 0; j < children.length; j++) {
26919                                     history.replace(iD.actions.Revert(children[j]));
26920                                 }
26921                             }
26922                             history.replace(iD.actions.Revert(conflicts[i].id));
26923                         }
26924                     }
26925
26926                     selection.remove();
26927                     save(e, true);
26928                 })
26929             );
26930         }
26931
26932
26933         function showErrors() {
26934             var selection = iD.ui.confirm(context.container());
26935
26936             history.pop();
26937             loading.close();
26938
26939             selection
26940                 .select('.modal-section.header')
26941                 .append('h3')
26942                 .text(t('save.error'));
26943
26944             addErrors(selection, errors);
26945             selection.okButton();
26946         }
26947
26948
26949         function addErrors(selection, data) {
26950             var message = selection
26951                 .select('.modal-section.message-text');
26952
26953             var items = message
26954                 .selectAll('.error-container')
26955                 .data(data);
26956
26957             var enter = items.enter()
26958                 .append('div')
26959                 .attr('class', 'error-container');
26960
26961             enter
26962                 .append('a')
26963                 .attr('class', 'error-description')
26964                 .attr('href', '#')
26965                 .classed('hide-toggle', true)
26966                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
26967                 .on('click', function() {
26968                     var error = d3.select(this),
26969                         detail = d3.select(this.nextElementSibling),
26970                         exp = error.classed('expanded');
26971
26972                     detail.style('display', exp ? 'none' : 'block');
26973                     error.classed('expanded', !exp);
26974
26975                     d3.event.preventDefault();
26976                 });
26977
26978             var details = enter
26979                 .append('div')
26980                 .attr('class', 'error-detail-container')
26981                 .style('display', 'none');
26982
26983             details
26984                 .append('ul')
26985                 .attr('class', 'error-detail-list')
26986                 .selectAll('li')
26987                 .data(function(d) { return d.details || []; })
26988                 .enter()
26989                 .append('li')
26990                 .attr('class', 'error-detail-item')
26991                 .text(function(d) { return d; });
26992
26993             items.exit()
26994                 .remove();
26995         }
26996
26997     }
26998
26999
27000     function success(e, changeset_id) {
27001         context.enter(iD.modes.Browse(context)
27002             .sidebar(iD.ui.Success(context)
27003                 .changeset({
27004                     id: changeset_id,
27005                     comment: e.comment
27006                 })
27007                 .on('cancel', function() {
27008                     context.ui().sidebar.hide();
27009                 })));
27010     }
27011
27012     var mode = {
27013         id: 'save'
27014     };
27015
27016     mode.enter = function() {
27017         context.connection().authenticate(function(err) {
27018             if (err) {
27019                 cancel();
27020             } else {
27021                 context.ui().sidebar.show(ui);
27022             }
27023         });
27024     };
27025
27026     mode.exit = function() {
27027         context.ui().sidebar.hide();
27028     };
27029
27030     return mode;
27031 };
27032 iD.modes.Select = function(context, selectedIDs) {
27033     var mode = {
27034         id: 'select',
27035         button: 'browse'
27036     };
27037
27038     var keybinding = d3.keybinding('select'),
27039         timeout = null,
27040         behaviors = [
27041             iD.behavior.Copy(context),
27042             iD.behavior.Paste(context),
27043             iD.behavior.Breathe(context),
27044             iD.behavior.Hover(context),
27045             iD.behavior.Select(context),
27046             iD.behavior.Lasso(context),
27047             iD.modes.DragNode(context)
27048                 .selectedIDs(selectedIDs)
27049                 .behavior],
27050         inspector,
27051         radialMenu,
27052         newFeature = false,
27053         suppressMenu = false;
27054
27055     var wrap = context.container()
27056         .select('.inspector-wrap');
27057
27058
27059     function singular() {
27060         if (selectedIDs.length === 1) {
27061             return context.hasEntity(selectedIDs[0]);
27062         }
27063     }
27064
27065     function closeMenu() {
27066         if (radialMenu) {
27067             context.surface().call(radialMenu.close);
27068         }
27069     }
27070
27071     function positionMenu() {
27072         if (suppressMenu || !radialMenu) { return; }
27073
27074         var entity = singular();
27075         if (entity && context.geometry(entity.id) === 'relation') {
27076             suppressMenu = true;
27077         } else if (entity && entity.type === 'node') {
27078             radialMenu.center(context.projection(entity.loc));
27079         } else {
27080             var point = context.mouse(),
27081                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
27082             if (iD.geo.pointInPolygon(point, viewport)) {
27083                 radialMenu.center(point);
27084             } else {
27085                 suppressMenu = true;
27086             }
27087         }
27088     }
27089
27090     function showMenu() {
27091         closeMenu();
27092         if (!suppressMenu && radialMenu) {
27093             context.surface().call(radialMenu);
27094         }
27095     }
27096
27097     function toggleMenu() {
27098         if (d3.select('.radial-menu').empty()) {
27099             showMenu();
27100         } else {
27101             closeMenu();
27102         }
27103     }
27104
27105     mode.selectedIDs = function() {
27106         return selectedIDs;
27107     };
27108
27109     mode.reselect = function() {
27110         var surfaceNode = context.surface().node();
27111         if (surfaceNode.focus) { // FF doesn't support it
27112             surfaceNode.focus();
27113         }
27114
27115         positionMenu();
27116         showMenu();
27117     };
27118
27119     mode.newFeature = function(_) {
27120         if (!arguments.length) return newFeature;
27121         newFeature = _;
27122         return mode;
27123     };
27124
27125     mode.suppressMenu = function(_) {
27126         if (!arguments.length) return suppressMenu;
27127         suppressMenu = _;
27128         return mode;
27129     };
27130
27131     mode.enter = function() {
27132         function update() {
27133             closeMenu();
27134             if (_.some(selectedIDs, function(id) { return !context.hasEntity(id); })) {
27135                 // Exit mode if selected entity gets undone
27136                 context.enter(iD.modes.Browse(context));
27137             }
27138         }
27139
27140         function dblclick() {
27141             var target = d3.select(d3.event.target),
27142                 datum = target.datum();
27143
27144             if (datum instanceof iD.Way && !target.classed('fill')) {
27145                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
27146                     node = iD.Node();
27147
27148                 var prev = datum.nodes[choice.index - 1],
27149                     next = datum.nodes[choice.index];
27150
27151                 context.perform(
27152                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
27153                     t('operations.add.annotation.vertex'));
27154
27155                 d3.event.preventDefault();
27156                 d3.event.stopPropagation();
27157             }
27158         }
27159
27160         function selectElements(drawn) {
27161             var entity = singular();
27162             if (entity && context.geometry(entity.id) === 'relation') {
27163                 suppressMenu = true;
27164                 return;
27165             }
27166
27167             var selection = context.surface()
27168                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
27169
27170             if (selection.empty()) {
27171                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
27172                     context.enter(iD.modes.Browse(context));
27173                 }
27174             } else {
27175                 selection
27176                     .classed('selected', true);
27177             }
27178         }
27179
27180         function esc() {
27181             if (!context.inIntro()) {
27182                 context.enter(iD.modes.Browse(context));
27183             }
27184         }
27185
27186
27187         behaviors.forEach(function(behavior) {
27188             context.install(behavior);
27189         });
27190
27191         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
27192                 .map(function(o) { return o(selectedIDs, context); })
27193                 .filter(function(o) { return o.available(); });
27194
27195         operations.unshift(iD.operations.Delete(selectedIDs, context));
27196
27197         keybinding
27198             .on('⎋', esc, true)
27199             .on('space', toggleMenu);
27200
27201         operations.forEach(function(operation) {
27202             operation.keys.forEach(function(key) {
27203                 keybinding.on(key, function() {
27204                     if (!(context.inIntro() || operation.disabled())) {
27205                         operation();
27206                     }
27207                 });
27208             });
27209         });
27210
27211         d3.select(document)
27212             .call(keybinding);
27213
27214         radialMenu = iD.ui.RadialMenu(context, operations);
27215
27216         context.ui().sidebar
27217             .select(singular() ? singular().id : null, newFeature);
27218
27219         context.history()
27220             .on('undone.select', update)
27221             .on('redone.select', update);
27222
27223         context.map()
27224             .on('move.select', closeMenu)
27225             .on('drawn.select', selectElements);
27226
27227         selectElements();
27228
27229         var show = d3.event && !suppressMenu;
27230
27231         if (show) {
27232             positionMenu();
27233         }
27234
27235         timeout = window.setTimeout(function() {
27236             if (show) {
27237                 showMenu();
27238             }
27239
27240             context.surface()
27241                 .on('dblclick.select', dblclick);
27242         }, 200);
27243
27244         if (selectedIDs.length > 1) {
27245             var entities = iD.ui.SelectionList(context, selectedIDs);
27246             context.ui().sidebar.show(entities);
27247         }
27248     };
27249
27250     mode.exit = function() {
27251         if (timeout) window.clearTimeout(timeout);
27252
27253         if (inspector) wrap.call(inspector.close);
27254
27255         behaviors.forEach(function(behavior) {
27256             context.uninstall(behavior);
27257         });
27258
27259         keybinding.off();
27260         closeMenu();
27261         radialMenu = undefined;
27262
27263         context.history()
27264             .on('undone.select', null)
27265             .on('redone.select', null);
27266
27267         context.surface()
27268             .on('dblclick.select', null)
27269             .selectAll('.selected')
27270             .classed('selected', false);
27271
27272         context.map().on('drawn.select', null);
27273         context.ui().sidebar.hide();
27274     };
27275
27276     return mode;
27277 };
27278 iD.operations = {};
27279 iD.operations.Circularize = function(selectedIDs, context) {
27280     var entityId = selectedIDs[0],
27281         entity = context.entity(entityId),
27282         extent = entity.extent(context.graph()),
27283         geometry = context.geometry(entityId),
27284         action = iD.actions.Circularize(entityId, context.projection);
27285
27286     var operation = function() {
27287         var annotation = t('operations.circularize.annotation.' + geometry);
27288         context.perform(action, annotation);
27289     };
27290
27291     operation.available = function() {
27292         return selectedIDs.length === 1 &&
27293             entity.type === 'way' &&
27294             _.uniq(entity.nodes).length > 1;
27295     };
27296
27297     operation.disabled = function() {
27298         var reason;
27299         if (extent.percentContainedIn(context.extent()) < 0.8) {
27300             reason = 'too_large';
27301         } else if (context.hasHiddenConnections(entityId)) {
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.circularize.' + disable) :
27311             t('operations.circularize.description.' + geometry);
27312     };
27313
27314     operation.id = 'circularize';
27315     operation.keys = [t('operations.circularize.key')];
27316     operation.title = t('operations.circularize.title');
27317
27318     return operation;
27319 };
27320 iD.operations.Continue = function(selectedIDs, context) {
27321     var graph = context.graph(),
27322         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
27323         geometries = _.extend({line: [], vertex: []},
27324             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
27325         vertex = geometries.vertex[0];
27326
27327     function candidateWays() {
27328         return graph.parentWays(vertex).filter(function(parent) {
27329             return parent.geometry(graph) === 'line' &&
27330                 parent.affix(vertex.id) &&
27331                 (geometries.line.length === 0 || geometries.line[0] === parent);
27332         });
27333     }
27334
27335     var operation = function() {
27336         var candidate = candidateWays()[0];
27337         context.enter(iD.modes.DrawLine(
27338             context,
27339             candidate.id,
27340             context.graph(),
27341             candidate.affix(vertex.id)));
27342     };
27343
27344     operation.available = function() {
27345         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
27346             !context.features().hasHiddenConnections(vertex, context.graph());
27347     };
27348
27349     operation.disabled = function() {
27350         var candidates = candidateWays();
27351         if (candidates.length === 0)
27352             return 'not_eligible';
27353         if (candidates.length > 1)
27354             return 'multiple';
27355     };
27356
27357     operation.tooltip = function() {
27358         var disable = operation.disabled();
27359         return disable ?
27360             t('operations.continue.' + disable) :
27361             t('operations.continue.description');
27362     };
27363
27364     operation.id = 'continue';
27365     operation.keys = [t('operations.continue.key')];
27366     operation.title = t('operations.continue.title');
27367
27368     return operation;
27369 };
27370 iD.operations.Delete = function(selectedIDs, context) {
27371     var action = iD.actions.DeleteMultiple(selectedIDs);
27372
27373     var operation = function() {
27374         var annotation,
27375             nextSelectedID;
27376
27377         if (selectedIDs.length > 1) {
27378             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
27379
27380         } else {
27381             var id = selectedIDs[0],
27382                 entity = context.entity(id),
27383                 geometry = context.geometry(id),
27384                 parents = context.graph().parentWays(entity),
27385                 parent = parents[0];
27386
27387             annotation = t('operations.delete.annotation.' + geometry);
27388
27389             // Select the next closest node in the way.
27390             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
27391                 var nodes = parent.nodes,
27392                     i = nodes.indexOf(id);
27393
27394                 if (i === 0) {
27395                     i++;
27396                 } else if (i === nodes.length - 1) {
27397                     i--;
27398                 } else {
27399                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
27400                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
27401                     i = a < b ? i - 1 : i + 1;
27402                 }
27403
27404                 nextSelectedID = nodes[i];
27405             }
27406         }
27407
27408         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
27409             context.enter(iD.modes.Select(context, [nextSelectedID]));
27410         } else {
27411             context.enter(iD.modes.Browse(context));
27412         }
27413
27414         context.perform(
27415             action,
27416             annotation);
27417     };
27418
27419     operation.available = function() {
27420         return true;
27421     };
27422
27423     operation.disabled = function() {
27424         var reason;
27425         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27426             reason = 'connected_to_hidden';
27427         }
27428         return action.disabled(context.graph()) || reason;
27429     };
27430
27431     operation.tooltip = function() {
27432         var disable = operation.disabled();
27433         return disable ?
27434             t('operations.delete.' + disable) :
27435             t('operations.delete.description');
27436     };
27437
27438     operation.id = 'delete';
27439     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
27440     operation.title = t('operations.delete.title');
27441
27442     return operation;
27443 };
27444 iD.operations.Disconnect = function(selectedIDs, context) {
27445     var vertices = _.filter(selectedIDs, function vertex(entityId) {
27446         return context.geometry(entityId) === 'vertex';
27447     });
27448
27449     var entityId = vertices[0],
27450         action = iD.actions.Disconnect(entityId);
27451
27452     if (selectedIDs.length > 1) {
27453         action.limitWays(_.without(selectedIDs, entityId));
27454     }
27455
27456     var operation = function() {
27457         context.perform(action, t('operations.disconnect.annotation'));
27458     };
27459
27460     operation.available = function() {
27461         return vertices.length === 1;
27462     };
27463
27464     operation.disabled = function() {
27465         var reason;
27466         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27467             reason = 'connected_to_hidden';
27468         }
27469         return action.disabled(context.graph()) || reason;
27470     };
27471
27472     operation.tooltip = function() {
27473         var disable = operation.disabled();
27474         return disable ?
27475             t('operations.disconnect.' + disable) :
27476             t('operations.disconnect.description');
27477     };
27478
27479     operation.id = 'disconnect';
27480     operation.keys = [t('operations.disconnect.key')];
27481     operation.title = t('operations.disconnect.title');
27482
27483     return operation;
27484 };
27485 iD.operations.Merge = function(selectedIDs, context) {
27486     var join = iD.actions.Join(selectedIDs),
27487         merge = iD.actions.Merge(selectedIDs),
27488         mergePolygon = iD.actions.MergePolygon(selectedIDs);
27489
27490     var operation = function() {
27491         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
27492             action;
27493
27494         if (!join.disabled(context.graph())) {
27495             action = join;
27496         } else if (!merge.disabled(context.graph())) {
27497             action = merge;
27498         } else {
27499             action = mergePolygon;
27500         }
27501
27502         context.perform(action, annotation);
27503         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
27504             .suppressMenu(true));
27505     };
27506
27507     operation.available = function() {
27508         return selectedIDs.length >= 2;
27509     };
27510
27511     operation.disabled = function() {
27512         return join.disabled(context.graph()) &&
27513             merge.disabled(context.graph()) &&
27514             mergePolygon.disabled(context.graph());
27515     };
27516
27517     operation.tooltip = function() {
27518         var j = join.disabled(context.graph()),
27519             m = merge.disabled(context.graph()),
27520             p = mergePolygon.disabled(context.graph());
27521
27522         if (j === 'restriction' && m && p)
27523             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
27524
27525         if (p === 'incomplete_relation' && j && m)
27526             return t('operations.merge.incomplete_relation');
27527
27528         if (j && m && p)
27529             return t('operations.merge.' + j);
27530
27531         return t('operations.merge.description');
27532     };
27533
27534     operation.id = 'merge';
27535     operation.keys = [t('operations.merge.key')];
27536     operation.title = t('operations.merge.title');
27537
27538     return operation;
27539 };
27540 iD.operations.Move = function(selectedIDs, context) {
27541     var extent = selectedIDs.reduce(function(extent, id) {
27542             return extent.extend(context.entity(id).extent(context.graph()));
27543         }, iD.geo.Extent());
27544
27545     var operation = function() {
27546         context.enter(iD.modes.Move(context, selectedIDs));
27547     };
27548
27549     operation.available = function() {
27550         return selectedIDs.length > 1 ||
27551             context.entity(selectedIDs[0]).type !== 'node';
27552     };
27553
27554     operation.disabled = function() {
27555         var reason;
27556         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
27557             reason = 'too_large';
27558         } else if (_.some(selectedIDs, context.hasHiddenConnections)) {
27559             reason = 'connected_to_hidden';
27560         }
27561         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
27562     };
27563
27564     operation.tooltip = function() {
27565         var disable = operation.disabled();
27566         return disable ?
27567             t('operations.move.' + disable) :
27568             t('operations.move.description');
27569     };
27570
27571     operation.id = 'move';
27572     operation.keys = [t('operations.move.key')];
27573     operation.title = t('operations.move.title');
27574
27575     return operation;
27576 };
27577 iD.operations.Orthogonalize = function(selectedIDs, context) {
27578     var entityId = selectedIDs[0],
27579         entity = context.entity(entityId),
27580         extent = entity.extent(context.graph()),
27581         geometry = context.geometry(entityId),
27582         action = iD.actions.Orthogonalize(entityId, context.projection);
27583
27584     var operation = function() {
27585         var annotation = t('operations.orthogonalize.annotation.' + geometry);
27586         context.perform(action, annotation);
27587     };
27588
27589     operation.available = function() {
27590         return selectedIDs.length === 1 &&
27591             entity.type === 'way' &&
27592             entity.isClosed() &&
27593             _.uniq(entity.nodes).length > 2;
27594     };
27595
27596     operation.disabled = function() {
27597         var reason;
27598         if (extent.percentContainedIn(context.extent()) < 0.8) {
27599             reason = 'too_large';
27600         } else if (context.hasHiddenConnections(entityId)) {
27601             reason = 'connected_to_hidden';
27602         }
27603         return action.disabled(context.graph()) || reason;
27604     };
27605
27606     operation.tooltip = function() {
27607         var disable = operation.disabled();
27608         return disable ?
27609             t('operations.orthogonalize.' + disable) :
27610             t('operations.orthogonalize.description.' + geometry);
27611     };
27612
27613     operation.id = 'orthogonalize';
27614     operation.keys = [t('operations.orthogonalize.key')];
27615     operation.title = t('operations.orthogonalize.title');
27616
27617     return operation;
27618 };
27619 iD.operations.Reverse = function(selectedIDs, context) {
27620     var entityId = selectedIDs[0];
27621
27622     var operation = function() {
27623         context.perform(
27624             iD.actions.Reverse(entityId),
27625             t('operations.reverse.annotation'));
27626     };
27627
27628     operation.available = function() {
27629         return selectedIDs.length === 1 &&
27630             context.geometry(entityId) === 'line';
27631     };
27632
27633     operation.disabled = function() {
27634         return false;
27635     };
27636
27637     operation.tooltip = function() {
27638         return t('operations.reverse.description');
27639     };
27640
27641     operation.id = 'reverse';
27642     operation.keys = [t('operations.reverse.key')];
27643     operation.title = t('operations.reverse.title');
27644
27645     return operation;
27646 };
27647 iD.operations.Rotate = function(selectedIDs, context) {
27648     var entityId = selectedIDs[0],
27649         entity = context.entity(entityId),
27650         extent = entity.extent(context.graph()),
27651         geometry = context.geometry(entityId);
27652
27653     var operation = function() {
27654         context.enter(iD.modes.RotateWay(context, entityId));
27655     };
27656
27657     operation.available = function() {
27658         if (selectedIDs.length !== 1 || entity.type !== 'way')
27659             return false;
27660         if (geometry === 'area')
27661             return true;
27662         if (entity.isClosed() &&
27663             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
27664             return true;
27665         return false;
27666     };
27667
27668     operation.disabled = function() {
27669         if (extent.percentContainedIn(context.extent()) < 0.8) {
27670             return 'too_large';
27671         } else if (context.hasHiddenConnections(entityId)) {
27672             return 'connected_to_hidden';
27673         } else {
27674             return false;
27675         }
27676     };
27677
27678     operation.tooltip = function() {
27679         var disable = operation.disabled();
27680         return disable ?
27681             t('operations.rotate.' + disable) :
27682             t('operations.rotate.description');
27683     };
27684
27685     operation.id = 'rotate';
27686     operation.keys = [t('operations.rotate.key')];
27687     operation.title = t('operations.rotate.title');
27688
27689     return operation;
27690 };
27691 iD.operations.Split = function(selectedIDs, context) {
27692     var vertices = _.filter(selectedIDs, function vertex(entityId) {
27693         return context.geometry(entityId) === 'vertex';
27694     });
27695
27696     var entityId = vertices[0],
27697         action = iD.actions.Split(entityId);
27698
27699     if (selectedIDs.length > 1) {
27700         action.limitWays(_.without(selectedIDs, entityId));
27701     }
27702
27703     var operation = function() {
27704         var annotation;
27705
27706         var ways = action.ways(context.graph());
27707         if (ways.length === 1) {
27708             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
27709         } else {
27710             annotation = t('operations.split.annotation.multiple', {n: ways.length});
27711         }
27712
27713         var difference = context.perform(action, annotation);
27714         context.enter(iD.modes.Select(context, difference.extantIDs()));
27715     };
27716
27717     operation.available = function() {
27718         return vertices.length === 1;
27719     };
27720
27721     operation.disabled = function() {
27722         var reason;
27723         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27724             reason = 'connected_to_hidden';
27725         }
27726         return action.disabled(context.graph()) || reason;
27727     };
27728
27729     operation.tooltip = function() {
27730         var disable = operation.disabled();
27731         if (disable) {
27732             return t('operations.split.' + disable);
27733         }
27734
27735         var ways = action.ways(context.graph());
27736         if (ways.length === 1) {
27737             return t('operations.split.description.' + context.geometry(ways[0].id));
27738         } else {
27739             return t('operations.split.description.multiple');
27740         }
27741     };
27742
27743     operation.id = 'split';
27744     operation.keys = [t('operations.split.key')];
27745     operation.title = t('operations.split.title');
27746
27747     return operation;
27748 };
27749 iD.operations.Straighten = function(selectedIDs, context) {
27750     var entityId = selectedIDs[0],
27751         action = iD.actions.Straighten(entityId, context.projection);
27752
27753     function operation() {
27754         var annotation = t('operations.straighten.annotation');
27755         context.perform(action, annotation);
27756     }
27757
27758     operation.available = function() {
27759         var entity = context.entity(entityId);
27760         return selectedIDs.length === 1 &&
27761             entity.type === 'way' &&
27762             !entity.isClosed() &&
27763             _.uniq(entity.nodes).length > 2;
27764     };
27765
27766     operation.disabled = function() {
27767         var reason;
27768         if (context.hasHiddenConnections(entityId)) {
27769             reason = 'connected_to_hidden';
27770         }
27771         return action.disabled(context.graph()) || reason;
27772     };
27773
27774     operation.tooltip = function() {
27775         var disable = operation.disabled();
27776         return disable ?
27777             t('operations.straighten.' + disable) :
27778             t('operations.straighten.description');
27779     };
27780
27781     operation.id = 'straighten';
27782     operation.keys = [t('operations.straighten.key')];
27783     operation.title = t('operations.straighten.title');
27784
27785     return operation;
27786 };
27787 iD.Connection = function(useHttps) {
27788     if (typeof useHttps !== 'boolean') {
27789       useHttps = window.location.protocol === 'https:';
27790     }
27791
27792     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
27793         protocol = useHttps ? 'https:' : 'http:',
27794         url = protocol + '//www.openstreetmap.org',
27795         connection = {},
27796         inflight = {},
27797         loadedTiles = {},
27798         tileZoom = 16,
27799         oauth = osmAuth({
27800             url: protocol + '//www.openstreetmap.org',
27801             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
27802             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
27803             loading: authenticating,
27804             done: authenticated
27805         }),
27806         ndStr = 'nd',
27807         tagStr = 'tag',
27808         memberStr = 'member',
27809         nodeStr = 'node',
27810         wayStr = 'way',
27811         relationStr = 'relation',
27812         userDetails,
27813         off;
27814
27815
27816     connection.changesetURL = function(changesetId) {
27817         return url + '/changeset/' + changesetId;
27818     };
27819
27820     connection.changesetsURL = function(center, zoom) {
27821         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
27822         return url + '/history#map=' +
27823             Math.floor(zoom) + '/' +
27824             center[1].toFixed(precision) + '/' +
27825             center[0].toFixed(precision);
27826     };
27827
27828     connection.entityURL = function(entity) {
27829         return url + '/' + entity.type + '/' + entity.osmId();
27830     };
27831
27832     connection.userURL = function(username) {
27833         return url + '/user/' + username;
27834     };
27835
27836     connection.loadFromURL = function(url, callback) {
27837         function done(err, dom) {
27838             return callback(err, parse(dom));
27839         }
27840         return d3.xml(url).get(done);
27841     };
27842
27843     connection.loadEntity = function(id, callback) {
27844         var type = iD.Entity.id.type(id),
27845             osmID = iD.Entity.id.toOSM(id);
27846
27847         connection.loadFromURL(
27848             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
27849             function(err, entities) {
27850                 if (callback) callback(err, {data: entities});
27851             });
27852     };
27853
27854     connection.loadEntityVersion = function(id, version, callback) {
27855         var type = iD.Entity.id.type(id),
27856             osmID = iD.Entity.id.toOSM(id);
27857
27858         connection.loadFromURL(
27859             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
27860             function(err, entities) {
27861                 if (callback) callback(err, {data: entities});
27862             });
27863     };
27864
27865     connection.loadMultiple = function(ids, callback) {
27866         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
27867             var type = k + 's',
27868                 osmIDs = _.map(v, iD.Entity.id.toOSM);
27869
27870             _.each(_.chunk(osmIDs, 150), function(arr) {
27871                 connection.loadFromURL(
27872                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
27873                     function(err, entities) {
27874                         if (callback) callback(err, {data: entities});
27875                     });
27876             });
27877         });
27878     };
27879
27880     function authenticating() {
27881         event.authenticating();
27882     }
27883
27884     function authenticated() {
27885         event.authenticated();
27886     }
27887
27888     function getLoc(attrs) {
27889         var lon = attrs.lon && attrs.lon.value,
27890             lat = attrs.lat && attrs.lat.value;
27891         return [parseFloat(lon), parseFloat(lat)];
27892     }
27893
27894     function getNodes(obj) {
27895         var elems = obj.getElementsByTagName(ndStr),
27896             nodes = new Array(elems.length);
27897         for (var i = 0, l = elems.length; i < l; i++) {
27898             nodes[i] = 'n' + elems[i].attributes.ref.value;
27899         }
27900         return nodes;
27901     }
27902
27903     function getTags(obj) {
27904         var elems = obj.getElementsByTagName(tagStr),
27905             tags = {};
27906         for (var i = 0, l = elems.length; i < l; i++) {
27907             var attrs = elems[i].attributes;
27908             tags[attrs.k.value] = attrs.v.value;
27909         }
27910         return tags;
27911     }
27912
27913     function getMembers(obj) {
27914         var elems = obj.getElementsByTagName(memberStr),
27915             members = new Array(elems.length);
27916         for (var i = 0, l = elems.length; i < l; i++) {
27917             var attrs = elems[i].attributes;
27918             members[i] = {
27919                 id: attrs.type.value[0] + attrs.ref.value,
27920                 type: attrs.type.value,
27921                 role: attrs.role.value
27922             };
27923         }
27924         return members;
27925     }
27926
27927     function getVisible(attrs) {
27928         return (!attrs.visible || attrs.visible.value !== 'false');
27929     }
27930
27931     var parsers = {
27932         node: function nodeData(obj) {
27933             var attrs = obj.attributes;
27934             return new iD.Node({
27935                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
27936                 loc: getLoc(attrs),
27937                 version: attrs.version.value,
27938                 user: attrs.user && attrs.user.value,
27939                 tags: getTags(obj),
27940                 visible: getVisible(attrs)
27941             });
27942         },
27943
27944         way: function wayData(obj) {
27945             var attrs = obj.attributes;
27946             return new iD.Way({
27947                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
27948                 version: attrs.version.value,
27949                 user: attrs.user && attrs.user.value,
27950                 tags: getTags(obj),
27951                 nodes: getNodes(obj),
27952                 visible: getVisible(attrs)
27953             });
27954         },
27955
27956         relation: function relationData(obj) {
27957             var attrs = obj.attributes;
27958             return new iD.Relation({
27959                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
27960                 version: attrs.version.value,
27961                 user: attrs.user && attrs.user.value,
27962                 tags: getTags(obj),
27963                 members: getMembers(obj),
27964                 visible: getVisible(attrs)
27965             });
27966         }
27967     };
27968
27969     function parse(dom) {
27970         if (!dom || !dom.childNodes) return;
27971
27972         var root = dom.childNodes[0],
27973             children = root.childNodes,
27974             entities = [];
27975
27976         for (var i = 0, l = children.length; i < l; i++) {
27977             var child = children[i],
27978                 parser = parsers[child.nodeName];
27979             if (parser) {
27980                 entities.push(parser(child));
27981             }
27982         }
27983
27984         return entities;
27985     }
27986
27987     connection.authenticated = function() {
27988         return oauth.authenticated();
27989     };
27990
27991     // Generate Changeset XML. Returns a string.
27992     connection.changesetJXON = function(tags) {
27993         return {
27994             osm: {
27995                 changeset: {
27996                     tag: _.map(tags, function(value, key) {
27997                         return { '@k': key, '@v': value };
27998                     }),
27999                     '@version': 0.6,
28000                     '@generator': 'iD'
28001                 }
28002             }
28003         };
28004     };
28005
28006     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
28007     // XML. Returns a string.
28008     connection.osmChangeJXON = function(changeset_id, changes) {
28009         function nest(x, order) {
28010             var groups = {};
28011             for (var i = 0; i < x.length; i++) {
28012                 var tagName = Object.keys(x[i])[0];
28013                 if (!groups[tagName]) groups[tagName] = [];
28014                 groups[tagName].push(x[i][tagName]);
28015             }
28016             var ordered = {};
28017             order.forEach(function(o) {
28018                 if (groups[o]) ordered[o] = groups[o];
28019             });
28020             return ordered;
28021         }
28022
28023         function rep(entity) {
28024             return entity.asJXON(changeset_id);
28025         }
28026
28027         return {
28028             osmChange: {
28029                 '@version': 0.6,
28030                 '@generator': 'iD',
28031                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
28032                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
28033                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
28034             }
28035         };
28036     };
28037
28038     connection.changesetTags = function(comment, imageryUsed) {
28039         var detected = iD.detect(),
28040             tags = {
28041                 created_by: 'iD ' + iD.version,
28042                 imagery_used: imageryUsed.join(';').substr(0, 255),
28043                 host: (window.location.origin + window.location.pathname).substr(0, 255),
28044                 locale: detected.locale
28045             };
28046
28047         if (comment) {
28048             tags.comment = comment.substr(0, 255);
28049         }
28050
28051         return tags;
28052     };
28053
28054     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
28055         oauth.xhr({
28056                 method: 'PUT',
28057                 path: '/api/0.6/changeset/create',
28058                 options: { header: { 'Content-Type': 'text/xml' } },
28059                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
28060             }, function(err, changeset_id) {
28061                 if (err) return callback(err);
28062                 oauth.xhr({
28063                     method: 'POST',
28064                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
28065                     options: { header: { 'Content-Type': 'text/xml' } },
28066                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
28067                 }, function(err) {
28068                     if (err) return callback(err);
28069                     // POST was successful, safe to call the callback.
28070                     // Still attempt to close changeset, but ignore response because #2667
28071                     // Add delay to allow for postgres replication #1646 #2678
28072                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
28073                     oauth.xhr({
28074                         method: 'PUT',
28075                         path: '/api/0.6/changeset/' + changeset_id + '/close',
28076                         options: { header: { 'Content-Type': 'text/xml' } }
28077                     }, d3.functor(true));
28078                 });
28079             });
28080     };
28081
28082     connection.userDetails = function(callback) {
28083         if (userDetails) {
28084             callback(undefined, userDetails);
28085             return;
28086         }
28087
28088         function done(err, user_details) {
28089             if (err) return callback(err);
28090
28091             var u = user_details.getElementsByTagName('user')[0],
28092                 img = u.getElementsByTagName('img'),
28093                 image_url = '';
28094
28095             if (img && img[0] && img[0].getAttribute('href')) {
28096                 image_url = img[0].getAttribute('href');
28097             }
28098
28099             userDetails = {
28100                 display_name: u.attributes.display_name.value,
28101                 image_url: image_url,
28102                 id: u.attributes.id.value
28103             };
28104
28105             callback(undefined, userDetails);
28106         }
28107
28108         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
28109     };
28110
28111     connection.userChangesets = function(callback) {
28112         connection.userDetails(function(err, user) {
28113             if (err) return callback(err);
28114
28115             function done(changesets) {
28116                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
28117                     function (changeset) {
28118                         return { tags: getTags(changeset) };
28119                     }));
28120             }
28121
28122             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
28123                 .on('load', done)
28124                 .on('error', callback);
28125         });
28126     };
28127
28128     connection.status = function(callback) {
28129         function done(capabilities) {
28130             var apiStatus = capabilities.getElementsByTagName('status');
28131             callback(undefined, apiStatus[0].getAttribute('api'));
28132         }
28133         d3.xml(url + '/api/capabilities').get()
28134             .on('load', done)
28135             .on('error', callback);
28136     };
28137
28138     function abortRequest(i) { i.abort(); }
28139
28140     connection.tileZoom = function(_) {
28141         if (!arguments.length) return tileZoom;
28142         tileZoom = _;
28143         return connection;
28144     };
28145
28146     connection.loadTiles = function(projection, dimensions, callback) {
28147
28148         if (off) return;
28149
28150         var s = projection.scale() * 2 * Math.PI,
28151             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
28152             ts = 256 * Math.pow(2, z - tileZoom),
28153             origin = [
28154                 s / 2 - projection.translate()[0],
28155                 s / 2 - projection.translate()[1]];
28156
28157         var tiles = d3.geo.tile()
28158             .scaleExtent([tileZoom, tileZoom])
28159             .scale(s)
28160             .size(dimensions)
28161             .translate(projection.translate())()
28162             .map(function(tile) {
28163                 var x = tile[0] * ts - origin[0],
28164                     y = tile[1] * ts - origin[1];
28165
28166                 return {
28167                     id: tile.toString(),
28168                     extent: iD.geo.Extent(
28169                         projection.invert([x, y + ts]),
28170                         projection.invert([x + ts, y]))
28171                 };
28172             });
28173
28174         function bboxUrl(tile) {
28175             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
28176         }
28177
28178         _.filter(inflight, function(v, i) {
28179             var wanted = _.find(tiles, function(tile) {
28180                 return i === tile.id;
28181             });
28182             if (!wanted) delete inflight[i];
28183             return !wanted;
28184         }).map(abortRequest);
28185
28186         tiles.forEach(function(tile) {
28187             var id = tile.id;
28188
28189             if (loadedTiles[id] || inflight[id]) return;
28190
28191             if (_.isEmpty(inflight)) {
28192                 event.loading();
28193             }
28194
28195             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
28196                 loadedTiles[id] = true;
28197                 delete inflight[id];
28198
28199                 if (callback) callback(err, _.extend({data: parsed}, tile));
28200
28201                 if (_.isEmpty(inflight)) {
28202                     event.loaded();
28203                 }
28204             });
28205         });
28206     };
28207
28208     connection.switch = function(options) {
28209         url = options.url;
28210         oauth.options(_.extend({
28211             loading: authenticating,
28212             done: authenticated
28213         }, options));
28214         event.auth();
28215         connection.flush();
28216         return connection;
28217     };
28218
28219     connection.toggle = function(_) {
28220         off = !_;
28221         return connection;
28222     };
28223
28224     connection.flush = function() {
28225         userDetails = undefined;
28226         _.forEach(inflight, abortRequest);
28227         loadedTiles = {};
28228         inflight = {};
28229         return connection;
28230     };
28231
28232     connection.loadedTiles = function(_) {
28233         if (!arguments.length) return loadedTiles;
28234         loadedTiles = _;
28235         return connection;
28236     };
28237
28238     connection.logout = function() {
28239         userDetails = undefined;
28240         oauth.logout();
28241         event.auth();
28242         return connection;
28243     };
28244
28245     connection.authenticate = function(callback) {
28246         userDetails = undefined;
28247         function done(err, res) {
28248             event.auth();
28249             if (callback) callback(err, res);
28250         }
28251         return oauth.authenticate(done);
28252     };
28253
28254     return d3.rebind(connection, event, 'on');
28255 };
28256 /*
28257     iD.Difference represents the difference between two graphs.
28258     It knows how to calculate the set of entities that were
28259     created, modified, or deleted, and also contains the logic
28260     for recursively extending a difference to the complete set
28261     of entities that will require a redraw, taking into account
28262     child and parent relationships.
28263  */
28264 iD.Difference = function(base, head) {
28265     var changes = {}, length = 0;
28266
28267     function changed(h, b) {
28268         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
28269     }
28270
28271     _.each(head.entities, function(h, id) {
28272         var b = base.entities[id];
28273         if (changed(h, b)) {
28274             changes[id] = {base: b, head: h};
28275             length++;
28276         }
28277     });
28278
28279     _.each(base.entities, function(b, id) {
28280         var h = head.entities[id];
28281         if (!changes[id] && changed(h, b)) {
28282             changes[id] = {base: b, head: h};
28283             length++;
28284         }
28285     });
28286
28287     function addParents(parents, result) {
28288         for (var i = 0; i < parents.length; i++) {
28289             var parent = parents[i];
28290
28291             if (parent.id in result)
28292                 continue;
28293
28294             result[parent.id] = parent;
28295             addParents(head.parentRelations(parent), result);
28296         }
28297     }
28298
28299     var difference = {};
28300
28301     difference.length = function() {
28302         return length;
28303     };
28304
28305     difference.changes = function() {
28306         return changes;
28307     };
28308
28309     difference.extantIDs = function() {
28310         var result = [];
28311         _.each(changes, function(change, id) {
28312             if (change.head) result.push(id);
28313         });
28314         return result;
28315     };
28316
28317     difference.modified = function() {
28318         var result = [];
28319         _.each(changes, function(change) {
28320             if (change.base && change.head) result.push(change.head);
28321         });
28322         return result;
28323     };
28324
28325     difference.created = function() {
28326         var result = [];
28327         _.each(changes, function(change) {
28328             if (!change.base && change.head) result.push(change.head);
28329         });
28330         return result;
28331     };
28332
28333     difference.deleted = function() {
28334         var result = [];
28335         _.each(changes, function(change) {
28336             if (change.base && !change.head) result.push(change.base);
28337         });
28338         return result;
28339     };
28340
28341     difference.summary = function() {
28342         var relevant = {};
28343
28344         function addEntity(entity, graph, changeType) {
28345             relevant[entity.id] = {
28346                 entity: entity,
28347                 graph: graph,
28348                 changeType: changeType
28349             };
28350         }
28351
28352         function addParents(entity) {
28353             var parents = head.parentWays(entity);
28354             for (var j = parents.length - 1; j >= 0; j--) {
28355                 var parent = parents[j];
28356                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
28357             }
28358         }
28359
28360         _.each(changes, function(change) {
28361             if (change.head && change.head.geometry(head) !== 'vertex') {
28362                 addEntity(change.head, head, change.base ? 'modified' : 'created');
28363
28364             } else if (change.base && change.base.geometry(base) !== 'vertex') {
28365                 addEntity(change.base, base, 'deleted');
28366
28367             } else if (change.base && change.head) { // modified vertex
28368                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
28369                     retagged = !_.isEqual(change.base.tags, change.head.tags);
28370
28371                 if (moved) {
28372                     addParents(change.head);
28373                 }
28374
28375                 if (retagged || (moved && change.head.hasInterestingTags())) {
28376                     addEntity(change.head, head, 'modified');
28377                 }
28378
28379             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
28380                 addEntity(change.head, head, 'created');
28381
28382             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
28383                 addEntity(change.base, base, 'deleted');
28384             }
28385         });
28386
28387         return d3.values(relevant);
28388     };
28389
28390     difference.complete = function(extent) {
28391         var result = {}, id, change;
28392
28393         for (id in changes) {
28394             change = changes[id];
28395
28396             var h = change.head,
28397                 b = change.base,
28398                 entity = h || b;
28399
28400             if (extent &&
28401                 (!h || !h.intersects(extent, head)) &&
28402                 (!b || !b.intersects(extent, base)))
28403                 continue;
28404
28405             result[id] = h;
28406
28407             if (entity.type === 'way') {
28408                 var nh = h ? h.nodes : [],
28409                     nb = b ? b.nodes : [],
28410                     diff, i;
28411
28412                 diff = _.difference(nh, nb);
28413                 for (i = 0; i < diff.length; i++) {
28414                     result[diff[i]] = head.hasEntity(diff[i]);
28415                 }
28416
28417                 diff = _.difference(nb, nh);
28418                 for (i = 0; i < diff.length; i++) {
28419                     result[diff[i]] = head.hasEntity(diff[i]);
28420                 }
28421             }
28422
28423             addParents(head.parentWays(entity), result);
28424             addParents(head.parentRelations(entity), result);
28425         }
28426
28427         return result;
28428     };
28429
28430     return difference;
28431 };
28432 iD.Entity = function(attrs) {
28433     // For prototypal inheritance.
28434     if (this instanceof iD.Entity) return;
28435
28436     // Create the appropriate subtype.
28437     if (attrs && attrs.type) {
28438         return iD.Entity[attrs.type].apply(this, arguments);
28439     } else if (attrs && attrs.id) {
28440         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
28441     }
28442
28443     // Initialize a generic Entity (used only in tests).
28444     return (new iD.Entity()).initialize(arguments);
28445 };
28446
28447 iD.Entity.id = function(type) {
28448     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
28449 };
28450
28451 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
28452
28453 iD.Entity.id.fromOSM = function(type, id) {
28454     return type[0] + id;
28455 };
28456
28457 iD.Entity.id.toOSM = function(id) {
28458     return id.slice(1);
28459 };
28460
28461 iD.Entity.id.type = function(id) {
28462     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
28463 };
28464
28465 // A function suitable for use as the second argument to d3.selection#data().
28466 iD.Entity.key = function(entity) {
28467     return entity.id + 'v' + (entity.v || 0);
28468 };
28469
28470 iD.Entity.prototype = {
28471     tags: {},
28472
28473     initialize: function(sources) {
28474         for (var i = 0; i < sources.length; ++i) {
28475             var source = sources[i];
28476             for (var prop in source) {
28477                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
28478                     if (source[prop] === undefined) {
28479                         delete this[prop];
28480                     } else {
28481                         this[prop] = source[prop];
28482                     }
28483                 }
28484             }
28485         }
28486
28487         if (!this.id && this.type) {
28488             this.id = iD.Entity.id(this.type);
28489         }
28490         if (!this.hasOwnProperty('visible')) {
28491             this.visible = true;
28492         }
28493
28494         if (iD.debug) {
28495             Object.freeze(this);
28496             Object.freeze(this.tags);
28497
28498             if (this.loc) Object.freeze(this.loc);
28499             if (this.nodes) Object.freeze(this.nodes);
28500             if (this.members) Object.freeze(this.members);
28501         }
28502
28503         return this;
28504     },
28505
28506     copy: function(resolver, copies) {
28507         if (copies[this.id])
28508             return copies[this.id];
28509
28510         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
28511         copies[this.id] = copy;
28512
28513         return copy;
28514     },
28515
28516     osmId: function() {
28517         return iD.Entity.id.toOSM(this.id);
28518     },
28519
28520     isNew: function() {
28521         return this.osmId() < 0;
28522     },
28523
28524     update: function(attrs) {
28525         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
28526     },
28527
28528     mergeTags: function(tags) {
28529         var merged = _.clone(this.tags), changed = false;
28530         for (var k in tags) {
28531             var t1 = merged[k],
28532                 t2 = tags[k];
28533             if (!t1) {
28534                 changed = true;
28535                 merged[k] = t2;
28536             } else if (t1 !== t2) {
28537                 changed = true;
28538                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
28539             }
28540         }
28541         return changed ? this.update({tags: merged}) : this;
28542     },
28543
28544     intersects: function(extent, resolver) {
28545         return this.extent(resolver).intersects(extent);
28546     },
28547
28548     isUsed: function(resolver) {
28549         return _.without(Object.keys(this.tags), 'area').length > 0 ||
28550             resolver.parentRelations(this).length > 0;
28551     },
28552
28553     hasInterestingTags: function() {
28554         return _.keys(this.tags).some(iD.interestingTag);
28555     },
28556
28557     isHighwayIntersection: function() {
28558         return false;
28559     },
28560
28561     deprecatedTags: function() {
28562         var tags = _.toPairs(this.tags);
28563         var deprecated = {};
28564
28565         iD.data.deprecated.forEach(function(d) {
28566             var match = _.toPairs(d.old)[0];
28567             tags.forEach(function(t) {
28568                 if (t[0] === match[0] &&
28569                     (t[1] === match[1] || match[1] === '*')) {
28570                     deprecated[t[0]] = t[1];
28571                 }
28572             });
28573         });
28574
28575         return deprecated;
28576     }
28577 };
28578 iD.Graph = function(other, mutable) {
28579     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
28580
28581     if (other instanceof iD.Graph) {
28582         var base = other.base();
28583         this.entities = _.assign(Object.create(base.entities), other.entities);
28584         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
28585         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
28586
28587     } else {
28588         this.entities = Object.create({});
28589         this._parentWays = Object.create({});
28590         this._parentRels = Object.create({});
28591         this.rebase(other || [], [this]);
28592     }
28593
28594     this.transients = {};
28595     this._childNodes = {};
28596     this.frozen = !mutable;
28597 };
28598
28599 iD.Graph.prototype = {
28600     hasEntity: function(id) {
28601         return this.entities[id];
28602     },
28603
28604     entity: function(id) {
28605         var entity = this.entities[id];
28606         if (!entity) {
28607             throw new Error('entity ' + id + ' not found');
28608         }
28609         return entity;
28610     },
28611
28612     transient: function(entity, key, fn) {
28613         var id = entity.id,
28614             transients = this.transients[id] ||
28615             (this.transients[id] = {});
28616
28617         if (transients[key] !== undefined) {
28618             return transients[key];
28619         }
28620
28621         transients[key] = fn.call(entity);
28622
28623         return transients[key];
28624     },
28625
28626     parentWays: function(entity) {
28627         var parents = this._parentWays[entity.id],
28628             result = [];
28629
28630         if (parents) {
28631             for (var i = 0; i < parents.length; i++) {
28632                 result.push(this.entity(parents[i]));
28633             }
28634         }
28635         return result;
28636     },
28637
28638     isPoi: function(entity) {
28639         var parentWays = this._parentWays[entity.id];
28640         return !parentWays || parentWays.length === 0;
28641     },
28642
28643     isShared: function(entity) {
28644         var parentWays = this._parentWays[entity.id];
28645         return parentWays && parentWays.length > 1;
28646     },
28647
28648     parentRelations: function(entity) {
28649         var parents = this._parentRels[entity.id],
28650             result = [];
28651
28652         if (parents) {
28653             for (var i = 0; i < parents.length; i++) {
28654                 result.push(this.entity(parents[i]));
28655             }
28656         }
28657         return result;
28658     },
28659
28660     childNodes: function(entity) {
28661         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
28662         if (!entity.nodes) return [];
28663
28664         var nodes = [];
28665         for (var i = 0; i < entity.nodes.length; i++) {
28666             nodes[i] = this.entity(entity.nodes[i]);
28667         }
28668
28669         if (iD.debug) Object.freeze(nodes);
28670
28671         this._childNodes[entity.id] = nodes;
28672         return this._childNodes[entity.id];
28673     },
28674
28675     base: function() {
28676         return {
28677             'entities': iD.util.getPrototypeOf(this.entities),
28678             'parentWays': iD.util.getPrototypeOf(this._parentWays),
28679             'parentRels': iD.util.getPrototypeOf(this._parentRels)
28680         };
28681     },
28682
28683     // Unlike other graph methods, rebase mutates in place. This is because it
28684     // is used only during the history operation that merges newly downloaded
28685     // data into each state. To external consumers, it should appear as if the
28686     // graph always contained the newly downloaded data.
28687     rebase: function(entities, stack, force) {
28688         var base = this.base(),
28689             i, j, k, id;
28690
28691         for (i = 0; i < entities.length; i++) {
28692             var entity = entities[i];
28693
28694             if (!entity.visible || (!force && base.entities[entity.id]))
28695                 continue;
28696
28697             // Merging data into the base graph
28698             base.entities[entity.id] = entity;
28699             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
28700
28701             // Restore provisionally-deleted nodes that are discovered to have an extant parent
28702             if (entity.type === 'way') {
28703                 for (j = 0; j < entity.nodes.length; j++) {
28704                     id = entity.nodes[j];
28705                     for (k = 1; k < stack.length; k++) {
28706                         var ents = stack[k].entities;
28707                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
28708                             delete ents[id];
28709                         }
28710                     }
28711                 }
28712             }
28713         }
28714
28715         for (i = 0; i < stack.length; i++) {
28716             stack[i]._updateRebased();
28717         }
28718     },
28719
28720     _updateRebased: function() {
28721         var base = this.base(),
28722             i, k, child, id, keys;
28723
28724         keys = Object.keys(this._parentWays);
28725         for (i = 0; i < keys.length; i++) {
28726             child = keys[i];
28727             if (base.parentWays[child]) {
28728                 for (k = 0; k < base.parentWays[child].length; k++) {
28729                     id = base.parentWays[child][k];
28730                     if (!this.entities.hasOwnProperty(id) && !_.includes(this._parentWays[child], id)) {
28731                         this._parentWays[child].push(id);
28732                     }
28733                 }
28734             }
28735         }
28736
28737         keys = Object.keys(this._parentRels);
28738         for (i = 0; i < keys.length; i++) {
28739             child = keys[i];
28740             if (base.parentRels[child]) {
28741                 for (k = 0; k < base.parentRels[child].length; k++) {
28742                     id = base.parentRels[child][k];
28743                     if (!this.entities.hasOwnProperty(id) && !_.includes(this._parentRels[child], id)) {
28744                         this._parentRels[child].push(id);
28745                     }
28746                 }
28747             }
28748         }
28749
28750         this.transients = {};
28751
28752         // this._childNodes is not updated, under the assumption that
28753         // ways are always downloaded with their child nodes.
28754     },
28755
28756     // Updates calculated properties (parentWays, parentRels) for the specified change
28757     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
28758
28759         parentWays = parentWays || this._parentWays;
28760         parentRels = parentRels || this._parentRels;
28761
28762         var type = entity && entity.type || oldentity && oldentity.type,
28763             removed, added, ways, rels, i;
28764
28765
28766         if (type === 'way') {
28767
28768             // Update parentWays
28769             if (oldentity && entity) {
28770                 removed = _.difference(oldentity.nodes, entity.nodes);
28771                 added = _.difference(entity.nodes, oldentity.nodes);
28772             } else if (oldentity) {
28773                 removed = oldentity.nodes;
28774                 added = [];
28775             } else if (entity) {
28776                 removed = [];
28777                 added = entity.nodes;
28778             }
28779             for (i = 0; i < removed.length; i++) {
28780                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
28781             }
28782             for (i = 0; i < added.length; i++) {
28783                 ways = _.without(parentWays[added[i]], entity.id);
28784                 ways.push(entity.id);
28785                 parentWays[added[i]] = ways;
28786             }
28787
28788         } else if (type === 'relation') {
28789
28790             // Update parentRels
28791             if (oldentity && entity) {
28792                 removed = _.difference(oldentity.members, entity.members);
28793                 added = _.difference(entity.members, oldentity);
28794             } else if (oldentity) {
28795                 removed = oldentity.members;
28796                 added = [];
28797             } else if (entity) {
28798                 removed = [];
28799                 added = entity.members;
28800             }
28801             for (i = 0; i < removed.length; i++) {
28802                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
28803             }
28804             for (i = 0; i < added.length; i++) {
28805                 rels = _.without(parentRels[added[i].id], entity.id);
28806                 rels.push(entity.id);
28807                 parentRels[added[i].id] = rels;
28808             }
28809         }
28810     },
28811
28812     replace: function(entity) {
28813         if (this.entities[entity.id] === entity)
28814             return this;
28815
28816         return this.update(function() {
28817             this._updateCalculated(this.entities[entity.id], entity);
28818             this.entities[entity.id] = entity;
28819         });
28820     },
28821
28822     remove: function(entity) {
28823         return this.update(function() {
28824             this._updateCalculated(entity, undefined);
28825             this.entities[entity.id] = undefined;
28826         });
28827     },
28828
28829     revert: function(id) {
28830         var baseEntity = this.base().entities[id],
28831             headEntity = this.entities[id];
28832
28833         if (headEntity === baseEntity)
28834             return this;
28835
28836         return this.update(function() {
28837             this._updateCalculated(headEntity, baseEntity);
28838             delete this.entities[id];
28839         });
28840     },
28841
28842     update: function() {
28843         var graph = this.frozen ? iD.Graph(this, true) : this;
28844
28845         for (var i = 0; i < arguments.length; i++) {
28846             arguments[i].call(graph, graph);
28847         }
28848
28849         if (this.frozen) graph.frozen = true;
28850
28851         return graph;
28852     },
28853
28854     // Obliterates any existing entities
28855     load: function(entities) {
28856         var base = this.base();
28857         this.entities = Object.create(base.entities);
28858
28859         for (var i in entities) {
28860             this.entities[i] = entities[i];
28861             this._updateCalculated(base.entities[i], this.entities[i]);
28862         }
28863
28864         return this;
28865     }
28866 };
28867 iD.History = function(context) {
28868     var stack, index, tree,
28869         imageryUsed = ['Bing'],
28870         dispatch = d3.dispatch('change', 'undone', 'redone'),
28871         lock = iD.util.SessionMutex('lock');
28872
28873     function perform(actions) {
28874         actions = Array.prototype.slice.call(actions);
28875
28876         var annotation;
28877
28878         if (!_.isFunction(_.last(actions))) {
28879             annotation = actions.pop();
28880         }
28881
28882         var graph = stack[index].graph;
28883         for (var i = 0; i < actions.length; i++) {
28884             graph = actions[i](graph);
28885         }
28886
28887         return {
28888             graph: graph,
28889             annotation: annotation,
28890             imageryUsed: imageryUsed
28891         };
28892     }
28893
28894     function change(previous) {
28895         var difference = iD.Difference(previous, history.graph());
28896         dispatch.change(difference);
28897         return difference;
28898     }
28899
28900     // iD uses namespaced keys so multiple installations do not conflict
28901     function getKey(n) {
28902         return 'iD_' + window.location.origin + '_' + n;
28903     }
28904
28905     var history = {
28906         graph: function() {
28907             return stack[index].graph;
28908         },
28909
28910         base: function() {
28911             return stack[0].graph;
28912         },
28913
28914         merge: function(entities, extent) {
28915             stack[0].graph.rebase(entities, _.map(stack, 'graph'), false);
28916             tree.rebase(entities, false);
28917
28918             dispatch.change(undefined, extent);
28919         },
28920
28921         perform: function() {
28922             var previous = stack[index].graph;
28923
28924             stack = stack.slice(0, index + 1);
28925             stack.push(perform(arguments));
28926             index++;
28927
28928             return change(previous);
28929         },
28930
28931         replace: function() {
28932             var previous = stack[index].graph;
28933
28934             // assert(index == stack.length - 1)
28935             stack[index] = perform(arguments);
28936
28937             return change(previous);
28938         },
28939
28940         pop: function() {
28941             var previous = stack[index].graph;
28942
28943             if (index > 0) {
28944                 index--;
28945                 stack.pop();
28946                 return change(previous);
28947             }
28948         },
28949
28950         // Same as calling pop and then perform
28951         overwrite: function() {
28952             var previous = stack[index].graph;
28953
28954             if (index > 0) {
28955                 index--;
28956                 stack.pop();
28957             }
28958             stack = stack.slice(0, index + 1);
28959             stack.push(perform(arguments));
28960             index++;
28961
28962             return change(previous);
28963         },
28964
28965         undo: function() {
28966             var previous = stack[index].graph;
28967
28968             // Pop to the next annotated state.
28969             while (index > 0) {
28970                 index--;
28971                 if (stack[index].annotation) break;
28972             }
28973
28974             dispatch.undone();
28975             return change(previous);
28976         },
28977
28978         redo: function() {
28979             var previous = stack[index].graph;
28980
28981             while (index < stack.length - 1) {
28982                 index++;
28983                 if (stack[index].annotation) break;
28984             }
28985
28986             dispatch.redone();
28987             return change(previous);
28988         },
28989
28990         undoAnnotation: function() {
28991             var i = index;
28992             while (i >= 0) {
28993                 if (stack[i].annotation) return stack[i].annotation;
28994                 i--;
28995             }
28996         },
28997
28998         redoAnnotation: function() {
28999             var i = index + 1;
29000             while (i <= stack.length - 1) {
29001                 if (stack[i].annotation) return stack[i].annotation;
29002                 i++;
29003             }
29004         },
29005
29006         intersects: function(extent) {
29007             return tree.intersects(extent, stack[index].graph);
29008         },
29009
29010         difference: function() {
29011             var base = stack[0].graph,
29012                 head = stack[index].graph;
29013             return iD.Difference(base, head);
29014         },
29015
29016         changes: function(action) {
29017             var base = stack[0].graph,
29018                 head = stack[index].graph;
29019
29020             if (action) {
29021                 head = action(head);
29022             }
29023
29024             var difference = iD.Difference(base, head);
29025
29026             return {
29027                 modified: difference.modified(),
29028                 created: difference.created(),
29029                 deleted: difference.deleted()
29030             };
29031         },
29032
29033         validate: function(changes) {
29034             return _(iD.validations)
29035                 .map(function(fn) { return fn()(changes, stack[index].graph); })
29036                 .flatten()
29037                 .value();
29038         },
29039
29040         hasChanges: function() {
29041             return this.difference().length() > 0;
29042         },
29043
29044         imageryUsed: function(sources) {
29045             if (sources) {
29046                 imageryUsed = sources;
29047                 return history;
29048             } else {
29049                 return _(stack.slice(1, index + 1))
29050                     .map('imageryUsed')
29051                     .flatten()
29052                     .uniq()
29053                     .without(undefined, 'Custom')
29054                     .value();
29055             }
29056         },
29057
29058         reset: function() {
29059             stack = [{graph: iD.Graph()}];
29060             index = 0;
29061             tree = iD.Tree(stack[0].graph);
29062             dispatch.change();
29063             return history;
29064         },
29065
29066         toJSON: function() {
29067             if (!this.hasChanges()) return;
29068
29069             var allEntities = {},
29070                 baseEntities = {},
29071                 base = stack[0];
29072
29073             var s = stack.map(function(i) {
29074                 var modified = [], deleted = [];
29075
29076                 _.forEach(i.graph.entities, function(entity, id) {
29077                     if (entity) {
29078                         var key = iD.Entity.key(entity);
29079                         allEntities[key] = entity;
29080                         modified.push(key);
29081                     } else {
29082                         deleted.push(id);
29083                     }
29084
29085                     // make sure that the originals of changed or deleted entities get merged
29086                     // into the base of the stack after restoring the data from JSON.
29087                     if (id in base.graph.entities) {
29088                         baseEntities[id] = base.graph.entities[id];
29089                     }
29090                     // get originals of parent entities too
29091                     _.forEach(base.graph._parentWays[id], function(parentId) {
29092                         if (parentId in base.graph.entities) {
29093                             baseEntities[parentId] = base.graph.entities[parentId];
29094                         }
29095                     });
29096                 });
29097
29098                 var x = {};
29099
29100                 if (modified.length) x.modified = modified;
29101                 if (deleted.length) x.deleted = deleted;
29102                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
29103                 if (i.annotation) x.annotation = i.annotation;
29104
29105                 return x;
29106             });
29107
29108             return JSON.stringify({
29109                 version: 3,
29110                 entities: _.values(allEntities),
29111                 baseEntities: _.values(baseEntities),
29112                 stack: s,
29113                 nextIDs: iD.Entity.id.next,
29114                 index: index
29115             });
29116         },
29117
29118         fromJSON: function(json, loadChildNodes) {
29119             var h = JSON.parse(json),
29120                 loadComplete = true;
29121
29122             iD.Entity.id.next = h.nextIDs;
29123             index = h.index;
29124
29125             if (h.version === 2 || h.version === 3) {
29126                 var allEntities = {};
29127
29128                 h.entities.forEach(function(entity) {
29129                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
29130                 });
29131
29132                 if (h.version === 3) {
29133                     // This merges originals for changed entities into the base of
29134                     // the stack even if the current stack doesn't have them (for
29135                     // example when iD has been restarted in a different region)
29136                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
29137                     stack[0].graph.rebase(baseEntities, _.map(stack, 'graph'), true);
29138                     tree.rebase(baseEntities, true);
29139
29140                     // When we restore a modified way, we also need to fetch any missing
29141                     // childnodes that would normally have been downloaded with it.. #2142
29142                     if (loadChildNodes) {
29143                         var missing =  _(baseEntities)
29144                                 .filter({ type: 'way' })
29145                                 .map('nodes')
29146                                 .flatten()
29147                                 .uniq()
29148                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
29149                                 .value();
29150
29151                         if (!_.isEmpty(missing)) {
29152                             loadComplete = false;
29153                             context.redrawEnable(false);
29154
29155                             var loading = iD.ui.Loading(context).blocking(true);
29156                             context.container().call(loading);
29157
29158                             var childNodesLoaded = function(err, result) {
29159                                 if (!err) {
29160                                     var visible = _.groupBy(result.data, 'visible');
29161                                     if (!_.isEmpty(visible.true)) {
29162                                         missing = _.difference(missing, _.map(visible.true, 'id'));
29163                                         stack[0].graph.rebase(visible.true, _.map(stack, 'graph'), true);
29164                                         tree.rebase(visible.true, true);
29165                                     }
29166
29167                                     // fetch older versions of nodes that were deleted..
29168                                     _.each(visible.false, function(entity) {
29169                                         context.connection()
29170                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
29171                                     });
29172                                 }
29173
29174                                 if (err || _.isEmpty(missing)) {
29175                                     loading.close();
29176                                     context.redrawEnable(true);
29177                                     dispatch.change();
29178                                 }
29179                             };
29180
29181                             context.connection().loadMultiple(missing, childNodesLoaded);
29182                         }
29183                     }
29184                 }
29185
29186                 stack = h.stack.map(function(d) {
29187                     var entities = {}, entity;
29188
29189                     if (d.modified) {
29190                         d.modified.forEach(function(key) {
29191                             entity = allEntities[key];
29192                             entities[entity.id] = entity;
29193                         });
29194                     }
29195
29196                     if (d.deleted) {
29197                         d.deleted.forEach(function(id) {
29198                             entities[id] = undefined;
29199                         });
29200                     }
29201
29202                     return {
29203                         graph: iD.Graph(stack[0].graph).load(entities),
29204                         annotation: d.annotation,
29205                         imageryUsed: d.imageryUsed
29206                     };
29207                 });
29208
29209             } else { // original version
29210                 stack = h.stack.map(function(d) {
29211                     var entities = {};
29212
29213                     for (var i in d.entities) {
29214                         var entity = d.entities[i];
29215                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
29216                     }
29217
29218                     d.graph = iD.Graph(stack[0].graph).load(entities);
29219                     return d;
29220                 });
29221             }
29222
29223             if (loadComplete) {
29224                 dispatch.change();
29225             }
29226
29227             return history;
29228         },
29229
29230         save: function() {
29231             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
29232             return history;
29233         },
29234
29235         clearSaved: function() {
29236             context.debouncedSave.cancel();
29237             if (lock.locked()) context.storage(getKey('saved_history'), null);
29238             return history;
29239         },
29240
29241         lock: function() {
29242             return lock.lock();
29243         },
29244
29245         unlock: function() {
29246             lock.unlock();
29247         },
29248
29249         // is iD not open in another window and it detects that
29250         // there's a history stored in localStorage that's recoverable?
29251         restorableChanges: function() {
29252             return lock.locked() && !!context.storage(getKey('saved_history'));
29253         },
29254
29255         // load history from a version stored in localStorage
29256         restore: function() {
29257             if (!lock.locked()) return;
29258
29259             var json = context.storage(getKey('saved_history'));
29260             if (json) history.fromJSON(json, true);
29261         },
29262
29263         _getKey: getKey
29264
29265     };
29266
29267     history.reset();
29268
29269     return d3.rebind(history, dispatch, 'on');
29270 };
29271 iD.Node = iD.Entity.node = function iD_Node() {
29272     if (!(this instanceof iD_Node)) {
29273         return (new iD_Node()).initialize(arguments);
29274     } else if (arguments.length) {
29275         this.initialize(arguments);
29276     }
29277 };
29278
29279 iD.Node.prototype = Object.create(iD.Entity.prototype);
29280
29281 _.extend(iD.Node.prototype, {
29282     type: 'node',
29283
29284     extent: function() {
29285         return new iD.geo.Extent(this.loc);
29286     },
29287
29288     geometry: function(graph) {
29289         return graph.transient(this, 'geometry', function() {
29290             return graph.isPoi(this) ? 'point' : 'vertex';
29291         });
29292     },
29293
29294     move: function(loc) {
29295         return this.update({loc: loc});
29296     },
29297
29298     isIntersection: function(resolver) {
29299         return resolver.transient(this, 'isIntersection', function() {
29300             return resolver.parentWays(this).filter(function(parent) {
29301                 return (parent.tags.highway ||
29302                     parent.tags.waterway ||
29303                     parent.tags.railway ||
29304                     parent.tags.aeroway) &&
29305                     parent.geometry(resolver) === 'line';
29306             }).length > 1;
29307         });
29308     },
29309
29310     isHighwayIntersection: function(resolver) {
29311         return resolver.transient(this, 'isHighwayIntersection', function() {
29312             return resolver.parentWays(this).filter(function(parent) {
29313                 return parent.tags.highway && parent.geometry(resolver) === 'line';
29314             }).length > 1;
29315         });
29316     },
29317
29318     isOnAddressLine: function(resolver) {
29319         return resolver.transient(this, 'isOnAddressLine', function() {
29320             return resolver.parentWays(this).filter(function(parent) {
29321                 return parent.tags.hasOwnProperty('addr:interpolation') &&
29322                     parent.geometry(resolver) === 'line';
29323             }).length > 0;
29324         });
29325     },
29326
29327     asJXON: function(changeset_id) {
29328         var r = {
29329             node: {
29330                 '@id': this.osmId(),
29331                 '@lon': this.loc[0],
29332                 '@lat': this.loc[1],
29333                 '@version': (this.version || 0),
29334                 tag: _.map(this.tags, function(v, k) {
29335                     return { keyAttributes: { k: k, v: v } };
29336                 })
29337             }
29338         };
29339         if (changeset_id) r.node['@changeset'] = changeset_id;
29340         return r;
29341     },
29342
29343     asGeoJSON: function() {
29344         return {
29345             type: 'Point',
29346             coordinates: this.loc
29347         };
29348     }
29349 });
29350 iD.Relation = iD.Entity.relation = function iD_Relation() {
29351     if (!(this instanceof iD_Relation)) {
29352         return (new iD_Relation()).initialize(arguments);
29353     } else if (arguments.length) {
29354         this.initialize(arguments);
29355     }
29356 };
29357
29358 iD.Relation.prototype = Object.create(iD.Entity.prototype);
29359
29360 iD.Relation.creationOrder = function(a, b) {
29361     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
29362     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
29363
29364     if (aId < 0 || bId < 0) return aId - bId;
29365     return bId - aId;
29366 };
29367
29368 _.extend(iD.Relation.prototype, {
29369     type: 'relation',
29370     members: [],
29371
29372     copy: function(resolver, copies) {
29373         if (copies[this.id])
29374             return copies[this.id];
29375
29376         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
29377
29378         var members = this.members.map(function(member) {
29379             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
29380         });
29381
29382         copy = copy.update({members: members});
29383         copies[this.id] = copy;
29384
29385         return copy;
29386     },
29387
29388     extent: function(resolver, memo) {
29389         return resolver.transient(this, 'extent', function() {
29390             if (memo && memo[this.id]) return iD.geo.Extent();
29391             memo = memo || {};
29392             memo[this.id] = true;
29393
29394             var extent = iD.geo.Extent();
29395             for (var i = 0; i < this.members.length; i++) {
29396                 var member = resolver.hasEntity(this.members[i].id);
29397                 if (member) {
29398                     extent._extend(member.extent(resolver, memo));
29399                 }
29400             }
29401             return extent;
29402         });
29403     },
29404
29405     geometry: function(graph) {
29406         return graph.transient(this, 'geometry', function() {
29407             return this.isMultipolygon() ? 'area' : 'relation';
29408         });
29409     },
29410
29411     isDegenerate: function() {
29412         return this.members.length === 0;
29413     },
29414
29415     // Return an array of members, each extended with an 'index' property whose value
29416     // is the member index.
29417     indexedMembers: function() {
29418         var result = new Array(this.members.length);
29419         for (var i = 0; i < this.members.length; i++) {
29420             result[i] = _.extend({}, this.members[i], {index: i});
29421         }
29422         return result;
29423     },
29424
29425     // Return the first member with the given role. A copy of the member object
29426     // is returned, extended with an 'index' property whose value is the member index.
29427     memberByRole: function(role) {
29428         for (var i = 0; i < this.members.length; i++) {
29429             if (this.members[i].role === role) {
29430                 return _.extend({}, this.members[i], {index: i});
29431             }
29432         }
29433     },
29434
29435     // Return the first member with the given id. A copy of the member object
29436     // is returned, extended with an 'index' property whose value is the member index.
29437     memberById: function(id) {
29438         for (var i = 0; i < this.members.length; i++) {
29439             if (this.members[i].id === id) {
29440                 return _.extend({}, this.members[i], {index: i});
29441             }
29442         }
29443     },
29444
29445     // Return the first member with the given id and role. A copy of the member object
29446     // is returned, extended with an 'index' property whose value is the member index.
29447     memberByIdAndRole: function(id, role) {
29448         for (var i = 0; i < this.members.length; i++) {
29449             if (this.members[i].id === id && this.members[i].role === role) {
29450                 return _.extend({}, this.members[i], {index: i});
29451             }
29452         }
29453     },
29454
29455     addMember: function(member, index) {
29456         var members = this.members.slice();
29457         members.splice(index === undefined ? members.length : index, 0, member);
29458         return this.update({members: members});
29459     },
29460
29461     updateMember: function(member, index) {
29462         var members = this.members.slice();
29463         members.splice(index, 1, _.extend({}, members[index], member));
29464         return this.update({members: members});
29465     },
29466
29467     removeMember: function(index) {
29468         var members = this.members.slice();
29469         members.splice(index, 1);
29470         return this.update({members: members});
29471     },
29472
29473     removeMembersWithID: function(id) {
29474         var members = _.reject(this.members, function(m) { return m.id === id; });
29475         return this.update({members: members});
29476     },
29477
29478     // Wherever a member appears with id `needle.id`, replace it with a member
29479     // with id `replacement.id`, type `replacement.type`, and the original role,
29480     // unless a member already exists with that id and role. Return an updated
29481     // relation.
29482     replaceMember: function(needle, replacement) {
29483         if (!this.memberById(needle.id))
29484             return this;
29485
29486         var members = [];
29487
29488         for (var i = 0; i < this.members.length; i++) {
29489             var member = this.members[i];
29490             if (member.id !== needle.id) {
29491                 members.push(member);
29492             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
29493                 members.push({id: replacement.id, type: replacement.type, role: member.role});
29494             }
29495         }
29496
29497         return this.update({members: members});
29498     },
29499
29500     asJXON: function(changeset_id) {
29501         var r = {
29502             relation: {
29503                 '@id': this.osmId(),
29504                 '@version': this.version || 0,
29505                 member: _.map(this.members, function(member) {
29506                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
29507                 }),
29508                 tag: _.map(this.tags, function(v, k) {
29509                     return { keyAttributes: { k: k, v: v } };
29510                 })
29511             }
29512         };
29513         if (changeset_id) r.relation['@changeset'] = changeset_id;
29514         return r;
29515     },
29516
29517     asGeoJSON: function(resolver) {
29518         return resolver.transient(this, 'GeoJSON', function () {
29519             if (this.isMultipolygon()) {
29520                 return {
29521                     type: 'MultiPolygon',
29522                     coordinates: this.multipolygon(resolver)
29523                 };
29524             } else {
29525                 return {
29526                     type: 'FeatureCollection',
29527                     properties: this.tags,
29528                     features: this.members.map(function (member) {
29529                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
29530                     })
29531                 };
29532             }
29533         });
29534     },
29535
29536     area: function(resolver) {
29537         return resolver.transient(this, 'area', function() {
29538             return d3.geo.area(this.asGeoJSON(resolver));
29539         });
29540     },
29541
29542     isMultipolygon: function() {
29543         return this.tags.type === 'multipolygon';
29544     },
29545
29546     isComplete: function(resolver) {
29547         for (var i = 0; i < this.members.length; i++) {
29548             if (!resolver.hasEntity(this.members[i].id)) {
29549                 return false;
29550             }
29551         }
29552         return true;
29553     },
29554
29555     isRestriction: function() {
29556         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
29557     },
29558
29559     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
29560     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
29561     //
29562     // This corresponds to the structure needed for rendering a multipolygon path using a
29563     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
29564     //
29565     // In the case of invalid geometries, this function will still return a result which
29566     // includes the nodes of all way members, but some Nds may be unclosed and some inner
29567     // rings not matched with the intended outer ring.
29568     //
29569     multipolygon: function(resolver) {
29570         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
29571             inners = this.members.filter(function(m) { return 'inner' === m.role; });
29572
29573         outers = iD.geo.joinWays(outers, resolver);
29574         inners = iD.geo.joinWays(inners, resolver);
29575
29576         outers = outers.map(function(outer) { return _.map(outer.nodes, 'loc'); });
29577         inners = inners.map(function(inner) { return _.map(inner.nodes, 'loc'); });
29578
29579         var result = outers.map(function(o) {
29580             // Heuristic for detecting counterclockwise winding order. Assumes
29581             // that OpenStreetMap polygons are not hemisphere-spanning.
29582             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
29583         });
29584
29585         function findOuter(inner) {
29586             var o, outer;
29587
29588             for (o = 0; o < outers.length; o++) {
29589                 outer = outers[o];
29590                 if (iD.geo.polygonContainsPolygon(outer, inner))
29591                     return o;
29592             }
29593
29594             for (o = 0; o < outers.length; o++) {
29595                 outer = outers[o];
29596                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
29597                     return o;
29598             }
29599         }
29600
29601         for (var i = 0; i < inners.length; i++) {
29602             var inner = inners[i];
29603
29604             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
29605                 inner = inner.reverse();
29606             }
29607
29608             var o = findOuter(inners[i]);
29609             if (o !== undefined)
29610                 result[o].push(inners[i]);
29611             else
29612                 result.push([inners[i]]); // Invalid geometry
29613         }
29614
29615         return result;
29616     }
29617 });
29618 iD.oneWayTags = {
29619     'aerialway': {
29620         'chair_lift': true,
29621         'mixed_lift': true,
29622         't-bar': true,
29623         'j-bar': true,
29624         'platter': true,
29625         'rope_tow': true,
29626         'magic_carpet': true,
29627         'yes': true
29628     },
29629     'highway': {
29630         'motorway': true,
29631         'motorway_link': true
29632     },
29633     'junction': {
29634         'roundabout': true
29635     },
29636     'man_made': {
29637         'piste:halfpipe': true
29638     },
29639     'piste:type': {
29640         'downhill': true,
29641         'sled': true,
29642         'yes': true
29643     },
29644     'waterway': {
29645         'river': true,
29646         'stream': true
29647     }
29648 };
29649
29650 iD.pavedTags = {
29651     'surface': {
29652         'paved': true,
29653         'asphalt': true,
29654         'concrete': true
29655     },
29656     'tracktype': {
29657         'grade1': true
29658     }
29659 };
29660
29661 iD.interestingTag = function (key) {
29662     return key !== 'attribution' &&
29663         key !== 'created_by' &&
29664         key !== 'source' &&
29665         key !== 'odbl' &&
29666         key.indexOf('tiger:') !== 0;
29667
29668 };
29669 iD.Tree = function(head) {
29670     var rtree = rbush(),
29671         rectangles = {};
29672
29673     function entityRectangle(entity) {
29674         var rect = entity.extent(head).rectangle();
29675         rect.id = entity.id;
29676         rectangles[entity.id] = rect;
29677         return rect;
29678     }
29679
29680     function updateParents(entity, insertions, memo) {
29681         head.parentWays(entity).forEach(function(way) {
29682             if (rectangles[way.id]) {
29683                 rtree.remove(rectangles[way.id]);
29684                 insertions[way.id] = way;
29685             }
29686             updateParents(way, insertions, memo);
29687         });
29688
29689         head.parentRelations(entity).forEach(function(relation) {
29690             if (memo[entity.id]) return;
29691             memo[entity.id] = true;
29692             if (rectangles[relation.id]) {
29693                 rtree.remove(rectangles[relation.id]);
29694                 insertions[relation.id] = relation;
29695             }
29696             updateParents(relation, insertions, memo);
29697         });
29698     }
29699
29700     var tree = {};
29701
29702     tree.rebase = function(entities, force) {
29703         var insertions = {};
29704
29705         for (var i = 0; i < entities.length; i++) {
29706             var entity = entities[i];
29707
29708             if (!entity.visible)
29709                 continue;
29710
29711             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
29712                 if (!force) {
29713                     continue;
29714                 } else if (rectangles[entity.id]) {
29715                     rtree.remove(rectangles[entity.id]);
29716                 }
29717             }
29718
29719             insertions[entity.id] = entity;
29720             updateParents(entity, insertions, {});
29721         }
29722
29723         rtree.load(_.map(insertions, entityRectangle));
29724
29725         return tree;
29726     };
29727
29728     tree.intersects = function(extent, graph) {
29729         if (graph !== head) {
29730             var diff = iD.Difference(head, graph),
29731                 insertions = {};
29732
29733             head = graph;
29734
29735             diff.deleted().forEach(function(entity) {
29736                 rtree.remove(rectangles[entity.id]);
29737                 delete rectangles[entity.id];
29738             });
29739
29740             diff.modified().forEach(function(entity) {
29741                 rtree.remove(rectangles[entity.id]);
29742                 insertions[entity.id] = entity;
29743                 updateParents(entity, insertions, {});
29744             });
29745
29746             diff.created().forEach(function(entity) {
29747                 insertions[entity.id] = entity;
29748             });
29749
29750             rtree.load(_.map(insertions, entityRectangle));
29751         }
29752
29753         return rtree.search(extent.rectangle()).map(function(rect) {
29754             return head.entity(rect.id);
29755         });
29756     };
29757
29758     return tree;
29759 };
29760 iD.Way = iD.Entity.way = function iD_Way() {
29761     if (!(this instanceof iD_Way)) {
29762         return (new iD_Way()).initialize(arguments);
29763     } else if (arguments.length) {
29764         this.initialize(arguments);
29765     }
29766 };
29767
29768 iD.Way.prototype = Object.create(iD.Entity.prototype);
29769
29770 _.extend(iD.Way.prototype, {
29771     type: 'way',
29772     nodes: [],
29773
29774     copy: function(resolver, copies) {
29775         if (copies[this.id])
29776             return copies[this.id];
29777
29778         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
29779
29780         var nodes = this.nodes.map(function(id) {
29781             return resolver.entity(id).copy(resolver, copies).id;
29782         });
29783
29784         copy = copy.update({nodes: nodes});
29785         copies[this.id] = copy;
29786
29787         return copy;
29788     },
29789
29790     extent: function(resolver) {
29791         return resolver.transient(this, 'extent', function() {
29792             var extent = iD.geo.Extent();
29793             for (var i = 0; i < this.nodes.length; i++) {
29794                 var node = resolver.hasEntity(this.nodes[i]);
29795                 if (node) {
29796                     extent._extend(node.extent());
29797                 }
29798             }
29799             return extent;
29800         });
29801     },
29802
29803     first: function() {
29804         return this.nodes[0];
29805     },
29806
29807     last: function() {
29808         return this.nodes[this.nodes.length - 1];
29809     },
29810
29811     contains: function(node) {
29812         return this.nodes.indexOf(node) >= 0;
29813     },
29814
29815     affix: function(node) {
29816         if (this.nodes[0] === node) return 'prefix';
29817         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
29818     },
29819
29820     layer: function() {
29821         // explicit layer tag, clamp between -10, 10..
29822         if (this.tags.layer !== undefined) {
29823             return Math.max(-10, Math.min(+(this.tags.layer), 10));
29824         }
29825
29826         // implied layer tag..
29827         if (this.tags.location === 'overground') return 1;
29828         if (this.tags.location === 'underground') return -1;
29829         if (this.tags.location === 'underwater') return -10;
29830
29831         if (this.tags.power === 'line') return 10;
29832         if (this.tags.power === 'minor_line') return 10;
29833         if (this.tags.aerialway) return 10;
29834         if (this.tags.bridge) return 1;
29835         if (this.tags.cutting) return -1;
29836         if (this.tags.tunnel) return -1;
29837         if (this.tags.waterway) return -1;
29838         if (this.tags.man_made === 'pipeline') return -10;
29839         if (this.tags.boundary) return -10;
29840         return 0;
29841     },
29842
29843     isOneWay: function() {
29844         // explicit oneway tag..
29845         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
29846         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
29847
29848         // implied oneway tag..
29849         for (var key in this.tags) {
29850             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
29851                 return true;
29852         }
29853         return false;
29854     },
29855
29856     isClosed: function() {
29857         return this.nodes.length > 0 && this.first() === this.last();
29858     },
29859
29860     isConvex: function(resolver) {
29861         if (!this.isClosed() || this.isDegenerate()) return null;
29862
29863         var nodes = _.uniq(resolver.childNodes(this)),
29864             coords = _.map(nodes, 'loc'),
29865             curr = 0, prev = 0;
29866
29867         for (var i = 0; i < coords.length; i++) {
29868             var o = coords[(i+1) % coords.length],
29869                 a = coords[i],
29870                 b = coords[(i+2) % coords.length],
29871                 res = iD.geo.cross(o, a, b);
29872
29873             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
29874             if (curr === 0) {
29875                 continue;
29876             } else if (prev && curr !== prev) {
29877                 return false;
29878             }
29879             prev = curr;
29880         }
29881         return true;
29882     },
29883
29884     isArea: function() {
29885         if (this.tags.area === 'yes')
29886             return true;
29887         if (!this.isClosed() || this.tags.area === 'no')
29888             return false;
29889         for (var key in this.tags)
29890             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
29891                 return true;
29892         return false;
29893     },
29894
29895     isDegenerate: function() {
29896         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
29897     },
29898
29899     areAdjacent: function(n1, n2) {
29900         for (var i = 0; i < this.nodes.length; i++) {
29901             if (this.nodes[i] === n1) {
29902                 if (this.nodes[i - 1] === n2) return true;
29903                 if (this.nodes[i + 1] === n2) return true;
29904             }
29905         }
29906         return false;
29907     },
29908
29909     geometry: function(graph) {
29910         return graph.transient(this, 'geometry', function() {
29911             return this.isArea() ? 'area' : 'line';
29912         });
29913     },
29914
29915     addNode: function(id, index) {
29916         var nodes = this.nodes.slice();
29917         nodes.splice(index === undefined ? nodes.length : index, 0, id);
29918         return this.update({nodes: nodes});
29919     },
29920
29921     updateNode: function(id, index) {
29922         var nodes = this.nodes.slice();
29923         nodes.splice(index, 1, id);
29924         return this.update({nodes: nodes});
29925     },
29926
29927     replaceNode: function(needle, replacement) {
29928         if (this.nodes.indexOf(needle) < 0)
29929             return this;
29930
29931         var nodes = this.nodes.slice();
29932         for (var i = 0; i < nodes.length; i++) {
29933             if (nodes[i] === needle) {
29934                 nodes[i] = replacement;
29935             }
29936         }
29937         return this.update({nodes: nodes});
29938     },
29939
29940     removeNode: function(id) {
29941         var nodes = [];
29942
29943         for (var i = 0; i < this.nodes.length; i++) {
29944             var node = this.nodes[i];
29945             if (node !== id && nodes[nodes.length - 1] !== node) {
29946                 nodes.push(node);
29947             }
29948         }
29949
29950         // Preserve circularity
29951         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
29952             nodes.push(nodes[0]);
29953         }
29954
29955         return this.update({nodes: nodes});
29956     },
29957
29958     asJXON: function(changeset_id) {
29959         var r = {
29960             way: {
29961                 '@id': this.osmId(),
29962                 '@version': this.version || 0,
29963                 nd: _.map(this.nodes, function(id) {
29964                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
29965                 }),
29966                 tag: _.map(this.tags, function(v, k) {
29967                     return { keyAttributes: { k: k, v: v } };
29968                 })
29969             }
29970         };
29971         if (changeset_id) r.way['@changeset'] = changeset_id;
29972         return r;
29973     },
29974
29975     asGeoJSON: function(resolver) {
29976         return resolver.transient(this, 'GeoJSON', function() {
29977             var coordinates = _.map(resolver.childNodes(this), 'loc');
29978             if (this.isArea() && this.isClosed()) {
29979                 return {
29980                     type: 'Polygon',
29981                     coordinates: [coordinates]
29982                 };
29983             } else {
29984                 return {
29985                     type: 'LineString',
29986                     coordinates: coordinates
29987                 };
29988             }
29989         });
29990     },
29991
29992     area: function(resolver) {
29993         return resolver.transient(this, 'area', function() {
29994             var nodes = resolver.childNodes(this);
29995
29996             var json = {
29997                 type: 'Polygon',
29998                 coordinates: [_.map(nodes, 'loc')]
29999             };
30000
30001             if (!this.isClosed() && nodes.length) {
30002                 json.coordinates[0].push(nodes[0].loc);
30003             }
30004
30005             var area = d3.geo.area(json);
30006
30007             // Heuristic for detecting counterclockwise winding order. Assumes
30008             // that OpenStreetMap polygons are not hemisphere-spanning.
30009             if (area > 2 * Math.PI) {
30010                 json.coordinates[0] = json.coordinates[0].reverse();
30011                 area = d3.geo.area(json);
30012             }
30013
30014             return isNaN(area) ? 0 : area;
30015         });
30016     }
30017 });
30018 iD.Background = function(context) {
30019     var dispatch = d3.dispatch('change'),
30020         baseLayer = iD.TileLayer(context).projection(context.projection),
30021         overlayLayers = [],
30022         backgroundSources;
30023
30024
30025     function findSource(id) {
30026         return _.find(backgroundSources, function(d) {
30027             return d.id && d.id === id;
30028         });
30029     }
30030
30031
30032     function background(selection) {
30033         var base = selection.selectAll('.layer-background')
30034             .data([0]);
30035
30036         base.enter()
30037             .insert('div', '.layer-data')
30038             .attr('class', 'layer layer-background');
30039
30040         base.call(baseLayer);
30041
30042         var overlays = selection.selectAll('.layer-overlay')
30043             .data(overlayLayers, function(d) { return d.source().name(); });
30044
30045         overlays.enter()
30046             .insert('div', '.layer-data')
30047             .attr('class', 'layer layer-overlay');
30048
30049         overlays.each(function(layer) {
30050             d3.select(this).call(layer);
30051         });
30052
30053         overlays.exit()
30054             .remove();
30055     }
30056
30057
30058     background.updateImagery = function() {
30059         var b = background.baseLayerSource(),
30060             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
30061             meters = iD.geo.offsetToMeters(b.offset()),
30062             epsilon = 0.01,
30063             x = +meters[0].toFixed(2),
30064             y = +meters[1].toFixed(2),
30065             q = iD.util.stringQs(location.hash.substring(1));
30066
30067         var id = b.id;
30068         if (id === 'custom') {
30069             id = 'custom:' + b.template;
30070         }
30071
30072         if (id) {
30073             q.background = id;
30074         } else {
30075             delete q.background;
30076         }
30077
30078         if (o) {
30079             q.overlays = o;
30080         } else {
30081             delete q.overlays;
30082         }
30083
30084         if (Math.abs(x) > epsilon || Math.abs(y) > epsilon) {
30085             q.offset = x + ',' + y;
30086         } else {
30087             delete q.offset;
30088         }
30089
30090         location.replace('#' + iD.util.qsString(q, true));
30091
30092         var imageryUsed = [b.imageryUsed()];
30093
30094         overlayLayers.forEach(function (d) {
30095             var source = d.source();
30096             if (!source.isLocatorOverlay()) {
30097                 imageryUsed.push(source.imageryUsed());
30098             }
30099         });
30100
30101         var gpx = context.layers().layer('gpx');
30102         if (gpx && gpx.enabled() && gpx.hasGpx()) {
30103             imageryUsed.push('Local GPX');
30104         }
30105
30106         var mapillary_images = context.layers().layer('mapillary-images');
30107         if (mapillary_images && mapillary_images.enabled()) {
30108             imageryUsed.push('Mapillary Images');
30109         }
30110
30111         var mapillary_signs = context.layers().layer('mapillary-signs');
30112         if (mapillary_signs && mapillary_signs.enabled()) {
30113             imageryUsed.push('Mapillary Signs');
30114         }
30115
30116         context.history().imageryUsed(imageryUsed);
30117     };
30118
30119     background.sources = function(extent) {
30120         return backgroundSources.filter(function(source) {
30121             return source.intersects(extent);
30122         });
30123     };
30124
30125     background.dimensions = function(_) {
30126         baseLayer.dimensions(_);
30127
30128         overlayLayers.forEach(function(layer) {
30129             layer.dimensions(_);
30130         });
30131     };
30132
30133     background.baseLayerSource = function(d) {
30134         if (!arguments.length) return baseLayer.source();
30135         baseLayer.source(d);
30136         dispatch.change();
30137         background.updateImagery();
30138         return background;
30139     };
30140
30141     background.bing = function() {
30142         background.baseLayerSource(findSource('Bing'));
30143     };
30144
30145     background.showsLayer = function(d) {
30146         return d === baseLayer.source() ||
30147             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
30148             overlayLayers.some(function(l) { return l.source() === d; });
30149     };
30150
30151     background.overlayLayerSources = function() {
30152         return overlayLayers.map(function (l) { return l.source(); });
30153     };
30154
30155     background.toggleOverlayLayer = function(d) {
30156         var layer;
30157
30158         for (var i = 0; i < overlayLayers.length; i++) {
30159             layer = overlayLayers[i];
30160             if (layer.source() === d) {
30161                 overlayLayers.splice(i, 1);
30162                 dispatch.change();
30163                 background.updateImagery();
30164                 return;
30165             }
30166         }
30167
30168         layer = iD.TileLayer(context)
30169             .source(d)
30170             .projection(context.projection)
30171             .dimensions(baseLayer.dimensions());
30172
30173         overlayLayers.push(layer);
30174         dispatch.change();
30175         background.updateImagery();
30176     };
30177
30178     background.nudge = function(d, zoom) {
30179         baseLayer.source().nudge(d, zoom);
30180         dispatch.change();
30181         background.updateImagery();
30182         return background;
30183     };
30184
30185     background.offset = function(d) {
30186         if (!arguments.length) return baseLayer.source().offset();
30187         baseLayer.source().offset(d);
30188         dispatch.change();
30189         background.updateImagery();
30190         return background;
30191     };
30192
30193     background.load = function(imagery) {
30194         function parseMap(qmap) {
30195             if (!qmap) return false;
30196             var args = qmap.split('/').map(Number);
30197             if (args.length < 3 || args.some(isNaN)) return false;
30198             return iD.geo.Extent([args[1], args[2]]);
30199         }
30200
30201         var q = iD.util.stringQs(location.hash.substring(1)),
30202             chosen = q.background || q.layer,
30203             extent = parseMap(q.map),
30204             best;
30205
30206         backgroundSources = imagery.map(function(source) {
30207             if (source.type === 'bing') {
30208                 return iD.BackgroundSource.Bing(source, dispatch);
30209             } else {
30210                 return iD.BackgroundSource(source);
30211             }
30212         });
30213
30214         backgroundSources.unshift(iD.BackgroundSource.None());
30215
30216         if (!chosen && extent) {
30217             best = _.find(this.sources(extent), function(s) { return s.best(); });
30218         }
30219
30220         if (chosen && chosen.indexOf('custom:') === 0) {
30221             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
30222         } else {
30223             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
30224         }
30225
30226         var locator = _.find(backgroundSources, function(d) {
30227             return d.overlay && d.default;
30228         });
30229
30230         if (locator) {
30231             background.toggleOverlayLayer(locator);
30232         }
30233
30234         var overlays = (q.overlays || '').split(',');
30235         overlays.forEach(function(overlay) {
30236             overlay = findSource(overlay);
30237             if (overlay) {
30238                 background.toggleOverlayLayer(overlay);
30239             }
30240         });
30241
30242         if (q.gpx) {
30243             var gpx = context.layers().layer('gpx');
30244             if (gpx) {
30245                 gpx.url(q.gpx);
30246             }
30247         }
30248
30249         if (q.offset) {
30250             var offset = q.offset.replace(/;/g, ',').split(',').map(function(n) {
30251                 return !isNaN(n) && n;
30252             });
30253
30254             if (offset.length === 2) {
30255                 background.offset(iD.geo.metersToOffset(offset));
30256             }
30257         }
30258     };
30259
30260     return d3.rebind(background, dispatch, 'on');
30261 };
30262 iD.BackgroundSource = function(data) {
30263     var source = _.clone(data),
30264         offset = [0, 0],
30265         name = source.name,
30266         best = !!source.best;
30267
30268     source.scaleExtent = data.scaleExtent || [0, 20];
30269     source.overzoom = data.overzoom !== false;
30270
30271     source.offset = function(_) {
30272         if (!arguments.length) return offset;
30273         offset = _;
30274         return source;
30275     };
30276
30277     source.nudge = function(_, zoomlevel) {
30278         offset[0] += _[0] / Math.pow(2, zoomlevel);
30279         offset[1] += _[1] / Math.pow(2, zoomlevel);
30280         return source;
30281     };
30282
30283     source.name = function() {
30284         return name;
30285     };
30286
30287     source.best = function() {
30288         return best;
30289     };
30290
30291     source.area = function() {
30292         if (!data.polygon) return Number.MAX_VALUE;  // worldwide
30293         var area = d3.geo.area({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
30294         return isNaN(area) ? 0 : area;
30295     };
30296
30297     source.imageryUsed = function() {
30298         return source.id || name;
30299     };
30300
30301     source.url = function(coord) {
30302         return data.template
30303             .replace('{x}', coord[0])
30304             .replace('{y}', coord[1])
30305             // TMS-flipped y coordinate
30306             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
30307             .replace(/\{z(oom)?\}/, coord[2])
30308             .replace(/\{switch:([^}]+)\}/, function(s, r) {
30309                 var subdomains = r.split(',');
30310                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
30311             })
30312             .replace('{u}', function() {
30313                 var u = '';
30314                 for (var zoom = coord[2]; zoom > 0; zoom--) {
30315                     var b = 0;
30316                     var mask = 1 << (zoom - 1);
30317                     if ((coord[0] & mask) !== 0) b++;
30318                     if ((coord[1] & mask) !== 0) b += 2;
30319                     u += b.toString();
30320                 }
30321                 return u;
30322             });
30323     };
30324
30325     source.intersects = function(extent) {
30326         extent = extent.polygon();
30327         return !data.polygon || data.polygon.some(function(polygon) {
30328             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
30329         });
30330     };
30331
30332     source.validZoom = function(z) {
30333         return source.scaleExtent[0] <= z &&
30334             (source.overzoom || source.scaleExtent[1] > z);
30335     };
30336
30337     source.isLocatorOverlay = function() {
30338         return name === 'Locator Overlay';
30339     };
30340
30341     source.copyrightNotices = function() {};
30342
30343     return source;
30344 };
30345
30346 iD.BackgroundSource.Bing = function(data, dispatch) {
30347     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
30348     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
30349
30350     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
30351
30352     var bing = iD.BackgroundSource(data),
30353         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
30354         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
30355             key + '&jsonp={callback}',
30356         providers = [];
30357
30358     d3.jsonp(url, function(json) {
30359         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
30360             return {
30361                 attribution: provider.attribution,
30362                 areas: provider.coverageAreas.map(function(area) {
30363                     return {
30364                         zoom: [area.zoomMin, area.zoomMax],
30365                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
30366                     };
30367                 })
30368             };
30369         });
30370         dispatch.change();
30371     });
30372
30373     bing.copyrightNotices = function(zoom, extent) {
30374         zoom = Math.min(zoom, 21);
30375         return providers.filter(function(provider) {
30376             return _.some(provider.areas, function(area) {
30377                 return extent.intersects(area.extent) &&
30378                     area.zoom[0] <= zoom &&
30379                     area.zoom[1] >= zoom;
30380             });
30381         }).map(function(provider) {
30382             return provider.attribution;
30383         }).join(', ');
30384     };
30385
30386     bing.logo = 'bing_maps.png';
30387     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
30388
30389     return bing;
30390 };
30391
30392 iD.BackgroundSource.None = function() {
30393     var source = iD.BackgroundSource({id: 'none', template: ''});
30394
30395     source.name = function() {
30396         return t('background.none');
30397     };
30398
30399     source.imageryUsed = function() {
30400         return 'None';
30401     };
30402
30403     source.area = function() {
30404         return -1;
30405     };
30406
30407     return source;
30408 };
30409
30410 iD.BackgroundSource.Custom = function(template) {
30411     var source = iD.BackgroundSource({id: 'custom', template: template});
30412
30413     source.name = function() {
30414         return t('background.custom');
30415     };
30416
30417     source.imageryUsed = function() {
30418         return 'Custom (' + template + ')';
30419     };
30420
30421     source.area = function() {
30422         return -2;
30423     };
30424
30425     return source;
30426 };
30427 iD.Features = function(context) {
30428     var traffic_roads = {
30429         'motorway': true,
30430         'motorway_link': true,
30431         'trunk': true,
30432         'trunk_link': true,
30433         'primary': true,
30434         'primary_link': true,
30435         'secondary': true,
30436         'secondary_link': true,
30437         'tertiary': true,
30438         'tertiary_link': true,
30439         'residential': true,
30440         'unclassified': true,
30441         'living_street': true
30442     };
30443
30444     var service_roads = {
30445         'service': true,
30446         'road': true,
30447         'track': true
30448     };
30449
30450     var paths = {
30451         'path': true,
30452         'footway': true,
30453         'cycleway': true,
30454         'bridleway': true,
30455         'steps': true,
30456         'pedestrian': true,
30457         'corridor': true
30458     };
30459
30460     var past_futures = {
30461         'proposed': true,
30462         'construction': true,
30463         'abandoned': true,
30464         'dismantled': true,
30465         'disused': true,
30466         'razed': true,
30467         'demolished': true,
30468         'obliterated': true
30469     };
30470
30471     var dispatch = d3.dispatch('change', 'redraw'),
30472         _cullFactor = 1,
30473         _cache = {},
30474         _features = {},
30475         _stats = {},
30476         _keys = [],
30477         _hidden = [];
30478
30479     function update() {
30480         _hidden = features.hidden();
30481         dispatch.change();
30482         dispatch.redraw();
30483     }
30484
30485     function defineFeature(k, filter, max) {
30486         _keys.push(k);
30487         _features[k] = {
30488             filter: filter,
30489             enabled: true,   // whether the user wants it enabled..
30490             count: 0,
30491             currentMax: (max || Infinity),
30492             defaultMax: (max || Infinity),
30493             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
30494             disable: function() { this.enabled = false; this.currentMax = 0; },
30495             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
30496             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
30497         };
30498     }
30499
30500
30501     defineFeature('points', function isPoint(entity, resolver, geometry) {
30502         return geometry === 'point';
30503     }, 200);
30504
30505     defineFeature('traffic_roads', function isTrafficRoad(entity) {
30506         return traffic_roads[entity.tags.highway];
30507     });
30508
30509     defineFeature('service_roads', function isServiceRoad(entity) {
30510         return service_roads[entity.tags.highway];
30511     });
30512
30513     defineFeature('paths', function isPath(entity) {
30514         return paths[entity.tags.highway];
30515     });
30516
30517     defineFeature('buildings', function isBuilding(entity) {
30518         return (
30519             !!entity.tags['building:part'] ||
30520             (!!entity.tags.building && entity.tags.building !== 'no') ||
30521             entity.tags.amenity === 'shelter' ||
30522             entity.tags.parking === 'multi-storey' ||
30523             entity.tags.parking === 'sheds' ||
30524             entity.tags.parking === 'carports' ||
30525             entity.tags.parking === 'garage_boxes'
30526         );
30527     }, 250);
30528
30529     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
30530         return geometry === 'area' &&
30531             !_features.buildings.filter(entity) &&
30532             !_features.water.filter(entity);
30533     });
30534
30535     defineFeature('boundaries', function isBoundary(entity) {
30536         return !!entity.tags.boundary;
30537     });
30538
30539     defineFeature('water', function isWater(entity) {
30540         return (
30541             !!entity.tags.waterway ||
30542             entity.tags.natural === 'water' ||
30543             entity.tags.natural === 'coastline' ||
30544             entity.tags.natural === 'bay' ||
30545             entity.tags.landuse === 'pond' ||
30546             entity.tags.landuse === 'basin' ||
30547             entity.tags.landuse === 'reservoir' ||
30548             entity.tags.landuse === 'salt_pond'
30549         );
30550     });
30551
30552     defineFeature('rail', function isRail(entity) {
30553         return (
30554             !!entity.tags.railway ||
30555             entity.tags.landuse === 'railway'
30556         ) && !(
30557             traffic_roads[entity.tags.highway] ||
30558             service_roads[entity.tags.highway] ||
30559             paths[entity.tags.highway]
30560         );
30561     });
30562
30563     defineFeature('power', function isPower(entity) {
30564         return !!entity.tags.power;
30565     });
30566
30567     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
30568     defineFeature('past_future', function isPastFuture(entity) {
30569         if (
30570             traffic_roads[entity.tags.highway] ||
30571             service_roads[entity.tags.highway] ||
30572             paths[entity.tags.highway]
30573         ) { return false; }
30574
30575         var strings = Object.keys(entity.tags);
30576
30577         for (var i = 0; i < strings.length; i++) {
30578             var s = strings[i];
30579             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
30580         }
30581         return false;
30582     });
30583
30584     // Lines or areas that don't match another feature filter.
30585     // IMPORTANT: The 'others' feature must be the last one defined,
30586     //   so that code in getMatches can skip this test if `hasMatch = true`
30587     defineFeature('others', function isOther(entity, resolver, geometry) {
30588         return (geometry === 'line' || geometry === 'area');
30589     });
30590
30591
30592     function features() {}
30593
30594     features.features = function() {
30595         return _features;
30596     };
30597
30598     features.keys = function() {
30599         return _keys;
30600     };
30601
30602     features.enabled = function(k) {
30603         if (!arguments.length) {
30604             return _.filter(_keys, function(k) { return _features[k].enabled; });
30605         }
30606         return _features[k] && _features[k].enabled;
30607     };
30608
30609     features.disabled = function(k) {
30610         if (!arguments.length) {
30611             return _.reject(_keys, function(k) { return _features[k].enabled; });
30612         }
30613         return _features[k] && !_features[k].enabled;
30614     };
30615
30616     features.hidden = function(k) {
30617         if (!arguments.length) {
30618             return _.filter(_keys, function(k) { return _features[k].hidden(); });
30619         }
30620         return _features[k] && _features[k].hidden();
30621     };
30622
30623     features.autoHidden = function(k) {
30624         if (!arguments.length) {
30625             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
30626         }
30627         return _features[k] && _features[k].autoHidden();
30628     };
30629
30630     features.enable = function(k) {
30631         if (_features[k] && !_features[k].enabled) {
30632             _features[k].enable();
30633             update();
30634         }
30635     };
30636
30637     features.disable = function(k) {
30638         if (_features[k] && _features[k].enabled) {
30639             _features[k].disable();
30640             update();
30641         }
30642     };
30643
30644     features.toggle = function(k) {
30645         if (_features[k]) {
30646             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
30647             update();
30648         }
30649     };
30650
30651     features.resetStats = function() {
30652         _.each(_features, function(f) { f.count = 0; });
30653         dispatch.change();
30654     };
30655
30656     features.gatherStats = function(d, resolver, dimensions) {
30657         var needsRedraw = false,
30658             type = _.groupBy(d, function(ent) { return ent.type; }),
30659             entities = [].concat(type.relation || [], type.way || [], type.node || []),
30660             currHidden, geometry, matches;
30661
30662         _.each(_features, function(f) { f.count = 0; });
30663
30664         // adjust the threshold for point/building culling based on viewport size..
30665         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
30666         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
30667
30668         for (var i = 0; i < entities.length; i++) {
30669             geometry = entities[i].geometry(resolver);
30670             if (!(geometry === 'vertex' || geometry === 'relation')) {
30671                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
30672                 for (var j = 0; j < matches.length; j++) {
30673                     _features[matches[j]].count++;
30674                 }
30675             }
30676         }
30677
30678         currHidden = features.hidden();
30679         if (currHidden !== _hidden) {
30680             _hidden = currHidden;
30681             needsRedraw = true;
30682             dispatch.change();
30683         }
30684
30685         return needsRedraw;
30686     };
30687
30688     features.stats = function() {
30689         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
30690         return _stats;
30691     };
30692
30693     features.clear = function(d) {
30694         for (var i = 0; i < d.length; i++) {
30695             features.clearEntity(d[i]);
30696         }
30697     };
30698
30699     features.clearEntity = function(entity) {
30700         delete _cache[iD.Entity.key(entity)];
30701     };
30702
30703     features.reset = function() {
30704         _cache = {};
30705     };
30706
30707     features.getMatches = function(entity, resolver, geometry) {
30708         if (geometry === 'vertex' || geometry === 'relation') return {};
30709
30710         var ent = iD.Entity.key(entity);
30711         if (!_cache[ent]) {
30712             _cache[ent] = {};
30713         }
30714
30715         if (!_cache[ent].matches) {
30716             var matches = {},
30717                 hasMatch = false;
30718
30719             for (var i = 0; i < _keys.length; i++) {
30720                 if (_keys[i] === 'others') {
30721                     if (hasMatch) continue;
30722
30723                     // Multipolygon members:
30724                     // If an entity...
30725                     //   1. is a way that hasn't matched other "interesting" feature rules,
30726                     //   2. and it belongs to a single parent multipolygon relation
30727                     // ...then match whatever feature rules the parent multipolygon has matched.
30728                     // see #2548, #2887
30729                     //
30730                     // IMPORTANT:
30731                     // For this to work, getMatches must be called on relations before ways.
30732                     //
30733                     if (entity.type === 'way') {
30734                         var parents = features.getParents(entity, resolver, geometry);
30735                         if (parents.length === 1 && parents[0].isMultipolygon()) {
30736                             var pkey = iD.Entity.key(parents[0]);
30737                             if (_cache[pkey] && _cache[pkey].matches) {
30738                                 matches = _.clone(_cache[pkey].matches);
30739                                 continue;
30740                             }
30741                         }
30742                     }
30743                 }
30744
30745                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
30746                     matches[_keys[i]] = hasMatch = true;
30747                 }
30748             }
30749             _cache[ent].matches = matches;
30750         }
30751
30752         return _cache[ent].matches;
30753     };
30754
30755     features.getParents = function(entity, resolver, geometry) {
30756         if (geometry === 'point') return [];
30757
30758         var ent = iD.Entity.key(entity);
30759         if (!_cache[ent]) {
30760             _cache[ent] = {};
30761         }
30762
30763         if (!_cache[ent].parents) {
30764             var parents = [];
30765             if (geometry === 'vertex') {
30766                 parents = resolver.parentWays(entity);
30767             } else {   // 'line', 'area', 'relation'
30768                 parents = resolver.parentRelations(entity);
30769             }
30770             _cache[ent].parents = parents;
30771         }
30772         return _cache[ent].parents;
30773     };
30774
30775     features.isHiddenFeature = function(entity, resolver, geometry) {
30776         if (!_hidden.length) return false;
30777         if (!entity.version) return false;
30778
30779         var matches = features.getMatches(entity, resolver, geometry);
30780
30781         for (var i = 0; i < _hidden.length; i++) {
30782             if (matches[_hidden[i]]) return true;
30783         }
30784         return false;
30785     };
30786
30787     features.isHiddenChild = function(entity, resolver, geometry) {
30788         if (!_hidden.length) return false;
30789         if (!entity.version || geometry === 'point') return false;
30790
30791         var parents = features.getParents(entity, resolver, geometry);
30792         if (!parents.length) return false;
30793
30794         for (var i = 0; i < parents.length; i++) {
30795             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
30796                 return false;
30797             }
30798         }
30799         return true;
30800     };
30801
30802     features.hasHiddenConnections = function(entity, resolver) {
30803         if (!_hidden.length) return false;
30804         var childNodes, connections;
30805
30806         if (entity.type === 'midpoint') {
30807             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
30808             connections = [];
30809         } else {
30810             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
30811             connections = features.getParents(entity, resolver, entity.geometry(resolver));
30812         }
30813
30814         // gather ways connected to child nodes..
30815         connections = _.reduce(childNodes, function(result, e) {
30816             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
30817         }, connections);
30818
30819         return connections.length ? _.some(connections, function(e) {
30820             return features.isHidden(e, resolver, e.geometry(resolver));
30821         }) : false;
30822     };
30823
30824     features.isHidden = function(entity, resolver, geometry) {
30825         if (!_hidden.length) return false;
30826         if (!entity.version) return false;
30827
30828         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
30829         return fn(entity, resolver, geometry);
30830     };
30831
30832     features.filter = function(d, resolver) {
30833         if (!_hidden.length) return d;
30834
30835         var result = [];
30836         for (var i = 0; i < d.length; i++) {
30837             var entity = d[i];
30838             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
30839                 result.push(entity);
30840             }
30841         }
30842         return result;
30843     };
30844
30845     return d3.rebind(features, dispatch, 'on');
30846 };
30847 iD.Map = function(context) {
30848     var dimensions = [1, 1],
30849         dispatch = d3.dispatch('move', 'drawn'),
30850         projection = context.projection,
30851         zoom = d3.behavior.zoom()
30852             .translate(projection.translate())
30853             .scale(projection.scale() * 2 * Math.PI)
30854             .scaleExtent([1024, 256 * Math.pow(2, 24)])
30855             .on('zoom', zoomPan),
30856         dblclickEnabled = true,
30857         redrawEnabled = true,
30858         transformStart,
30859         transformed = false,
30860         easing = false,
30861         minzoom = 0,
30862         drawLayers = iD.svg.Layers(projection, context),
30863         drawPoints = iD.svg.Points(projection, context),
30864         drawVertices = iD.svg.Vertices(projection, context),
30865         drawLines = iD.svg.Lines(projection),
30866         drawAreas = iD.svg.Areas(projection),
30867         drawMidpoints = iD.svg.Midpoints(projection, context),
30868         drawLabels = iD.svg.Labels(projection, context),
30869         supersurface,
30870         wrapper,
30871         surface,
30872         mouse,
30873         mousemove;
30874
30875     function map(selection) {
30876         context
30877             .on('change.map', redraw);
30878         context.history()
30879             .on('change.map', redraw);
30880         context.background()
30881             .on('change.map', redraw);
30882         context.features()
30883             .on('redraw.map', redraw);
30884         drawLayers
30885             .on('change.map', function() {
30886                 context.background().updateImagery();
30887                 redraw();
30888             });
30889
30890         selection
30891             .on('dblclick.map', dblClick)
30892             .call(zoom);
30893
30894         supersurface = selection.append('div')
30895             .attr('id', 'supersurface')
30896             .call(iD.util.setTransform, 0, 0);
30897
30898         // Need a wrapper div because Opera can't cope with an absolutely positioned
30899         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
30900         wrapper = supersurface
30901             .append('div')
30902             .attr('class', 'layer layer-data');
30903
30904         map.surface = surface = wrapper
30905             .call(drawLayers)
30906             .selectAll('.surface')
30907             .attr('id', 'surface');
30908
30909         surface
30910             .on('mousedown.zoom', function() {
30911                 if (d3.event.button === 2) {
30912                     d3.event.stopPropagation();
30913                 }
30914             }, true)
30915             .on('mouseup.zoom', function() {
30916                 if (resetTransform()) redraw();
30917             })
30918             .on('mousemove.map', function() {
30919                 mousemove = d3.event;
30920             })
30921             .on('mouseover.vertices', function() {
30922                 if (map.editable() && !transformed) {
30923                     var hover = d3.event.target.__data__;
30924                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
30925                     dispatch.drawn({full: false});
30926                 }
30927             })
30928             .on('mouseout.vertices', function() {
30929                 if (map.editable() && !transformed) {
30930                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
30931                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
30932                     dispatch.drawn({full: false});
30933                 }
30934             });
30935
30936
30937         supersurface
30938             .call(context.background());
30939
30940
30941         context.on('enter.map', function() {
30942             if (map.editable() && !transformed) {
30943                 var all = context.intersects(map.extent()),
30944                     filter = d3.functor(true),
30945                     graph = context.graph();
30946
30947                 all = context.features().filter(all, graph);
30948                 surface
30949                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
30950                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
30951                 dispatch.drawn({full: false});
30952             }
30953         });
30954
30955         map.dimensions(selection.dimensions());
30956
30957         drawLabels.supersurface(supersurface);
30958     }
30959
30960     function pxCenter() {
30961         return [dimensions[0] / 2, dimensions[1] / 2];
30962     }
30963
30964     function drawVector(difference, extent) {
30965         var graph = context.graph(),
30966             features = context.features(),
30967             all = context.intersects(map.extent()),
30968             data, filter;
30969
30970         if (difference) {
30971             var complete = difference.complete(map.extent());
30972             data = _.compact(_.values(complete));
30973             filter = function(d) { return d.id in complete; };
30974             features.clear(data);
30975
30976         } else {
30977             // force a full redraw if gatherStats detects that a feature
30978             // should be auto-hidden (e.g. points or buildings)..
30979             if (features.gatherStats(all, graph, dimensions)) {
30980                 extent = undefined;
30981             }
30982
30983             if (extent) {
30984                 data = context.intersects(map.extent().intersection(extent));
30985                 var set = d3.set(_.map(data, 'id'));
30986                 filter = function(d) { return set.has(d.id); };
30987
30988             } else {
30989                 data = all;
30990                 filter = d3.functor(true);
30991             }
30992         }
30993
30994         data = features.filter(data, graph);
30995
30996         surface
30997             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
30998             .call(drawLines, graph, data, filter)
30999             .call(drawAreas, graph, data, filter)
31000             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
31001             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
31002             .call(drawPoints, graph, data, filter);
31003
31004         dispatch.drawn({full: true});
31005     }
31006
31007     function editOff() {
31008         context.features().resetStats();
31009         surface.selectAll('.layer-osm *').remove();
31010         dispatch.drawn({full: true});
31011     }
31012
31013     function dblClick() {
31014         if (!dblclickEnabled) {
31015             d3.event.preventDefault();
31016             d3.event.stopImmediatePropagation();
31017         }
31018     }
31019
31020     function zoomPan() {
31021         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
31022             surface.interrupt();
31023             iD.ui.flash(context.container())
31024                 .select('.content')
31025                 .text(t('cannot_zoom'));
31026             setZoom(context.minEditableZoom(), true);
31027             queueRedraw();
31028             dispatch.move(map);
31029             return;
31030         }
31031
31032         projection
31033             .translate(d3.event.translate)
31034             .scale(d3.event.scale / (2 * Math.PI));
31035
31036         var scale = d3.event.scale / transformStart[0],
31037             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
31038             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
31039
31040         transformed = true;
31041         iD.util.setTransform(supersurface, tX, tY, scale);
31042         queueRedraw();
31043
31044         dispatch.move(map);
31045     }
31046
31047     function resetTransform() {
31048         if (!transformed) return false;
31049
31050         surface.selectAll('.radial-menu').interrupt().remove();
31051         iD.util.setTransform(supersurface, 0, 0);
31052         transformed = false;
31053         return true;
31054     }
31055
31056     function redraw(difference, extent) {
31057         if (!surface || !redrawEnabled) return;
31058
31059         clearTimeout(timeoutId);
31060
31061         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
31062         // It would result in artifacts where differenced entities are redrawn with
31063         // one transform and unchanged entities with another.
31064         if (resetTransform()) {
31065             difference = extent = undefined;
31066         }
31067
31068         var zoom = String(~~map.zoom());
31069         if (surface.attr('data-zoom') !== zoom) {
31070             surface.attr('data-zoom', zoom)
31071                 .classed('low-zoom', zoom <= 16);
31072         }
31073
31074         if (!difference) {
31075             supersurface.call(context.background());
31076         }
31077
31078         // OSM
31079         if (map.editable()) {
31080             context.loadTiles(projection, dimensions);
31081             drawVector(difference, extent);
31082         } else {
31083             editOff();
31084         }
31085
31086         wrapper
31087             .call(drawLayers);
31088
31089         transformStart = [
31090             projection.scale() * 2 * Math.PI,
31091             projection.translate().slice()];
31092
31093         return map;
31094     }
31095
31096     var timeoutId;
31097     function queueRedraw() {
31098         timeoutId = setTimeout(function() { redraw(); }, 750);
31099     }
31100
31101     function pointLocation(p) {
31102         var translate = projection.translate(),
31103             scale = projection.scale() * 2 * Math.PI;
31104         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
31105     }
31106
31107     function locationPoint(l) {
31108         var translate = projection.translate(),
31109             scale = projection.scale() * 2 * Math.PI;
31110         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
31111     }
31112
31113     map.mouse = function() {
31114         var e = mousemove || d3.event, s;
31115         while ((s = e.sourceEvent)) e = s;
31116         return mouse(e);
31117     };
31118
31119     map.mouseCoordinates = function() {
31120         return projection.invert(map.mouse());
31121     };
31122
31123     map.dblclickEnable = function(_) {
31124         if (!arguments.length) return dblclickEnabled;
31125         dblclickEnabled = _;
31126         return map;
31127     };
31128
31129     map.redrawEnable = function(_) {
31130         if (!arguments.length) return redrawEnabled;
31131         redrawEnabled = _;
31132         return map;
31133     };
31134
31135     function interpolateZoom(_) {
31136         var k = projection.scale(),
31137             t = projection.translate();
31138
31139         surface.node().__chart__ = {
31140             x: t[0],
31141             y: t[1],
31142             k: k * 2 * Math.PI
31143         };
31144
31145         setZoom(_);
31146         projection.scale(k).translate(t);  // undo setZoom projection changes
31147
31148         zoom.event(surface.transition());
31149     }
31150
31151     function setZoom(_, force) {
31152         if (_ === map.zoom() && !force)
31153             return false;
31154         var scale = 256 * Math.pow(2, _),
31155             center = pxCenter(),
31156             l = pointLocation(center);
31157         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
31158         projection.scale(scale / (2 * Math.PI));
31159         zoom.scale(scale);
31160         var t = projection.translate();
31161         l = locationPoint(l);
31162         t[0] += center[0] - l[0];
31163         t[1] += center[1] - l[1];
31164         projection.translate(t);
31165         zoom.translate(projection.translate());
31166         return true;
31167     }
31168
31169     function setCenter(_) {
31170         var c = map.center();
31171         if (_[0] === c[0] && _[1] === c[1])
31172             return false;
31173         var t = projection.translate(),
31174             pxC = pxCenter(),
31175             ll = projection(_);
31176         projection.translate([
31177             t[0] - ll[0] + pxC[0],
31178             t[1] - ll[1] + pxC[1]]);
31179         zoom.translate(projection.translate());
31180         return true;
31181     }
31182
31183     map.pan = function(d) {
31184         var t = projection.translate();
31185         t[0] += d[0];
31186         t[1] += d[1];
31187         projection.translate(t);
31188         zoom.translate(projection.translate());
31189         dispatch.move(map);
31190         return redraw();
31191     };
31192
31193     map.dimensions = function(_) {
31194         if (!arguments.length) return dimensions;
31195         var center = map.center();
31196         dimensions = _;
31197         drawLayers.dimensions(dimensions);
31198         context.background().dimensions(dimensions);
31199         projection.clipExtent([[0, 0], dimensions]);
31200         mouse = iD.util.fastMouse(supersurface.node());
31201         setCenter(center);
31202         return redraw();
31203     };
31204
31205     function zoomIn(integer) {
31206       interpolateZoom(~~map.zoom() + integer);
31207     }
31208
31209     function zoomOut(integer) {
31210       interpolateZoom(~~map.zoom() - integer);
31211     }
31212
31213     map.zoomIn = function() { zoomIn(1); };
31214     map.zoomInFurther = function() { zoomIn(4); };
31215
31216     map.zoomOut = function() { zoomOut(1); };
31217     map.zoomOutFurther = function() { zoomOut(4); };
31218
31219     map.center = function(loc) {
31220         if (!arguments.length) {
31221             return projection.invert(pxCenter());
31222         }
31223
31224         if (setCenter(loc)) {
31225             dispatch.move(map);
31226         }
31227
31228         return redraw();
31229     };
31230
31231     map.zoom = function(z) {
31232         if (!arguments.length) {
31233             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
31234         }
31235
31236         if (z < minzoom) {
31237             surface.interrupt();
31238             iD.ui.flash(context.container())
31239                 .select('.content')
31240                 .text(t('cannot_zoom'));
31241             z = context.minEditableZoom();
31242         }
31243
31244         if (setZoom(z)) {
31245             dispatch.move(map);
31246         }
31247
31248         return redraw();
31249     };
31250
31251     map.zoomTo = function(entity, zoomLimits) {
31252         var extent = entity.extent(context.graph());
31253         if (!isFinite(extent.area())) return;
31254
31255         var zoom = map.trimmedExtentZoom(extent);
31256         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
31257         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
31258     };
31259
31260     map.centerZoom = function(loc, z) {
31261         var centered = setCenter(loc),
31262             zoomed   = setZoom(z);
31263
31264         if (centered || zoomed) {
31265             dispatch.move(map);
31266         }
31267
31268         return redraw();
31269     };
31270
31271     map.centerEase = function(loc2, duration) {
31272         duration = duration || 250;
31273
31274         surface.one('mousedown.ease', function() {
31275             map.cancelEase();
31276         });
31277
31278         if (easing) {
31279             map.cancelEase();
31280         }
31281
31282         var t1 = Date.now(),
31283             t2 = t1 + duration,
31284             loc1 = map.center(),
31285             ease = d3.ease('cubic-in-out');
31286
31287         easing = true;
31288
31289         d3.timer(function() {
31290             if (!easing) return true;  // cancelled ease
31291
31292             var tNow = Date.now();
31293             if (tNow > t2) {
31294                 tNow = t2;
31295                 easing = false;
31296             }
31297
31298             var locNow = iD.geo.interp(loc1, loc2, ease((tNow - t1) / duration));
31299             setCenter(locNow);
31300
31301             d3.event = {
31302                 scale: zoom.scale(),
31303                 translate: zoom.translate()
31304             };
31305
31306             zoomPan();
31307             return !easing;
31308         });
31309
31310         return map;
31311     };
31312
31313     map.cancelEase = function() {
31314         easing = false;
31315         d3.timer.flush();
31316         return map;
31317     };
31318
31319     map.extent = function(_) {
31320         if (!arguments.length) {
31321             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
31322                                  projection.invert([dimensions[0], 0]));
31323         } else {
31324             var extent = iD.geo.Extent(_);
31325             map.centerZoom(extent.center(), map.extentZoom(extent));
31326         }
31327     };
31328
31329     map.trimmedExtent = function(_) {
31330         if (!arguments.length) {
31331             var headerY = 60, footerY = 30, pad = 10;
31332             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
31333                     projection.invert([dimensions[0] - pad, headerY + pad]));
31334         } else {
31335             var extent = iD.geo.Extent(_);
31336             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
31337         }
31338     };
31339
31340     function calcZoom(extent, dim) {
31341         var tl = projection([extent[0][0], extent[1][1]]),
31342             br = projection([extent[1][0], extent[0][1]]);
31343
31344         // Calculate maximum zoom that fits extent
31345         var hFactor = (br[0] - tl[0]) / dim[0],
31346             vFactor = (br[1] - tl[1]) / dim[1],
31347             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
31348             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
31349             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
31350
31351         return newZoom;
31352     }
31353
31354     map.extentZoom = function(_) {
31355         return calcZoom(iD.geo.Extent(_), dimensions);
31356     };
31357
31358     map.trimmedExtentZoom = function(_) {
31359         var trimY = 120, trimX = 40,
31360             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
31361         return calcZoom(iD.geo.Extent(_), trimmed);
31362     };
31363
31364     map.editable = function() {
31365         return map.zoom() >= context.minEditableZoom();
31366     };
31367
31368     map.minzoom = function(_) {
31369         if (!arguments.length) return minzoom;
31370         minzoom = _;
31371         return map;
31372     };
31373
31374     map.layers = drawLayers;
31375
31376     return d3.rebind(map, dispatch, 'on');
31377 };
31378 iD.TileLayer = function(context) {
31379     var tileSize = 256,
31380         tile = d3.geo.tile(),
31381         projection,
31382         cache = {},
31383         tileOrigin,
31384         z,
31385         transformProp = iD.util.prefixCSSProperty('Transform'),
31386         source = d3.functor('');
31387
31388
31389     // blacklist overlay tiles around Null Island..
31390     function nearNullIsland(x, y, z) {
31391         if (z >= 7) {
31392             var center = Math.pow(2, z - 1),
31393                 width = Math.pow(2, z - 6),
31394                 min = center - (width / 2),
31395                 max = center + (width / 2) - 1;
31396             return x >= min && x <= max && y >= min && y <= max;
31397         }
31398         return false;
31399     }
31400
31401     function tileSizeAtZoom(d, z) {
31402         var epsilon = 0.002;
31403         return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
31404     }
31405
31406     function atZoom(t, distance) {
31407         var power = Math.pow(2, distance);
31408         return [
31409             Math.floor(t[0] * power),
31410             Math.floor(t[1] * power),
31411             t[2] + distance];
31412     }
31413
31414     function lookUp(d) {
31415         for (var up = -1; up > -d[2]; up--) {
31416             var tile = atZoom(d, up);
31417             if (cache[source.url(tile)] !== false) {
31418                 return tile;
31419             }
31420         }
31421     }
31422
31423     function uniqueBy(a, n) {
31424         var o = [], seen = {};
31425         for (var i = 0; i < a.length; i++) {
31426             if (seen[a[i][n]] === undefined) {
31427                 o.push(a[i]);
31428                 seen[a[i][n]] = true;
31429             }
31430         }
31431         return o;
31432     }
31433
31434     function addSource(d) {
31435         d.push(source.url(d));
31436         return d;
31437     }
31438
31439     // Update tiles based on current state of `projection`.
31440     function background(selection) {
31441         tile.scale(projection.scale() * 2 * Math.PI)
31442             .translate(projection.translate());
31443
31444         tileOrigin = [
31445             projection.scale() * Math.PI - projection.translate()[0],
31446             projection.scale() * Math.PI - projection.translate()[1]];
31447
31448         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
31449
31450         render(selection);
31451     }
31452
31453     // Derive the tiles onscreen, remove those offscreen and position them.
31454     // Important that this part not depend on `projection` because it's
31455     // rentered when tiles load/error (see #644).
31456     function render(selection) {
31457         var requests = [];
31458         var showDebug = context.getDebug('tile') && !source.overlay;
31459
31460         if (source.validZoom(z)) {
31461             tile().forEach(function(d) {
31462                 addSource(d);
31463                 if (d[3] === '') return;
31464                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
31465                 requests.push(d);
31466                 if (cache[d[3]] === false && lookUp(d)) {
31467                     requests.push(addSource(lookUp(d)));
31468                 }
31469             });
31470
31471             requests = uniqueBy(requests, 3).filter(function(r) {
31472                 if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
31473                     return false;
31474                 }
31475                 // don't re-request tiles which have failed in the past
31476                 return cache[r[3]] !== false;
31477             });
31478         }
31479
31480         var pixelOffset = [
31481             source.offset()[0] * Math.pow(2, z),
31482             source.offset()[1] * Math.pow(2, z)
31483         ];
31484
31485         function load(d) {
31486             cache[d[3]] = true;
31487             d3.select(this)
31488                 .on('error', null)
31489                 .on('load', null)
31490                 .classed('tile-loaded', true);
31491             render(selection);
31492         }
31493
31494         function error(d) {
31495             cache[d[3]] = false;
31496             d3.select(this)
31497                 .on('error', null)
31498                 .on('load', null)
31499                 .remove();
31500             render(selection);
31501         }
31502
31503         function imageTransform(d) {
31504             var _ts = tileSize * Math.pow(2, z - d[2]);
31505             var scale = tileSizeAtZoom(d, z);
31506             return 'translate(' +
31507                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
31508                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
31509                 'scale(' + scale + ',' + scale + ')';
31510         }
31511
31512         function debugTransform(d) {
31513             var _ts = tileSize * Math.pow(2, z - d[2]);
31514             var scale = tileSizeAtZoom(d, z);
31515             return 'translate(' +
31516                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)) + 'px,' +
31517                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) + 'px)';
31518         }
31519
31520         var image = selection
31521             .selectAll('img')
31522             .data(requests, function(d) { return d[3]; });
31523
31524         image.exit()
31525             .style(transformProp, imageTransform)
31526             .classed('tile-removing', true)
31527             .each(function() {
31528                 var tile = d3.select(this);
31529                 window.setTimeout(function() {
31530                     if (tile.classed('tile-removing')) {
31531                         tile.remove();
31532                     }
31533                 }, 300);
31534             });
31535
31536         image.enter().append('img')
31537             .attr('class', 'tile')
31538             .attr('src', function(d) { return d[3]; })
31539             .on('error', error)
31540             .on('load', load);
31541
31542         image
31543             .style(transformProp, imageTransform)
31544             .classed('tile-debug', showDebug)
31545             .classed('tile-removing', false);
31546
31547
31548         var debug = selection.selectAll('.tile-label-debug')
31549             .data(showDebug ? requests : [], function(d) { return d[3]; });
31550
31551         debug.exit()
31552             .remove();
31553
31554         debug.enter()
31555             .append('div')
31556             .attr('class', 'tile-label-debug');
31557
31558         debug
31559             .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; })
31560             .style(transformProp, debugTransform);
31561     }
31562
31563     background.projection = function(_) {
31564         if (!arguments.length) return projection;
31565         projection = _;
31566         return background;
31567     };
31568
31569     background.dimensions = function(_) {
31570         if (!arguments.length) return tile.size();
31571         tile.size(_);
31572         return background;
31573     };
31574
31575     background.source = function(_) {
31576         if (!arguments.length) return source;
31577         source = _;
31578         cache = {};
31579         tile.scaleExtent(source.scaleExtent);
31580         return background;
31581     };
31582
31583     return background;
31584 };
31585 iD.svg = {
31586     PointTransform: function(projection) {
31587         return function(entity) {
31588             // http://jsperf.com/short-array-join
31589             var pt = projection(entity.loc);
31590             return 'translate(' + pt[0] + ',' + pt[1] + ')';
31591         };
31592     },
31593
31594     Path: function(projection, graph, polygon) {
31595         var cache = {},
31596             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
31597             project = projection.stream,
31598             path = d3.geo.path()
31599                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
31600
31601         return function(entity) {
31602             if (entity.id in cache) {
31603                 return cache[entity.id];
31604             } else {
31605                 return cache[entity.id] = path(entity.asGeoJSON(graph));
31606             }
31607         };
31608     },
31609
31610     OneWaySegments: function(projection, graph, dt) {
31611         return function(entity) {
31612             var a,
31613                 b,
31614                 i = 0,
31615                 offset = dt,
31616                 segments = [],
31617                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
31618                 coordinates = graph.childNodes(entity).map(function(n) {
31619                     return n.loc;
31620                 });
31621
31622             if (entity.tags.oneway === '-1') coordinates.reverse();
31623
31624             d3.geo.stream({
31625                 type: 'LineString',
31626                 coordinates: coordinates
31627             }, projection.stream(clip({
31628                 lineStart: function() {},
31629                 lineEnd: function() {
31630                     a = null;
31631                 },
31632                 point: function(x, y) {
31633                     b = [x, y];
31634
31635                     if (a) {
31636                         var span = iD.geo.euclideanDistance(a, b) - offset;
31637
31638                         if (span >= 0) {
31639                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
31640                                 dx = dt * Math.cos(angle),
31641                                 dy = dt * Math.sin(angle),
31642                                 p = [a[0] + offset * Math.cos(angle),
31643                                      a[1] + offset * Math.sin(angle)];
31644
31645                             var segment = 'M' + a[0] + ',' + a[1] +
31646                                           'L' + p[0] + ',' + p[1];
31647
31648                             for (span -= dt; span >= 0; span -= dt) {
31649                                 p[0] += dx;
31650                                 p[1] += dy;
31651                                 segment += 'L' + p[0] + ',' + p[1];
31652                             }
31653
31654                             segment += 'L' + b[0] + ',' + b[1];
31655                             segments.push({id: entity.id, index: i, d: segment});
31656                         }
31657
31658                         offset = -span;
31659                         i++;
31660                     }
31661
31662                     a = b;
31663                 }
31664             })));
31665
31666             return segments;
31667         };
31668     },
31669
31670     RelationMemberTags: function(graph) {
31671         return function(entity) {
31672             var tags = entity.tags;
31673             graph.parentRelations(entity).forEach(function(relation) {
31674                 var type = relation.tags.type;
31675                 if (type === 'multipolygon' || type === 'boundary') {
31676                     tags = _.extend({}, relation.tags, tags);
31677                 }
31678             });
31679             return tags;
31680         };
31681     }
31682 };
31683 iD.svg.Areas = function(projection) {
31684     // Patterns only work in Firefox when set directly on element.
31685     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
31686     var patterns = {
31687         wetland: 'wetland',
31688         beach: 'beach',
31689         scrub: 'scrub',
31690         construction: 'construction',
31691         military: 'construction',
31692         cemetery: 'cemetery',
31693         grave_yard: 'cemetery',
31694         meadow: 'meadow',
31695         farm: 'farmland',
31696         farmland: 'farmland',
31697         orchard: 'orchard'
31698     };
31699
31700     var patternKeys = ['landuse', 'natural', 'amenity'];
31701
31702     function setPattern(d) {
31703         for (var i = 0; i < patternKeys.length; i++) {
31704             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
31705                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
31706                 return;
31707             }
31708         }
31709         this.style.fill = this.style.stroke = '';
31710     }
31711
31712     return function drawAreas(surface, graph, entities, filter) {
31713         var path = iD.svg.Path(projection, graph, true),
31714             areas = {},
31715             multipolygon;
31716
31717         for (var i = 0; i < entities.length; i++) {
31718             var entity = entities[i];
31719             if (entity.geometry(graph) !== 'area') continue;
31720
31721             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
31722             if (multipolygon) {
31723                 areas[multipolygon.id] = {
31724                     entity: multipolygon.mergeTags(entity.tags),
31725                     area: Math.abs(entity.area(graph))
31726                 };
31727             } else if (!areas[entity.id]) {
31728                 areas[entity.id] = {
31729                     entity: entity,
31730                     area: Math.abs(entity.area(graph))
31731                 };
31732             }
31733         }
31734
31735         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
31736         areas.sort(function areaSort(a, b) { return b.area - a.area; });
31737         areas = _.map(areas, 'entity');
31738
31739         var strokes = areas.filter(function(area) {
31740             return area.type === 'way';
31741         });
31742
31743         var data = {
31744             clip: areas,
31745             shadow: strokes,
31746             stroke: strokes,
31747             fill: areas
31748         };
31749
31750         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
31751            .filter(filter)
31752            .data(data.clip, iD.Entity.key);
31753
31754         clipPaths.enter()
31755            .append('clipPath')
31756            .attr('class', 'clipPath')
31757            .attr('id', function(entity) { return entity.id + '-clippath'; })
31758            .append('path');
31759
31760         clipPaths.selectAll('path')
31761            .attr('d', path);
31762
31763         clipPaths.exit()
31764            .remove();
31765
31766         var areagroup = surface
31767             .selectAll('.layer-areas')
31768             .selectAll('g.areagroup')
31769             .data(['fill', 'shadow', 'stroke']);
31770
31771         areagroup.enter()
31772             .append('g')
31773             .attr('class', function(d) { return 'layer areagroup area-' + d; });
31774
31775         var paths = areagroup
31776             .selectAll('path')
31777             .filter(filter)
31778             .data(function(layer) { return data[layer]; }, iD.Entity.key);
31779
31780         // Remove exiting areas first, so they aren't included in the `fills`
31781         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
31782         paths.exit()
31783             .remove();
31784
31785         var fills = surface.selectAll('.area-fill path.area')[0];
31786
31787         var bisect = d3.bisector(function(node) {
31788             return -node.__data__.area(graph);
31789         }).left;
31790
31791         function sortedByArea(entity) {
31792             if (this.__data__ === 'fill') {
31793                 return fills[bisect(fills, -entity.area(graph))];
31794             }
31795         }
31796
31797         paths.enter()
31798             .insert('path', sortedByArea)
31799             .each(function(entity) {
31800                 var layer = this.parentNode.__data__;
31801
31802                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
31803
31804                 if (layer === 'fill') {
31805                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
31806                     setPattern.apply(this, arguments);
31807                 }
31808             })
31809             .call(iD.svg.TagClasses());
31810
31811         paths
31812             .attr('d', path);
31813     };
31814 };
31815 iD.svg.Debug = function(projection, context) {
31816
31817     function multipolygons(imagery) {
31818         return imagery.map(function(data) {
31819             return {
31820                 type: 'MultiPolygon',
31821                 coordinates: [ data.polygon ]
31822             };
31823         });
31824     }
31825
31826     function drawDebug(surface) {
31827         var showsTile = context.getDebug('tile'),
31828             showsCollision = context.getDebug('collision'),
31829             showsImagery = context.getDebug('imagery'),
31830             showsImperial = context.getDebug('imperial'),
31831             showsDriveLeft = context.getDebug('driveLeft'),
31832             path = d3.geo.path().projection(projection);
31833
31834
31835         var debugData = [];
31836         if (showsTile) {
31837             debugData.push({ class: 'red', label: 'tile' });
31838         }
31839         if (showsCollision) {
31840             debugData.push({ class: 'yellow', label: 'collision' });
31841         }
31842         if (showsImagery) {
31843             debugData.push({ class: 'orange', label: 'imagery' });
31844         }
31845         if (showsImperial) {
31846             debugData.push({ class: 'cyan', label: 'imperial' });
31847         }
31848         if (showsDriveLeft) {
31849             debugData.push({ class: 'green', label: 'driveLeft' });
31850         }
31851
31852
31853         var legend = d3.select('#content')
31854             .selectAll('.debug-legend')
31855             .data(debugData.length ? [0] : []);
31856
31857         legend.enter()
31858             .append('div')
31859             .attr('class', 'fillD debug-legend');
31860
31861         legend.exit()
31862             .remove();
31863
31864
31865         var legendItems = legend.selectAll('.debug-legend-item')
31866             .data(debugData, function(d) { return d.label; });
31867
31868         legendItems.enter()
31869             .append('span')
31870             .attr('class', function(d) { return 'debug-legend-item ' + d.class; })
31871             .text(function(d) { return d.label; });
31872
31873         legendItems.exit()
31874             .remove();
31875
31876
31877         var layer = surface.selectAll('.layer-debug')
31878             .data(showsImagery || showsImperial || showsDriveLeft ? [0] : []);
31879
31880         layer.enter()
31881             .append('g')
31882             .attr('class', 'layer-debug');
31883
31884         layer.exit()
31885             .remove();
31886
31887
31888         var extent = context.map().extent(),
31889             availableImagery = showsImagery && multipolygons(iD.data.imagery.filter(function(source) {
31890                 if (!source.polygon) return false;
31891                 return source.polygon.some(function(polygon) {
31892                     return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
31893                 });
31894             }));
31895
31896         var imagery = layer.selectAll('path.debug-imagery')
31897             .data(showsImagery ? availableImagery : []);
31898
31899         imagery.enter()
31900             .append('path')
31901             .attr('class', 'debug-imagery debug orange');
31902
31903         imagery.exit()
31904             .remove();
31905
31906
31907         var imperial = layer
31908             .selectAll('path.debug-imperial')
31909             .data(showsImperial ? [iD.data.imperial] : []);
31910
31911         imperial.enter()
31912             .append('path')
31913             .attr('class', 'debug-imperial debug cyan');
31914
31915         imperial.exit()
31916             .remove();
31917
31918
31919         var driveLeft = layer
31920             .selectAll('path.debug-drive-left')
31921             .data(showsDriveLeft ? [iD.data.driveLeft] : []);
31922
31923         driveLeft.enter()
31924             .append('path')
31925             .attr('class', 'debug-drive-left debug green');
31926
31927         driveLeft.exit()
31928             .remove();
31929
31930
31931         // update
31932         layer.selectAll('path')
31933             .attr('d', path);
31934     }
31935
31936     // This looks strange because `enabled` methods on other layers are
31937     // chainable getter/setters, and this one is just a getter.
31938     drawDebug.enabled = function() {
31939         if (!arguments.length) {
31940             return context.getDebug('tile') ||
31941                 context.getDebug('collision') ||
31942                 context.getDebug('imagery') ||
31943                 context.getDebug('imperial') ||
31944                 context.getDebug('driveLeft');
31945         } else {
31946             return this;
31947         }
31948     };
31949
31950     return drawDebug;
31951 };
31952 /*
31953     A standalone SVG element that contains only a `defs` sub-element. To be
31954     used once globally, since defs IDs must be unique within a document.
31955 */
31956 iD.svg.Defs = function(context) {
31957
31958     function SVGSpriteDefinition(id, href) {
31959         return function(defs) {
31960             d3.xml(href, 'image/svg+xml', function(err, svg) {
31961                 if (err) return;
31962                 defs.node().appendChild(
31963                     d3.select(svg.documentElement).attr('id', id).node()
31964                 );
31965             });
31966         };
31967     }
31968
31969     return function drawDefs(selection) {
31970         var defs = selection.append('defs');
31971
31972         // marker
31973         defs.append('marker')
31974             .attr({
31975                 id: 'oneway-marker',
31976                 viewBox: '0 0 10 10',
31977                 refY: 2.5,
31978                 refX: 5,
31979                 markerWidth: 2,
31980                 markerHeight: 2,
31981                 markerUnits: 'strokeWidth',
31982                 orient: 'auto'
31983             })
31984             .append('path')
31985             .attr('class', 'oneway')
31986             .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')
31987             .attr('stroke', 'none')
31988             .attr('fill', '#000')
31989             .attr('opacity', '0.5');
31990
31991         // patterns
31992         var patterns = defs.selectAll('pattern')
31993             .data([
31994                 // pattern name, pattern image name
31995                 ['wetland', 'wetland'],
31996                 ['construction', 'construction'],
31997                 ['cemetery', 'cemetery'],
31998                 ['orchard', 'orchard'],
31999                 ['farmland', 'farmland'],
32000                 ['beach', 'dots'],
32001                 ['scrub', 'dots'],
32002                 ['meadow', 'dots']
32003             ])
32004             .enter()
32005             .append('pattern')
32006             .attr({
32007                 id: function (d) {
32008                     return 'pattern-' + d[0];
32009                 },
32010                 width: 32,
32011                 height: 32,
32012                 patternUnits: 'userSpaceOnUse'
32013             });
32014
32015         patterns.append('rect')
32016             .attr({
32017                 x: 0,
32018                 y: 0,
32019                 width: 32,
32020                 height: 32,
32021                 'class': function (d) {
32022                     return 'pattern-color-' + d[0];
32023                 }
32024             });
32025
32026         patterns.append('image')
32027             .attr({
32028                 x: 0,
32029                 y: 0,
32030                 width: 32,
32031                 height: 32
32032             })
32033             .attr('xlink:href', function (d) {
32034                 return context.imagePath('pattern/' + d[1] + '.png');
32035             });
32036
32037         // clip paths
32038         defs.selectAll()
32039             .data([12, 18, 20, 32, 45])
32040             .enter().append('clipPath')
32041             .attr('id', function (d) {
32042                 return 'clip-square-' + d;
32043             })
32044             .append('rect')
32045             .attr('x', 0)
32046             .attr('y', 0)
32047             .attr('width', function (d) {
32048                 return d;
32049             })
32050             .attr('height', function (d) {
32051                 return d;
32052             });
32053
32054         defs.call(SVGSpriteDefinition(
32055             'iD-sprite',
32056             context.imagePath('iD-sprite.svg')));
32057
32058         defs.call(SVGSpriteDefinition(
32059             'maki-sprite',
32060             context.imagePath('maki-sprite.svg')));
32061     };
32062 };
32063 iD.svg.Gpx = function(projection, context, dispatch) {
32064     var showLabels = true,
32065         layer;
32066
32067     function init() {
32068         if (iD.svg.Gpx.initialized) return;  // run once
32069
32070         iD.svg.Gpx.geojson = {};
32071         iD.svg.Gpx.enabled = true;
32072
32073         function over() {
32074             d3.event.stopPropagation();
32075             d3.event.preventDefault();
32076             d3.event.dataTransfer.dropEffect = 'copy';
32077         }
32078
32079         d3.select('body')
32080             .attr('dropzone', 'copy')
32081             .on('drop.localgpx', function() {
32082                 d3.event.stopPropagation();
32083                 d3.event.preventDefault();
32084                 if (!iD.detect().filedrop) return;
32085                 drawGpx.files(d3.event.dataTransfer.files);
32086             })
32087             .on('dragenter.localgpx', over)
32088             .on('dragexit.localgpx', over)
32089             .on('dragover.localgpx', over);
32090
32091         iD.svg.Gpx.initialized = true;
32092     }
32093
32094
32095     function drawGpx(surface) {
32096         var geojson = iD.svg.Gpx.geojson,
32097             enabled = iD.svg.Gpx.enabled;
32098
32099         layer = surface.selectAll('.layer-gpx')
32100             .data(enabled ? [0] : []);
32101
32102         layer.enter()
32103             .append('g')
32104             .attr('class', 'layer-gpx');
32105
32106         layer.exit()
32107             .remove();
32108
32109
32110         var paths = layer
32111             .selectAll('path')
32112             .data([geojson]);
32113
32114         paths.enter()
32115             .append('path')
32116             .attr('class', 'gpx');
32117
32118         paths.exit()
32119             .remove();
32120
32121         var path = d3.geo.path()
32122             .projection(projection);
32123
32124         paths
32125             .attr('d', path);
32126
32127
32128         var labels = layer.selectAll('text')
32129             .data(showLabels && geojson.features ? geojson.features : []);
32130
32131         labels.enter()
32132             .append('text')
32133             .attr('class', 'gpx');
32134
32135         labels.exit()
32136             .remove();
32137
32138         labels
32139             .text(function(d) {
32140                 return d.properties.desc || d.properties.name;
32141             })
32142             .attr('x', function(d) {
32143                 var centroid = path.centroid(d);
32144                 return centroid[0] + 7;
32145             })
32146             .attr('y', function(d) {
32147                 var centroid = path.centroid(d);
32148                 return centroid[1];
32149             });
32150
32151     }
32152
32153     function toDom(x) {
32154         return (new DOMParser()).parseFromString(x, 'text/xml');
32155     }
32156
32157     drawGpx.showLabels = function(_) {
32158         if (!arguments.length) return showLabels;
32159         showLabels = _;
32160         return this;
32161     };
32162
32163     drawGpx.enabled = function(_) {
32164         if (!arguments.length) return iD.svg.Gpx.enabled;
32165         iD.svg.Gpx.enabled = _;
32166         dispatch.change();
32167         return this;
32168     };
32169
32170     drawGpx.hasGpx = function() {
32171         var geojson = iD.svg.Gpx.geojson;
32172         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
32173     };
32174
32175     drawGpx.geojson = function(gj) {
32176         if (!arguments.length) return iD.svg.Gpx.geojson;
32177         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
32178         iD.svg.Gpx.geojson = gj;
32179         dispatch.change();
32180         return this;
32181     };
32182
32183     drawGpx.url = function(url) {
32184         d3.text(url, function(err, data) {
32185             if (!err) {
32186                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
32187             }
32188         });
32189         return this;
32190     };
32191
32192     drawGpx.files = function(fileList) {
32193         if (!fileList.length) return this;
32194         var f = fileList[0],
32195             reader = new FileReader();
32196
32197         reader.onload = function(e) {
32198             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
32199         };
32200
32201         reader.readAsText(f);
32202         return this;
32203     };
32204
32205     drawGpx.fitZoom = function() {
32206         if (!this.hasGpx()) return this;
32207         var geojson = iD.svg.Gpx.geojson;
32208
32209         var map = context.map(),
32210             viewport = map.trimmedExtent().polygon(),
32211             coords = _.reduce(geojson.features, function(coords, feature) {
32212                 var c = feature.geometry.coordinates;
32213                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
32214             }, []);
32215
32216         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
32217             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
32218             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
32219         }
32220
32221         return this;
32222     };
32223
32224     init();
32225     return drawGpx;
32226 };
32227 iD.svg.Icon = function(name, svgklass, useklass) {
32228     return function drawIcon(selection) {
32229         selection.selectAll('svg')
32230             .data([0])
32231             .enter()
32232             .append('svg')
32233             .attr('class', 'icon ' + (svgklass || ''))
32234             .append('use')
32235             .attr('xlink:href', name)
32236             .attr('class', useklass);
32237     };
32238 };
32239 iD.svg.Labels = function(projection, context) {
32240     var path = d3.geo.path().projection(projection);
32241
32242     // Replace with dict and iterate over entities tags instead?
32243     var label_stack = [
32244         ['line', 'aeroway'],
32245         ['line', 'highway'],
32246         ['line', 'railway'],
32247         ['line', 'waterway'],
32248         ['area', 'aeroway'],
32249         ['area', 'amenity'],
32250         ['area', 'building'],
32251         ['area', 'historic'],
32252         ['area', 'leisure'],
32253         ['area', 'man_made'],
32254         ['area', 'natural'],
32255         ['area', 'shop'],
32256         ['area', 'tourism'],
32257         ['point', 'aeroway'],
32258         ['point', 'amenity'],
32259         ['point', 'building'],
32260         ['point', 'historic'],
32261         ['point', 'leisure'],
32262         ['point', 'man_made'],
32263         ['point', 'natural'],
32264         ['point', 'shop'],
32265         ['point', 'tourism'],
32266         ['line', 'name'],
32267         ['area', 'name'],
32268         ['point', 'name']
32269     ];
32270
32271     var default_size = 12;
32272
32273     var font_sizes = label_stack.map(function(d) {
32274         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
32275             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
32276         if (m) return parseInt(m[1], 10);
32277
32278         style = iD.util.getStyle('text.' + d[0]);
32279         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
32280         if (m) return parseInt(m[1], 10);
32281
32282         return default_size;
32283     });
32284
32285     var iconSize = 18;
32286
32287     var pointOffsets = [
32288         [15, -11, 'start'], // right
32289         [10, -11, 'start'], // unused right now
32290         [-15, -11, 'end']
32291     ];
32292
32293     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
32294         75, 20, 80, 15, 95, 10, 90, 5, 95];
32295
32296
32297     var noIcons = ['building', 'landuse', 'natural'];
32298     function blacklisted(preset) {
32299         return _.some(noIcons, function(s) {
32300             return preset.id.indexOf(s) >= 0;
32301         });
32302     }
32303
32304     function get(array, prop) {
32305         return function(d, i) { return array[i][prop]; };
32306     }
32307
32308     var textWidthCache = {};
32309
32310     function textWidth(text, size, elem) {
32311         var c = textWidthCache[size];
32312         if (!c) c = textWidthCache[size] = {};
32313
32314         if (c[text]) {
32315             return c[text];
32316
32317         } else if (elem) {
32318             c[text] = elem.getComputedTextLength();
32319             return c[text];
32320
32321         } else {
32322             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
32323             if (str === null) {
32324                 return size / 3 * 2 * text.length;
32325             } else {
32326                 return size / 3 * (2 * text.length + str.length);
32327             }
32328         }
32329     }
32330
32331     function drawLineLabels(group, entities, filter, classes, labels) {
32332         var texts = group.selectAll('text.' + classes)
32333             .filter(filter)
32334             .data(entities, iD.Entity.key);
32335
32336         texts.enter()
32337             .append('text')
32338             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
32339             .append('textPath')
32340             .attr('class', 'textpath');
32341
32342
32343         texts.selectAll('.textpath')
32344             .filter(filter)
32345             .data(entities, iD.Entity.key)
32346             .attr({
32347                 'startOffset': '50%',
32348                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
32349             })
32350             .text(iD.util.displayName);
32351
32352         texts.exit().remove();
32353     }
32354
32355     function drawLinePaths(group, entities, filter, classes, labels) {
32356         var halos = group.selectAll('path')
32357             .filter(filter)
32358             .data(entities, iD.Entity.key);
32359
32360         halos.enter()
32361             .append('path')
32362             .style('stroke-width', get(labels, 'font-size'))
32363             .attr('id', function(d) { return 'labelpath-' + d.id; })
32364             .attr('class', classes);
32365
32366         halos.attr('d', get(labels, 'lineString'));
32367
32368         halos.exit().remove();
32369     }
32370
32371     function drawPointLabels(group, entities, filter, classes, labels) {
32372         var texts = group.selectAll('text.' + classes)
32373             .filter(filter)
32374             .data(entities, iD.Entity.key);
32375
32376         texts.enter()
32377             .append('text')
32378             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
32379
32380         texts.attr('x', get(labels, 'x'))
32381             .attr('y', get(labels, 'y'))
32382             .style('text-anchor', get(labels, 'textAnchor'))
32383             .text(iD.util.displayName)
32384             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
32385
32386         texts.exit().remove();
32387         return texts;
32388     }
32389
32390     function drawAreaLabels(group, entities, filter, classes, labels) {
32391         entities = entities.filter(hasText);
32392         labels = labels.filter(hasText);
32393         return drawPointLabels(group, entities, filter, classes, labels);
32394
32395         function hasText(d, i) {
32396             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
32397         }
32398     }
32399
32400     function drawAreaIcons(group, entities, filter, classes, labels) {
32401         var icons = group.selectAll('use')
32402             .filter(filter)
32403             .data(entities, iD.Entity.key);
32404
32405         icons.enter()
32406             .append('use')
32407             .attr('class', 'icon areaicon')
32408             .attr('width', '18px')
32409             .attr('height', '18px');
32410
32411         icons.attr('transform', get(labels, 'transform'))
32412             .attr('xlink:href', function(d) {
32413                 var icon = context.presets().match(d, context.graph()).icon;
32414                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
32415             });
32416
32417
32418         icons.exit().remove();
32419     }
32420
32421     function reverse(p) {
32422         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
32423         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
32424     }
32425
32426     function lineString(nodes) {
32427         return 'M' + nodes.join('L');
32428     }
32429
32430     function subpath(nodes, from, to) {
32431         function segmentLength(i) {
32432             var dx = nodes[i][0] - nodes[i + 1][0];
32433             var dy = nodes[i][1] - nodes[i + 1][1];
32434             return Math.sqrt(dx * dx + dy * dy);
32435         }
32436
32437         var sofar = 0,
32438             start, end, i0, i1;
32439         for (var i = 0; i < nodes.length - 1; i++) {
32440             var current = segmentLength(i);
32441             var portion;
32442             if (!start && sofar + current >= from) {
32443                 portion = (from - sofar) / current;
32444                 start = [
32445                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
32446                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
32447                 ];
32448                 i0 = i + 1;
32449             }
32450             if (!end && sofar + current >= to) {
32451                 portion = (to - sofar) / current;
32452                 end = [
32453                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
32454                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
32455                 ];
32456                 i1 = i + 1;
32457             }
32458             sofar += current;
32459
32460         }
32461         var ret = nodes.slice(i0, i1);
32462         ret.unshift(start);
32463         ret.push(end);
32464         return ret;
32465
32466     }
32467
32468     function hideOnMouseover() {
32469         var layers = d3.select(this)
32470             .selectAll('.layer-label, .layer-halo');
32471
32472         layers.selectAll('.proximate')
32473             .classed('proximate', false);
32474
32475         var mouse = context.mouse(),
32476             pad = 50,
32477             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
32478             ids = _.map(rtree.search(rect), 'id');
32479
32480         if (!ids.length) return;
32481         layers.selectAll('.' + ids.join(', .'))
32482             .classed('proximate', true);
32483     }
32484
32485     var rtree = rbush(),
32486         rectangles = {};
32487
32488     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
32489         var hidePoints = !surface.selectAll('.node.point').node();
32490
32491         var labelable = [], i, k, entity;
32492         for (i = 0; i < label_stack.length; i++) labelable.push([]);
32493
32494         if (fullRedraw) {
32495             rtree.clear();
32496             rectangles = {};
32497         } else {
32498             for (i = 0; i < entities.length; i++) {
32499                 rtree.remove(rectangles[entities[i].id]);
32500             }
32501         }
32502
32503         // Split entities into groups specified by label_stack
32504         for (i = 0; i < entities.length; i++) {
32505             entity = entities[i];
32506             var geometry = entity.geometry(graph);
32507
32508             if (geometry === 'vertex')
32509                 continue;
32510             if (hidePoints && geometry === 'point')
32511                 continue;
32512
32513             var preset = geometry === 'area' && context.presets().match(entity, graph),
32514                 icon = preset && !blacklisted(preset) && preset.icon;
32515
32516             if (!icon && !iD.util.displayName(entity))
32517                 continue;
32518
32519             for (k = 0; k < label_stack.length; k++) {
32520                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
32521                     labelable[k].push(entity);
32522                     break;
32523                 }
32524             }
32525         }
32526
32527         var positions = {
32528             point: [],
32529             line: [],
32530             area: []
32531         };
32532
32533         var labelled = {
32534             point: [],
32535             line: [],
32536             area: []
32537         };
32538
32539         // Try and find a valid label for labellable entities
32540         for (k = 0; k < labelable.length; k++) {
32541             var font_size = font_sizes[k];
32542             for (i = 0; i < labelable[k].length; i++) {
32543                 entity = labelable[k][i];
32544                 var name = iD.util.displayName(entity),
32545                     width = name && textWidth(name, font_size),
32546                     p;
32547                 if (entity.geometry(graph) === 'point') {
32548                     p = getPointLabel(entity, width, font_size);
32549                 } else if (entity.geometry(graph) === 'line') {
32550                     p = getLineLabel(entity, width, font_size);
32551                 } else if (entity.geometry(graph) === 'area') {
32552                     p = getAreaLabel(entity, width, font_size);
32553                 }
32554                 if (p) {
32555                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
32556                     positions[entity.geometry(graph)].push(p);
32557                     labelled[entity.geometry(graph)].push(entity);
32558                 }
32559             }
32560         }
32561
32562         function getPointLabel(entity, width, height) {
32563             var coord = projection(entity.loc),
32564                 m = 5,  // margin
32565                 offset = pointOffsets[0],
32566                 p = {
32567                     height: height,
32568                     width: width,
32569                     x: coord[0] + offset[0],
32570                     y: coord[1] + offset[1],
32571                     textAnchor: offset[2]
32572                 };
32573             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
32574             if (tryInsert(rect, entity.id)) return p;
32575         }
32576
32577
32578         function getLineLabel(entity, width, height) {
32579             var nodes = _.map(graph.childNodes(entity), 'loc').map(projection),
32580                 length = iD.geo.pathLength(nodes);
32581             if (length < width + 20) return;
32582
32583             for (var i = 0; i < lineOffsets.length; i++) {
32584                 var offset = lineOffsets[i],
32585                     middle = offset / 100 * length,
32586                     start = middle - width/2;
32587                 if (start < 0 || start + width > length) continue;
32588                 var sub = subpath(nodes, start, start + width),
32589                     rev = reverse(sub),
32590                     rect = [
32591                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
32592                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
32593                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
32594                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
32595                     ];
32596                 if (rev) sub = sub.reverse();
32597                 if (tryInsert(rect, entity.id)) return {
32598                     'font-size': height + 2,
32599                     lineString: lineString(sub),
32600                     startOffset: offset + '%'
32601                 };
32602             }
32603         }
32604
32605         function getAreaLabel(entity, width, height) {
32606             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
32607                 extent = entity.extent(graph),
32608                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
32609                 rect;
32610
32611             if (isNaN(centroid[0]) || entitywidth < 20) return;
32612
32613             var iconX = centroid[0] - (iconSize/2),
32614                 iconY = centroid[1] - (iconSize/2),
32615                 textOffset = iconSize + 5;
32616
32617             var p = {
32618                 transform: 'translate(' + iconX + ',' + iconY + ')'
32619             };
32620
32621             if (width && entitywidth >= width + 20) {
32622                 p.x = centroid[0];
32623                 p.y = centroid[1] + textOffset;
32624                 p.textAnchor = 'middle';
32625                 p.height = height;
32626                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
32627             } else {
32628                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
32629             }
32630
32631             if (tryInsert(rect, entity.id)) return p;
32632
32633         }
32634
32635         function tryInsert(rect, id) {
32636             // Check that label is visible
32637             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
32638                 rect[3] > dimensions[1]) return false;
32639             var v = rtree.search(rect).length === 0;
32640             if (v) {
32641                 rect.id = id;
32642                 rtree.insert(rect);
32643                 rectangles[id] = rect;
32644             }
32645             return v;
32646         }
32647
32648         var label = surface.selectAll('.layer-label'),
32649             halo = surface.selectAll('.layer-halo');
32650
32651         // points
32652         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
32653         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
32654
32655         // lines
32656         drawLinePaths(halo, labelled.line, filter, '', positions.line);
32657         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
32658         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
32659
32660         // areas
32661         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
32662         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
32663         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
32664
32665         // debug
32666         var showDebug = context.getDebug('collision');
32667         var debug = label.selectAll('.layer-label-debug')
32668             .data(showDebug ? [true] : []);
32669
32670         debug.enter()
32671             .append('g')
32672             .attr('class', 'layer-label-debug');
32673
32674         debug.exit()
32675             .remove();
32676
32677         if (showDebug) {
32678             var gj = rtree.all().map(function(d) {
32679                 return { type: 'Polygon', coordinates: [[
32680                     [d[0], d[1]],
32681                     [d[2], d[1]],
32682                     [d[2], d[3]],
32683                     [d[0], d[3]],
32684                     [d[0], d[1]]
32685                 ]]};
32686             });
32687
32688             var debugboxes = debug.selectAll('.debug').data(gj);
32689
32690             debugboxes.enter()
32691                 .append('path')
32692                 .attr('class', 'debug yellow');
32693
32694             debugboxes.exit()
32695                 .remove();
32696
32697             debugboxes
32698                 .attr('d', d3.geo.path().projection(null));
32699         }
32700     }
32701
32702     drawLabels.supersurface = function(supersurface) {
32703         supersurface
32704             .on('mousemove.hidelabels', hideOnMouseover)
32705             .on('mousedown.hidelabels', function () {
32706                 supersurface.on('mousemove.hidelabels', null);
32707             })
32708             .on('mouseup.hidelabels', function () {
32709                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
32710             });
32711     };
32712
32713     return drawLabels;
32714 };
32715 iD.svg.Layers = function(projection, context) {
32716     var dispatch = d3.dispatch('change'),
32717         svg = d3.select(null),
32718         layers = [
32719             { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
32720             { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
32721             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
32722             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context, dispatch) },
32723             { id: 'debug', layer: iD.svg.Debug(projection, context, dispatch) }
32724         ];
32725
32726
32727     function drawLayers(selection) {
32728         svg = selection.selectAll('.surface')
32729             .data([0]);
32730
32731         svg.enter()
32732             .append('svg')
32733             .attr('class', 'surface')
32734             .append('defs');
32735
32736         var groups = svg.selectAll('.data-layer')
32737             .data(layers);
32738
32739         groups.enter()
32740             .append('g')
32741             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
32742
32743         groups
32744             .each(function(d) { d3.select(this).call(d.layer); });
32745
32746         groups.exit()
32747             .remove();
32748     }
32749
32750     drawLayers.all = function() {
32751         return layers;
32752     };
32753
32754     drawLayers.layer = function(id) {
32755         var obj = _.find(layers, function(o) {return o.id === id;});
32756         return obj && obj.layer;
32757     };
32758
32759     drawLayers.only = function(what) {
32760         var arr = [].concat(what);
32761         drawLayers.remove(_.difference(_.map(layers, 'id'), arr));
32762         return this;
32763     };
32764
32765     drawLayers.remove = function(what) {
32766         var arr = [].concat(what);
32767         arr.forEach(function(id) {
32768             layers = _.reject(layers, function(o) {return o.id === id;});
32769         });
32770         dispatch.change();
32771         return this;
32772     };
32773
32774     drawLayers.add = function(what) {
32775         var arr = [].concat(what);
32776         arr.forEach(function(obj) {
32777             if ('id' in obj && 'layer' in obj) {
32778                 layers.push(obj);
32779             }
32780         });
32781         dispatch.change();
32782         return this;
32783     };
32784
32785     drawLayers.dimensions = function(_) {
32786         if (!arguments.length) return svg.dimensions();
32787         svg.dimensions(_);
32788         layers.forEach(function(obj) {
32789             if (obj.layer.dimensions) {
32790                 obj.layer.dimensions(_);
32791             }
32792         });
32793         return this;
32794     };
32795
32796
32797     return d3.rebind(drawLayers, dispatch, 'on');
32798 };
32799 iD.svg.Lines = function(projection) {
32800
32801     var highway_stack = {
32802         motorway: 0,
32803         motorway_link: 1,
32804         trunk: 2,
32805         trunk_link: 3,
32806         primary: 4,
32807         primary_link: 5,
32808         secondary: 6,
32809         tertiary: 7,
32810         unclassified: 8,
32811         residential: 9,
32812         service: 10,
32813         footway: 11
32814     };
32815
32816     function waystack(a, b) {
32817         var as = 0, bs = 0;
32818
32819         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
32820         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
32821         return as - bs;
32822     }
32823
32824     return function drawLines(surface, graph, entities, filter) {
32825         var ways = [], pathdata = {}, onewaydata = {},
32826             getPath = iD.svg.Path(projection, graph);
32827
32828         for (var i = 0; i < entities.length; i++) {
32829             var entity = entities[i],
32830                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
32831             if (outer) {
32832                 ways.push(entity.mergeTags(outer.tags));
32833             } else if (entity.geometry(graph) === 'line') {
32834                 ways.push(entity);
32835             }
32836         }
32837
32838         ways = ways.filter(getPath);
32839
32840         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
32841
32842         _.forOwn(pathdata, function(v, k) {
32843             onewaydata[k] = _(v)
32844                 .filter(function(d) { return d.isOneWay(); })
32845                 .map(iD.svg.OneWaySegments(projection, graph, 35))
32846                 .flatten()
32847                 .valueOf();
32848         });
32849
32850         var layergroup = surface
32851             .selectAll('.layer-lines')
32852             .selectAll('g.layergroup')
32853             .data(d3.range(-10, 11));
32854
32855         layergroup.enter()
32856             .append('g')
32857             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
32858
32859
32860         var linegroup = layergroup
32861             .selectAll('g.linegroup')
32862             .data(['shadow', 'casing', 'stroke']);
32863
32864         linegroup.enter()
32865             .append('g')
32866             .attr('class', function(d) { return 'layer linegroup line-' + d; });
32867
32868
32869         var lines = linegroup
32870             .selectAll('path')
32871             .filter(filter)
32872             .data(
32873                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
32874                 iD.Entity.key
32875             );
32876
32877         // Optimization: call simple TagClasses only on enter selection. This
32878         // works because iD.Entity.key is defined to include the entity v attribute.
32879         lines.enter()
32880             .append('path')
32881             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
32882             .call(iD.svg.TagClasses());
32883
32884         lines
32885             .sort(waystack)
32886             .attr('d', getPath)
32887             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
32888
32889         lines.exit()
32890             .remove();
32891
32892
32893         var onewaygroup = layergroup
32894             .selectAll('g.onewaygroup')
32895             .data(['oneway']);
32896
32897         onewaygroup.enter()
32898             .append('g')
32899             .attr('class', 'layer onewaygroup');
32900
32901
32902         var oneways = onewaygroup
32903             .selectAll('path')
32904             .filter(filter)
32905             .data(
32906                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
32907                 function(d) { return [d.id, d.index]; }
32908             );
32909
32910         oneways.enter()
32911             .append('path')
32912             .attr('class', 'oneway')
32913             .attr('marker-mid', 'url(#oneway-marker)');
32914
32915         oneways
32916             .attr('d', function(d) { return d.d; });
32917
32918         if (iD.detect().ie) {
32919             oneways.each(function() { this.parentNode.insertBefore(this, this); });
32920         }
32921
32922         oneways.exit()
32923             .remove();
32924
32925     };
32926 };
32927 iD.svg.MapillaryImages = function(projection, context, dispatch) {
32928     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
32929         minZoom = 12,
32930         layer = d3.select(null),
32931         _mapillary;
32932
32933
32934     function init() {
32935         if (iD.svg.MapillaryImages.initialized) return;  // run once
32936         iD.svg.MapillaryImages.enabled = false;
32937         iD.svg.MapillaryImages.initialized = true;
32938     }
32939
32940     function getMapillary() {
32941         if (iD.services.mapillary && !_mapillary) {
32942             _mapillary = iD.services.mapillary();
32943             _mapillary.on('loadedImages', debouncedRedraw);
32944         } else if (!iD.services.mapillary && _mapillary) {
32945             _mapillary = null;
32946         }
32947
32948         return _mapillary;
32949     }
32950
32951     function showLayer() {
32952         var mapillary = getMapillary();
32953         if (!mapillary) return;
32954
32955         mapillary.loadViewer();
32956         editOn();
32957
32958         layer
32959             .style('opacity', 0)
32960             .transition()
32961             .duration(500)
32962             .style('opacity', 1)
32963             .each('end', debouncedRedraw);
32964     }
32965
32966     function hideLayer() {
32967         var mapillary = getMapillary();
32968         if (mapillary) {
32969             mapillary.hideViewer();
32970         }
32971
32972         debouncedRedraw.cancel();
32973
32974         layer
32975             .transition()
32976             .duration(500)
32977             .style('opacity', 0)
32978             .each('end', editOff);
32979     }
32980
32981     function editOn() {
32982         layer.style('display', 'block');
32983     }
32984
32985     function editOff() {
32986         layer.selectAll('.viewfield-group').remove();
32987         layer.style('display', 'none');
32988     }
32989
32990     function click(d) {
32991         var mapillary = getMapillary();
32992         if (!mapillary) return;
32993
32994         context.map().centerEase(d.loc);
32995
32996         mapillary
32997             .setSelectedImage(d.key, true)
32998             .updateViewer(d.key, context)
32999             .showViewer();
33000     }
33001
33002     function transform(d) {
33003         var t = iD.svg.PointTransform(projection)(d);
33004         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
33005         return t;
33006     }
33007
33008     function update() {
33009         var mapillary = getMapillary(),
33010             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []),
33011             imageKey = mapillary ? mapillary.getSelectedImage() : null;
33012
33013         var markers = layer.selectAll('.viewfield-group')
33014             .data(data, function(d) { return d.key; });
33015
33016         // Enter
33017         var enter = markers.enter()
33018             .append('g')
33019             .attr('class', 'viewfield-group')
33020             .classed('selected', function(d) { return d.key === imageKey; })
33021             .on('click', click);
33022
33023         enter.append('path')
33024             .attr('class', 'viewfield')
33025             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
33026             .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');
33027
33028         enter.append('circle')
33029             .attr('dx', '0')
33030             .attr('dy', '0')
33031             .attr('r', '6');
33032
33033         // Exit
33034         markers.exit()
33035             .remove();
33036
33037         // Update
33038         markers
33039             .attr('transform', transform);
33040     }
33041
33042     function drawImages(selection) {
33043         var enabled = iD.svg.MapillaryImages.enabled,
33044             mapillary = getMapillary();
33045
33046         layer = selection.selectAll('.layer-mapillary-images')
33047             .data(mapillary ? [0] : []);
33048
33049         layer.enter()
33050             .append('g')
33051             .attr('class', 'layer-mapillary-images')
33052             .style('display', enabled ? 'block' : 'none');
33053
33054         layer.exit()
33055             .remove();
33056
33057         if (enabled) {
33058             if (mapillary && ~~context.map().zoom() >= minZoom) {
33059                 editOn();
33060                 update();
33061                 mapillary.loadImages(projection, layer.dimensions());
33062             } else {
33063                 editOff();
33064             }
33065         }
33066     }
33067
33068     drawImages.enabled = function(_) {
33069         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
33070         iD.svg.MapillaryImages.enabled = _;
33071         if (iD.svg.MapillaryImages.enabled) {
33072             showLayer();
33073         } else {
33074             hideLayer();
33075         }
33076         dispatch.change();
33077         return this;
33078     };
33079
33080     drawImages.supported = function() {
33081         return !!getMapillary();
33082     };
33083
33084     drawImages.dimensions = function(_) {
33085         if (!arguments.length) return layer.dimensions();
33086         layer.dimensions(_);
33087         return this;
33088     };
33089
33090     init();
33091     return drawImages;
33092 };
33093 iD.svg.MapillarySigns = function(projection, context, dispatch) {
33094     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
33095         minZoom = 12,
33096         layer = d3.select(null),
33097         _mapillary;
33098
33099
33100     function init() {
33101         if (iD.svg.MapillarySigns.initialized) return;  // run once
33102         iD.svg.MapillarySigns.enabled = false;
33103         iD.svg.MapillarySigns.initialized = true;
33104     }
33105
33106     function getMapillary() {
33107         if (iD.services.mapillary && !_mapillary) {
33108             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
33109         } else if (!iD.services.mapillary && _mapillary) {
33110             _mapillary = null;
33111         }
33112         return _mapillary;
33113     }
33114
33115     function showLayer() {
33116         editOn();
33117         debouncedRedraw();
33118     }
33119
33120     function hideLayer() {
33121         debouncedRedraw.cancel();
33122         editOff();
33123     }
33124
33125     function editOn() {
33126         layer.style('display', 'block');
33127     }
33128
33129     function editOff() {
33130         layer.selectAll('.icon-sign').remove();
33131         layer.style('display', 'none');
33132     }
33133
33134     function click(d) {
33135         var mapillary = getMapillary();
33136         if (!mapillary) return;
33137
33138         context.map().centerEase(d.loc);
33139
33140         mapillary
33141             .setSelectedImage(d.key, true)
33142             .updateViewer(d.key, context)
33143             .showViewer();
33144     }
33145
33146     function update() {
33147         var mapillary = getMapillary(),
33148             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []),
33149             imageKey = mapillary ? mapillary.getSelectedImage() : null;
33150
33151         var signs = layer.selectAll('.icon-sign')
33152             .data(data, function(d) { return d.key; });
33153
33154         // Enter
33155         var enter = signs.enter()
33156             .append('foreignObject')
33157             .attr('class', 'icon-sign')
33158             .attr('width', '32px')      // for Firefox
33159             .attr('height', '32px')     // for Firefox
33160             .classed('selected', function(d) { return d.key === imageKey; })
33161             .on('click', click);
33162
33163         enter
33164             .append('xhtml:body')
33165             .html(mapillary.signHTML);
33166
33167         // Exit
33168         signs.exit()
33169             .remove();
33170
33171         // Update
33172         signs
33173             .attr('transform', iD.svg.PointTransform(projection));
33174     }
33175
33176     function drawSigns(selection) {
33177         var enabled = iD.svg.MapillarySigns.enabled,
33178             mapillary = getMapillary();
33179
33180         layer = selection.selectAll('.layer-mapillary-signs')
33181             .data(mapillary ? [0] : []);
33182
33183         layer.enter()
33184             .append('g')
33185             .attr('class', 'layer-mapillary-signs')
33186             .style('display', enabled ? 'block' : 'none')
33187             .attr('transform', 'translate(-16, -16)');  // center signs on loc
33188
33189         layer.exit()
33190             .remove();
33191
33192         if (enabled) {
33193             if (mapillary && ~~context.map().zoom() >= minZoom) {
33194                 editOn();
33195                 update();
33196                 mapillary.loadSigns(context, projection, layer.dimensions());
33197             } else {
33198                 editOff();
33199             }
33200         }
33201     }
33202
33203     drawSigns.enabled = function(_) {
33204         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
33205         iD.svg.MapillarySigns.enabled = _;
33206         if (iD.svg.MapillarySigns.enabled) {
33207             showLayer();
33208         } else {
33209             hideLayer();
33210         }
33211         dispatch.change();
33212         return this;
33213     };
33214
33215     drawSigns.supported = function() {
33216         var mapillary = getMapillary();
33217         return (mapillary && mapillary.signsSupported());
33218     };
33219
33220     drawSigns.dimensions = function(_) {
33221         if (!arguments.length) return layer.dimensions();
33222         layer.dimensions(_);
33223         return this;
33224     };
33225
33226     init();
33227     return drawSigns;
33228 };
33229 iD.svg.Midpoints = function(projection, context) {
33230     return function drawMidpoints(surface, graph, entities, filter, extent) {
33231         var poly = extent.polygon(),
33232             midpoints = {};
33233
33234         for (var i = 0; i < entities.length; i++) {
33235             var entity = entities[i];
33236
33237             if (entity.type !== 'way')
33238                 continue;
33239             if (!filter(entity))
33240                 continue;
33241             if (context.selectedIDs().indexOf(entity.id) < 0)
33242                 continue;
33243
33244             var nodes = graph.childNodes(entity);
33245             for (var j = 0; j < nodes.length - 1; j++) {
33246
33247                 var a = nodes[j],
33248                     b = nodes[j + 1],
33249                     id = [a.id, b.id].sort().join('-');
33250
33251                 if (midpoints[id]) {
33252                     midpoints[id].parents.push(entity);
33253                 } else {
33254                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
33255                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
33256                             loc = null;
33257
33258                         if (extent.intersects(point)) {
33259                             loc = point;
33260                         } else {
33261                             for (var k = 0; k < 4; k++) {
33262                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
33263                                 if (point &&
33264                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
33265                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
33266                                 {
33267                                     loc = point;
33268                                     break;
33269                                 }
33270                             }
33271                         }
33272
33273                         if (loc) {
33274                             midpoints[id] = {
33275                                 type: 'midpoint',
33276                                 id: id,
33277                                 loc: loc,
33278                                 edge: [a.id, b.id],
33279                                 parents: [entity]
33280                             };
33281                         }
33282                     }
33283                 }
33284             }
33285         }
33286
33287         function midpointFilter(d) {
33288             if (midpoints[d.id])
33289                 return true;
33290
33291             for (var i = 0; i < d.parents.length; i++)
33292                 if (filter(d.parents[i]))
33293                     return true;
33294
33295             return false;
33296         }
33297
33298         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
33299             .filter(midpointFilter)
33300             .data(_.values(midpoints), function(d) { return d.id; });
33301
33302         var enter = groups.enter()
33303             .insert('g', ':first-child')
33304             .attr('class', 'midpoint');
33305
33306         enter.append('polygon')
33307             .attr('points', '-6,8 10,0 -6,-8')
33308             .attr('class', 'shadow');
33309
33310         enter.append('polygon')
33311             .attr('points', '-3,4 5,0 -3,-4')
33312             .attr('class', 'fill');
33313
33314         groups
33315             .attr('transform', function(d) {
33316                 var translate = iD.svg.PointTransform(projection),
33317                     a = context.entity(d.edge[0]),
33318                     b = context.entity(d.edge[1]),
33319                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
33320                 return translate(d) + ' rotate(' + angle + ')';
33321             })
33322             .call(iD.svg.TagClasses().tags(
33323                 function(d) { return d.parents[0].tags; }
33324             ));
33325
33326         // Propagate data bindings.
33327         groups.select('polygon.shadow');
33328         groups.select('polygon.fill');
33329
33330         groups.exit()
33331             .remove();
33332     };
33333 };
33334 iD.svg.Osm = function() {
33335     return function drawOsm(selection) {
33336         var layers = selection.selectAll('.layer-osm')
33337             .data(['areas', 'lines', 'hit', 'halo', 'label']);
33338
33339         layers.enter().append('g')
33340             .attr('class', function(d) { return 'layer-osm layer-' + d; });
33341     };
33342 };
33343 iD.svg.Points = function(projection, context) {
33344     function markerPath(selection, klass) {
33345         selection
33346             .attr('class', klass)
33347             .attr('transform', 'translate(-8, -23)')
33348             .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');
33349     }
33350
33351     function sortY(a, b) {
33352         return b.loc[1] - a.loc[1];
33353     }
33354
33355     return function drawPoints(surface, graph, entities, filter) {
33356         var wireframe = surface.classed('fill-wireframe'),
33357             points = wireframe ? [] : _.filter(entities, function(e) {
33358                 return e.geometry(graph) === 'point';
33359             });
33360
33361         points.sort(sortY);
33362
33363         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
33364             .filter(filter)
33365             .data(points, iD.Entity.key);
33366
33367         var group = groups.enter()
33368             .append('g')
33369             .attr('class', function(d) { return 'node point ' + d.id; })
33370             .order();
33371
33372         group.append('path')
33373             .call(markerPath, 'shadow');
33374
33375         group.append('path')
33376             .call(markerPath, 'stroke');
33377
33378         group.append('use')
33379             .attr('transform', 'translate(-6, -20)')
33380             .attr('class', 'icon')
33381             .attr('width', '12px')
33382             .attr('height', '12px');
33383
33384         groups.attr('transform', iD.svg.PointTransform(projection))
33385             .call(iD.svg.TagClasses());
33386
33387         // Selecting the following implicitly
33388         // sets the data (point entity) on the element
33389         groups.select('.shadow');
33390         groups.select('.stroke');
33391         groups.select('.icon')
33392             .attr('xlink:href', function(entity) {
33393                 var preset = context.presets().match(entity, graph);
33394                 return preset.icon ? '#' + preset.icon + '-12' : '';
33395             });
33396
33397         groups.exit()
33398             .remove();
33399     };
33400 };
33401 iD.svg.TagClasses = function() {
33402     var primaries = [
33403             'building', 'highway', 'railway', 'waterway', 'aeroway',
33404             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
33405             'leisure', 'place'
33406         ],
33407         statuses = [
33408             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
33409             'razed', 'demolished', 'obliterated'
33410         ],
33411         secondaries = [
33412             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
33413             'surface', 'tracktype', 'crossing'
33414         ],
33415         tagClassRe = /^tag-/,
33416         tags = function(entity) { return entity.tags; };
33417
33418
33419     var tagClasses = function(selection) {
33420         selection.each(function tagClassesEach(entity) {
33421             var value = this.className,
33422                 classes, primary, status;
33423
33424             if (value.baseVal !== undefined) value = value.baseVal;
33425
33426             classes = value.trim().split(/\s+/).filter(function(name) {
33427                 return name.length && !tagClassRe.test(name);
33428             }).join(' ');
33429
33430             var t = tags(entity), i, k, v;
33431
33432             // pick at most one primary classification tag..
33433             for (i = 0; i < primaries.length; i++) {
33434                 k = primaries[i];
33435                 v = t[k];
33436                 if (!v || v === 'no') continue;
33437
33438                 primary = k;
33439                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
33440                     status = v;
33441                     classes += ' tag-' + k;
33442                 } else {
33443                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
33444                 }
33445
33446                 break;
33447             }
33448
33449             // add at most one status tag, only if relates to primary tag..
33450             if (!status) {
33451                 for (i = 0; i < statuses.length; i++) {
33452                     k = statuses[i];
33453                     v = t[k];
33454                     if (!v || v === 'no') continue;
33455
33456                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
33457                         status = k;
33458                     }
33459                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
33460                         status = k;
33461                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
33462                         status = k;
33463                         primary = v;
33464                         classes += ' tag-' + v;
33465                     }  // else ignore e.g.  `highway=path + abandoned=railway`
33466
33467                     if (status) break;
33468                 }
33469             }
33470
33471             if (status) {
33472                 classes += ' tag-status tag-status-' + status;
33473             }
33474
33475             // add any secondary (structure) tags
33476             for (i = 0; i < secondaries.length; i++) {
33477                 k = secondaries[i];
33478                 v = t[k];
33479                 if (!v || v === 'no') continue;
33480                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
33481             }
33482
33483             // For highways, look for surface tagging..
33484             if (primary === 'highway') {
33485                 var paved = (t.highway !== 'track');
33486                 for (k in t) {
33487                     v = t[k];
33488                     if (k in iD.pavedTags) {
33489                         paved = !!iD.pavedTags[k][v];
33490                         break;
33491                     }
33492                 }
33493                 if (!paved) {
33494                     classes += ' tag-unpaved';
33495                 }
33496             }
33497
33498             classes = classes.trim();
33499
33500             if (classes !== value) {
33501                 d3.select(this).attr('class', classes);
33502             }
33503         });
33504     };
33505
33506     tagClasses.tags = function(_) {
33507         if (!arguments.length) return tags;
33508         tags = _;
33509         return tagClasses;
33510     };
33511
33512     return tagClasses;
33513 };
33514 iD.svg.Turns = function(projection) {
33515     return function drawTurns(surface, graph, turns) {
33516         function key(turn) {
33517             return [turn.from.node + turn.via.node + turn.to.node].join('-');
33518         }
33519
33520         function icon(turn) {
33521             var u = turn.u ? '-u' : '';
33522             if (!turn.restriction)
33523                 return '#turn-yes' + u;
33524             var restriction = graph.entity(turn.restriction).tags.restriction;
33525             return '#turn-' +
33526                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
33527         }
33528
33529         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
33530             .data(turns, key);
33531
33532         // Enter
33533         var enter = groups.enter().append('g')
33534             .attr('class', 'turn');
33535
33536         var nEnter = enter.filter(function (turn) { return !turn.u; });
33537
33538         nEnter.append('rect')
33539             .attr('transform', 'translate(-22, -12)')
33540             .attr('width', '44')
33541             .attr('height', '24');
33542
33543         nEnter.append('use')
33544             .attr('transform', 'translate(-22, -12)')
33545             .attr('width', '44')
33546             .attr('height', '24');
33547
33548
33549         var uEnter = enter.filter(function (turn) { return turn.u; });
33550
33551         uEnter.append('circle')
33552             .attr('r', '16');
33553
33554         uEnter.append('use')
33555             .attr('transform', 'translate(-16, -16)')
33556             .attr('width', '32')
33557             .attr('height', '32');
33558
33559
33560         // Update
33561         groups
33562             .attr('transform', function (turn) {
33563                 var v = graph.entity(turn.via.node),
33564                     t = graph.entity(turn.to.node),
33565                     a = iD.geo.angle(v, t, projection),
33566                     p = projection(v.loc),
33567                     r = turn.u ? 0 : 60;
33568
33569                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
33570                     'rotate(' + a * 180 / Math.PI + ')';
33571             });
33572
33573         groups.select('use')
33574             .attr('xlink:href', icon);
33575
33576         groups.select('rect');
33577         groups.select('circle');
33578
33579
33580         // Exit
33581         groups.exit()
33582             .remove();
33583
33584         return this;
33585     };
33586 };
33587 iD.svg.Vertices = function(projection, context) {
33588     var radiuses = {
33589         //       z16-, z17, z18+, tagged
33590         shadow: [6,    7.5,   7.5,  11.5],
33591         stroke: [2.5,  3.5,   3.5,  7],
33592         fill:   [1,    1.5,   1.5,  1.5]
33593     };
33594
33595     var hover;
33596
33597     function siblingAndChildVertices(ids, graph, extent) {
33598         var vertices = {};
33599
33600         function addChildVertices(entity) {
33601             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
33602                 var i;
33603                 if (entity.type === 'way') {
33604                     for (i = 0; i < entity.nodes.length; i++) {
33605                         addChildVertices(graph.entity(entity.nodes[i]));
33606                     }
33607                 } else if (entity.type === 'relation') {
33608                     for (i = 0; i < entity.members.length; i++) {
33609                         var member = context.hasEntity(entity.members[i].id);
33610                         if (member) {
33611                             addChildVertices(member);
33612                         }
33613                     }
33614                 } else if (entity.intersects(extent, graph)) {
33615                     vertices[entity.id] = entity;
33616                 }
33617             }
33618         }
33619
33620         ids.forEach(function(id) {
33621             var entity = context.hasEntity(id);
33622             if (entity && entity.type === 'node') {
33623                 vertices[entity.id] = entity;
33624                 context.graph().parentWays(entity).forEach(function(entity) {
33625                     addChildVertices(entity);
33626                 });
33627             } else if (entity) {
33628                 addChildVertices(entity);
33629             }
33630         });
33631
33632         return vertices;
33633     }
33634
33635     function draw(selection, vertices, klass, graph, zoom) {
33636         var icons = {},
33637             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
33638
33639         var groups = selection
33640             .data(vertices, iD.Entity.key);
33641
33642         function icon(entity) {
33643             if (entity.id in icons) return icons[entity.id];
33644             icons[entity.id] =
33645                 entity.hasInterestingTags() &&
33646                 context.presets().match(entity, graph).icon;
33647             return icons[entity.id];
33648         }
33649
33650         function setClass(klass) {
33651             return function(entity) {
33652                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
33653             };
33654         }
33655
33656         function setAttributes(selection) {
33657             ['shadow','stroke','fill'].forEach(function(klass) {
33658                 var rads = radiuses[klass];
33659                 selection.selectAll('.' + klass)
33660                     .each(function(entity) {
33661                         var i = z && icon(entity),
33662                             c = i ? 0.5 : 0,
33663                             r = rads[i ? 3 : z];
33664                         this.setAttribute('cx', c);
33665                         this.setAttribute('cy', -c);
33666                         this.setAttribute('r', r);
33667                         if (i && klass === 'fill') {
33668                             this.setAttribute('visibility', 'hidden');
33669                         } else {
33670                             this.removeAttribute('visibility');
33671                         }
33672                     });
33673             });
33674
33675             selection.selectAll('use')
33676                 .each(function() {
33677                     if (z) {
33678                         this.removeAttribute('visibility');
33679                     } else {
33680                         this.setAttribute('visibility', 'hidden');
33681                     }
33682                 });
33683         }
33684
33685         var enter = groups.enter()
33686             .append('g')
33687             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
33688
33689         enter.append('circle')
33690             .each(setClass('shadow'));
33691
33692         enter.append('circle')
33693             .each(setClass('stroke'));
33694
33695         // Vertices with icons get a `use`.
33696         enter.filter(function(d) { return icon(d); })
33697             .append('use')
33698             .attr('transform', 'translate(-6, -6)')
33699             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
33700             .attr('width', '12px')
33701             .attr('height', '12px')
33702             .each(setClass('icon'));
33703
33704         // Vertices with tags get a fill.
33705         enter.filter(function(d) { return d.hasInterestingTags(); })
33706             .append('circle')
33707             .each(setClass('fill'));
33708
33709         groups
33710             .attr('transform', iD.svg.PointTransform(projection))
33711             .classed('shared', function(entity) { return graph.isShared(entity); })
33712             .call(setAttributes);
33713
33714         groups.exit()
33715             .remove();
33716     }
33717
33718     function drawVertices(surface, graph, entities, filter, extent, zoom) {
33719         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
33720             wireframe = surface.classed('fill-wireframe'),
33721             vertices = [];
33722
33723         for (var i = 0; i < entities.length; i++) {
33724             var entity = entities[i],
33725                 geometry = entity.geometry(graph);
33726
33727             if (wireframe && geometry === 'point') {
33728                 vertices.push(entity);
33729                 continue;
33730             }
33731
33732             if (geometry !== 'vertex')
33733                 continue;
33734
33735             if (entity.id in selected ||
33736                 entity.hasInterestingTags() ||
33737                 entity.isIntersection(graph)) {
33738                 vertices.push(entity);
33739             }
33740         }
33741
33742         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
33743             .filter(filter)
33744             .call(draw, vertices, 'vertex-persistent', graph, zoom);
33745
33746         drawHover(surface, graph, extent, zoom);
33747     }
33748
33749     function drawHover(surface, graph, extent, zoom) {
33750         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
33751
33752         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
33753             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
33754     }
33755
33756     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
33757         if (target === hover) return;
33758         hover = target;
33759         drawHover(surface, graph, extent, zoom);
33760     };
33761
33762     return drawVertices;
33763 };
33764 iD.ui = function(context) {
33765     function render(container) {
33766         var map = context.map();
33767
33768         if (iD.detect().opera) container.classed('opera', true);
33769
33770         var hash = iD.behavior.Hash(context);
33771
33772         hash();
33773
33774         if (!hash.hadHash) {
33775             map.centerZoom([0, 0], 2);
33776         }
33777
33778         container.append('svg')
33779             .attr('id', 'defs')
33780             .call(iD.svg.Defs(context));
33781
33782         container.append('div')
33783             .attr('id', 'sidebar')
33784             .attr('class', 'col4')
33785             .call(ui.sidebar);
33786
33787         var content = container.append('div')
33788             .attr('id', 'content');
33789
33790         var bar = content.append('div')
33791             .attr('id', 'bar')
33792             .attr('class', 'fillD');
33793
33794         content.append('div')
33795             .attr('id', 'map')
33796             .call(map);
33797
33798         content
33799             .call(iD.ui.MapInMap(context));
33800
33801         content.append('div')
33802             .call(iD.ui.Info(context));
33803
33804         bar.append('div')
33805             .attr('class', 'spacer col4');
33806
33807         var limiter = bar.append('div')
33808             .attr('class', 'limiter');
33809
33810         limiter.append('div')
33811             .attr('class', 'button-wrap joined col3')
33812             .call(iD.ui.Modes(context), limiter);
33813
33814         limiter.append('div')
33815             .attr('class', 'button-wrap joined col1')
33816             .call(iD.ui.UndoRedo(context));
33817
33818         limiter.append('div')
33819             .attr('class', 'button-wrap col1')
33820             .call(iD.ui.Save(context));
33821
33822         bar.append('div')
33823             .attr('class', 'full-screen')
33824             .call(iD.ui.FullScreen(context));
33825
33826         bar.append('div')
33827             .attr('class', 'spinner')
33828             .call(iD.ui.Spinner(context));
33829
33830         var controls = bar.append('div')
33831             .attr('class', 'map-controls');
33832
33833         controls.append('div')
33834             .attr('class', 'map-control zoombuttons')
33835             .call(iD.ui.Zoom(context));
33836
33837         controls.append('div')
33838             .attr('class', 'map-control geolocate-control')
33839             .call(iD.ui.Geolocate(context));
33840
33841         controls.append('div')
33842             .attr('class', 'map-control background-control')
33843             .call(iD.ui.Background(context));
33844
33845         controls.append('div')
33846             .attr('class', 'map-control map-data-control')
33847             .call(iD.ui.MapData(context));
33848
33849         controls.append('div')
33850             .attr('class', 'map-control help-control')
33851             .call(iD.ui.Help(context));
33852
33853         var about = content.append('div')
33854             .attr('id', 'about');
33855
33856         about.append('div')
33857             .attr('id', 'attrib')
33858             .call(iD.ui.Attribution(context));
33859
33860         var footer = about.append('div')
33861             .attr('id', 'footer')
33862             .attr('class', 'fillD');
33863
33864         footer.append('div')
33865             .attr('class', 'api-status')
33866             .call(iD.ui.Status(context));
33867
33868         footer.append('div')
33869             .attr('id', 'scale-block')
33870             .call(iD.ui.Scale(context));
33871
33872         var aboutList = footer.append('div')
33873             .attr('id', 'info-block')
33874             .append('ul')
33875             .attr('id', 'about-list');
33876
33877         if (!context.embed()) {
33878             aboutList.call(iD.ui.Account(context));
33879         }
33880
33881         aboutList.append('li')
33882             .append('a')
33883             .attr('target', '_blank')
33884             .attr('tabindex', -1)
33885             .attr('href', 'https://github.com/openstreetmap/iD')
33886             .text(iD.version);
33887
33888         var issueLinks = aboutList.append('li');
33889
33890         issueLinks.append('a')
33891             .attr('target', '_blank')
33892             .attr('tabindex', -1)
33893             .attr('href', 'https://github.com/openstreetmap/iD/issues')
33894             .call(iD.svg.Icon('#icon-bug', 'light'))
33895             .call(bootstrap.tooltip()
33896                 .title(t('report_a_bug'))
33897                 .placement('top')
33898             );
33899
33900         issueLinks.append('a')
33901             .attr('target', '_blank')
33902             .attr('tabindex', -1)
33903             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
33904             .call(iD.svg.Icon('#icon-translate', 'light'))
33905             .call(bootstrap.tooltip()
33906                 .title(t('help_translate'))
33907                 .placement('top')
33908             );
33909
33910         aboutList.append('li')
33911             .attr('class', 'feature-warning')
33912             .attr('tabindex', -1)
33913             .call(iD.ui.FeatureInfo(context));
33914
33915         aboutList.append('li')
33916             .attr('class', 'user-list')
33917             .attr('tabindex', -1)
33918             .call(iD.ui.Contributors(context));
33919
33920         window.onbeforeunload = function() {
33921             return context.save();
33922         };
33923
33924         window.onunload = function() {
33925             context.history().unlock();
33926         };
33927
33928         var mapDimensions = map.dimensions();
33929
33930         d3.select(window).on('resize.editor', function() {
33931             mapDimensions = content.dimensions(null);
33932             map.dimensions(mapDimensions);
33933         });
33934
33935         function pan(d) {
33936             return function() {
33937                 d3.event.preventDefault();
33938                 if (!context.inIntro()) context.pan(d);
33939             };
33940         }
33941
33942         // pan amount
33943         var pa = 10;
33944
33945         var keybinding = d3.keybinding('main')
33946             .on('⌫', function() { d3.event.preventDefault(); })
33947             .on('←', pan([pa, 0]))
33948             .on('↑', pan([0, pa]))
33949             .on('→', pan([-pa, 0]))
33950             .on('↓', pan([0, -pa]))
33951             .on('⇧←', pan([mapDimensions[0], 0]))
33952             .on('⇧↑', pan([0, mapDimensions[1]]))
33953             .on('⇧→', pan([-mapDimensions[0], 0]))
33954             .on('⇧↓', pan([0, -mapDimensions[1]]))
33955             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
33956             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
33957             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
33958             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
33959
33960         d3.select(document)
33961             .call(keybinding);
33962
33963         context.enter(iD.modes.Browse(context));
33964
33965         context.container()
33966             .call(iD.ui.Splash(context))
33967             .call(iD.ui.Restore(context));
33968
33969         var authenticating = iD.ui.Loading(context)
33970             .message(t('loading_auth'));
33971
33972         context.connection()
33973             .on('authenticating.ui', function() {
33974                 context.container()
33975                     .call(authenticating);
33976             })
33977             .on('authenticated.ui', function() {
33978                 authenticating.close();
33979             });
33980     }
33981
33982     function ui(container) {
33983         context.container(container);
33984         context.loadLocale(function() {
33985             render(container);
33986         });
33987     }
33988
33989     ui.sidebar = iD.ui.Sidebar(context);
33990
33991     return ui;
33992 };
33993
33994 iD.ui.tooltipHtml = function(text, key) {
33995     var s = '<span>' + text + '</span>';
33996     if (key) {
33997         s += '<div class="keyhint-wrap">' +
33998             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
33999             '<span class="keyhint"> ' + key + '</span></div>';
34000     }
34001     return s;
34002 };
34003 iD.ui.Account = function(context) {
34004     var connection = context.connection();
34005
34006     function update(selection) {
34007         if (!connection.authenticated()) {
34008             selection.selectAll('#userLink, #logoutLink')
34009                 .classed('hide', true);
34010             return;
34011         }
34012
34013         connection.userDetails(function(err, details) {
34014             var userLink = selection.select('#userLink'),
34015                 logoutLink = selection.select('#logoutLink');
34016
34017             userLink.html('');
34018             logoutLink.html('');
34019
34020             if (err) return;
34021
34022             selection.selectAll('#userLink, #logoutLink')
34023                 .classed('hide', false);
34024
34025             // Link
34026             userLink.append('a')
34027                 .attr('href', connection.userURL(details.display_name))
34028                 .attr('target', '_blank');
34029
34030             // Add thumbnail or dont
34031             if (details.image_url) {
34032                 userLink.append('img')
34033                     .attr('class', 'icon pre-text user-icon')
34034                     .attr('src', details.image_url);
34035             } else {
34036                 userLink
34037                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
34038             }
34039
34040             // Add user name
34041             userLink.append('span')
34042                 .attr('class', 'label')
34043                 .text(details.display_name);
34044
34045             logoutLink.append('a')
34046                 .attr('class', 'logout')
34047                 .attr('href', '#')
34048                 .text(t('logout'))
34049                 .on('click.logout', function() {
34050                     d3.event.preventDefault();
34051                     connection.logout();
34052                 });
34053         });
34054     }
34055
34056     return function(selection) {
34057         selection.append('li')
34058             .attr('id', 'logoutLink')
34059             .classed('hide', true);
34060
34061         selection.append('li')
34062             .attr('id', 'userLink')
34063             .classed('hide', true);
34064
34065         connection.on('auth.account', function() { update(selection); });
34066         update(selection);
34067     };
34068 };
34069 iD.ui.Attribution = function(context) {
34070     var selection;
34071
34072     function attribution(data, klass) {
34073         var div = selection.selectAll('.' + klass)
34074             .data([0]);
34075
34076         div.enter()
34077             .append('div')
34078             .attr('class', klass);
34079
34080         var background = div.selectAll('.attribution')
34081             .data(data, function(d) { return d.name(); });
34082
34083         background.enter()
34084             .append('span')
34085             .attr('class', 'attribution')
34086             .each(function(d) {
34087                 if (d.terms_html) {
34088                     d3.select(this)
34089                         .html(d.terms_html);
34090                     return;
34091                 }
34092
34093                 var source = d.terms_text || d.id || d.name();
34094
34095                 if (d.logo) {
34096                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
34097                 }
34098
34099                 if (d.terms_url) {
34100                     d3.select(this)
34101                         .append('a')
34102                         .attr('href', d.terms_url)
34103                         .attr('target', '_blank')
34104                         .html(source);
34105                 } else {
34106                     d3.select(this)
34107                         .text(source);
34108                 }
34109             });
34110
34111         background.exit()
34112             .remove();
34113
34114         var copyright = background.selectAll('.copyright-notice')
34115             .data(function(d) {
34116                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
34117                 return notice ? [notice] : [];
34118             });
34119
34120         copyright.enter()
34121             .append('span')
34122             .attr('class', 'copyright-notice');
34123
34124         copyright.text(String);
34125
34126         copyright.exit()
34127             .remove();
34128     }
34129
34130     function update() {
34131         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
34132         attribution(context.background().overlayLayerSources().filter(function (s) {
34133             return s.validZoom(context.map().zoom());
34134         }), 'overlay-layer-attribution');
34135     }
34136
34137     return function(select) {
34138         selection = select;
34139
34140         context.background()
34141             .on('change.attribution', update);
34142
34143         context.map()
34144             .on('move.attribution', _.throttle(update, 400, {leading: false}));
34145
34146         update();
34147     };
34148 };
34149 iD.ui.Background = function(context) {
34150     var key = 'B',
34151         opacities = [1, 0.75, 0.5, 0.25],
34152         directions = [
34153             ['right', [0.5, 0]],
34154             ['top', [0, -0.5]],
34155             ['left', [-0.5, 0]],
34156             ['bottom', [0, 0.5]]],
34157         opacityDefault = (context.storage('background-opacity') !== null) ?
34158             (+context.storage('background-opacity')) : 1.0,
34159         customTemplate = context.storage('background-custom-template') || '',
34160         previous;
34161
34162     // Can be 0 from <1.3.0 use or due to issue #1923.
34163     if (opacityDefault === 0) opacityDefault = 1.0;
34164
34165
34166     function background(selection) {
34167
34168         function sortSources(a, b) {
34169             return a.best() && !b.best() ? -1
34170                 : b.best() && !a.best() ? 1
34171                 : d3.descending(a.area(), b.area()) || d3.ascending(a.name(), b.name()) || 0;
34172         }
34173
34174         function setOpacity(d) {
34175             var bg = context.container().selectAll('.layer-background')
34176                 .transition()
34177                 .style('opacity', d)
34178                 .attr('data-opacity', d);
34179
34180             if (!iD.detect().opera) {
34181                 iD.util.setTransform(bg, 0, 0);
34182             }
34183
34184             opacityList.selectAll('li')
34185                 .classed('active', function(_) { return _ === d; });
34186
34187             context.storage('background-opacity', d);
34188         }
34189
34190         function setTooltips(selection) {
34191             selection.each(function(d) {
34192                 var item = d3.select(this);
34193                 if (d === previous) {
34194                     item.call(bootstrap.tooltip()
34195                         .html(true)
34196                         .title(function() {
34197                             var tip = '<div>' + t('background.switch') + '</div>';
34198                             return iD.ui.tooltipHtml(tip, iD.ui.cmd('⌘B'));
34199                         })
34200                         .placement('top')
34201                     );
34202                 } else if (d.description) {
34203                     item.call(bootstrap.tooltip()
34204                         .title(d.description)
34205                         .placement('top')
34206                     );
34207                 } else {
34208                     item.call(bootstrap.tooltip().destroy);
34209                 }
34210             });
34211         }
34212
34213         function selectLayer() {
34214             function active(d) {
34215                 return context.background().showsLayer(d);
34216             }
34217
34218             content.selectAll('.layer, .custom_layer')
34219                 .classed('active', active)
34220                 .classed('switch', function(d) { return d === previous; })
34221                 .call(setTooltips)
34222                 .selectAll('input')
34223                 .property('checked', active);
34224         }
34225
34226         function clickSetSource(d) {
34227             previous = context.background().baseLayerSource();
34228             d3.event.preventDefault();
34229             context.background().baseLayerSource(d);
34230             selectLayer();
34231             document.activeElement.blur();
34232         }
34233
34234         function editCustom() {
34235             d3.event.preventDefault();
34236             var template = window.prompt(t('background.custom_prompt'), customTemplate);
34237             if (!template ||
34238                 template.indexOf('google.com') !== -1 ||
34239                 template.indexOf('googleapis.com') !== -1 ||
34240                 template.indexOf('google.ru') !== -1) {
34241                 selectLayer();
34242                 return;
34243             }
34244             setCustom(template);
34245         }
34246
34247         function setCustom(template) {
34248             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
34249             selectLayer();
34250             context.storage('background-custom-template', template);
34251         }
34252
34253         function clickSetOverlay(d) {
34254             d3.event.preventDefault();
34255             context.background().toggleOverlayLayer(d);
34256             selectLayer();
34257             document.activeElement.blur();
34258         }
34259
34260         function drawList(layerList, type, change, filter) {
34261             var sources = context.background()
34262                 .sources(context.map().extent())
34263                 .filter(filter);
34264
34265             var layerLinks = layerList.selectAll('li.layer')
34266                 .data(sources, function(d) { return d.name(); });
34267
34268             var enter = layerLinks.enter()
34269                 .insert('li', '.custom_layer')
34270                 .attr('class', 'layer')
34271                 .classed('best', function(d) { return d.best(); });
34272
34273             enter.filter(function(d) { return d.best(); })
34274                 .append('div')
34275                 .attr('class', 'best')
34276                 .call(bootstrap.tooltip()
34277                     .title(t('background.best_imagery'))
34278                     .placement('left'))
34279                 .append('span')
34280                 .html('&#9733;');
34281
34282             var label = enter.append('label');
34283
34284             label.append('input')
34285                 .attr('type', type)
34286                 .attr('name', 'layers')
34287                 .on('change', change);
34288
34289             label.append('span')
34290                 .text(function(d) { return d.name(); });
34291
34292
34293             layerLinks.exit()
34294                 .remove();
34295
34296             layerList.selectAll('li.layer')
34297                 .sort(sortSources)
34298                 .style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
34299         }
34300
34301         function update() {
34302             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
34303             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
34304
34305             selectLayer();
34306
34307             var source = context.background().baseLayerSource();
34308             if (source.id === 'custom') {
34309                 customTemplate = source.template;
34310             }
34311
34312             updateOffsetVal();
34313         }
34314
34315         function updateOffsetVal() {
34316             var meters = iD.geo.offsetToMeters(context.background().offset()),
34317                 x = +meters[0].toFixed(2),
34318                 y = +meters[1].toFixed(2);
34319
34320             d3.selectAll('.nudge-inner-rect')
34321                 .select('input')
34322                 .classed('error', false)
34323                 .property('value', x + ', ' + y);
34324
34325             d3.selectAll('.nudge-reset')
34326                 .classed('disabled', function() {
34327                     return (x === 0 && y === 0);
34328                 });
34329         }
34330
34331         function resetOffset() {
34332             context.background().offset([0, 0]);
34333             updateOffsetVal();
34334         }
34335
34336         function nudge(d) {
34337             context.background().nudge(d, context.map().zoom());
34338             updateOffsetVal();
34339         }
34340
34341         function buttonOffset(d) {
34342             var timeout = window.setTimeout(function() {
34343                     interval = window.setInterval(nudge.bind(null, d), 100);
34344                 }, 500),
34345                 interval;
34346
34347             d3.select(window).on('mouseup', function() {
34348                 window.clearInterval(interval);
34349                 window.clearTimeout(timeout);
34350                 d3.select(window).on('mouseup', null);
34351             });
34352
34353             nudge(d);
34354         }
34355
34356         function inputOffset() {
34357             var input = d3.select(this);
34358             var d = input.node().value;
34359
34360             if (d === '') return resetOffset();
34361
34362             d = d.replace(/;/g, ',').split(',').map(function(n) {
34363                 // if n is NaN, it will always get mapped to false.
34364                 return !isNaN(n) && n;
34365             });
34366
34367             if (d.length !== 2 || !d[0] || !d[1]) {
34368                 input.classed('error', true);
34369                 return;
34370             }
34371
34372             context.background().offset(iD.geo.metersToOffset(d));
34373             updateOffsetVal();
34374         }
34375
34376         function dragOffset() {
34377             var origin = [d3.event.clientX, d3.event.clientY];
34378
34379             context.container()
34380                 .append('div')
34381                 .attr('class', 'nudge-surface');
34382
34383             d3.select(window)
34384                 .on('mousemove.offset', function() {
34385                     var latest = [d3.event.clientX, d3.event.clientY];
34386                     var d = [
34387                         -(origin[0] - latest[0]) / 4,
34388                         -(origin[1] - latest[1]) / 4
34389                     ];
34390
34391                     origin = latest;
34392                     nudge(d);
34393                 })
34394                 .on('mouseup.offset', function() {
34395                     d3.selectAll('.nudge-surface')
34396                         .remove();
34397
34398                     d3.select(window)
34399                         .on('mousemove.offset', null)
34400                         .on('mouseup.offset', null);
34401                 });
34402
34403             d3.event.preventDefault();
34404         }
34405
34406         function hide() {
34407             setVisible(false);
34408         }
34409
34410         function toggle() {
34411             if (d3.event) d3.event.preventDefault();
34412             tooltip.hide(button);
34413             setVisible(!button.classed('active'));
34414         }
34415
34416         function quickSwitch() {
34417             if (previous) {
34418                 clickSetSource(previous);
34419             }
34420         }
34421
34422         function setVisible(show) {
34423             if (show !== shown) {
34424                 button.classed('active', show);
34425                 shown = show;
34426
34427                 if (show) {
34428                     selection.on('mousedown.background-inside', function() {
34429                         return d3.event.stopPropagation();
34430                     });
34431                     content.style('display', 'block')
34432                         .style('right', '-300px')
34433                         .transition()
34434                         .duration(200)
34435                         .style('right', '0px');
34436                 } else {
34437                     content.style('display', 'block')
34438                         .style('right', '0px')
34439                         .transition()
34440                         .duration(200)
34441                         .style('right', '-300px')
34442                         .each('end', function() {
34443                             d3.select(this).style('display', 'none');
34444                         });
34445                     selection.on('mousedown.background-inside', null);
34446                 }
34447             }
34448         }
34449
34450
34451         var content = selection.append('div')
34452                 .attr('class', 'fillL map-overlay col3 content hide'),
34453             tooltip = bootstrap.tooltip()
34454                 .placement('left')
34455                 .html(true)
34456                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
34457             button = selection.append('button')
34458                 .attr('tabindex', -1)
34459                 .on('click', toggle)
34460                 .call(iD.svg.Icon('#icon-layers', 'light'))
34461                 .call(tooltip),
34462             shown = false;
34463
34464
34465         /* opacity switcher */
34466
34467         var opa = content.append('div')
34468                 .attr('class', 'opacity-options-wrapper');
34469
34470         opa.append('h4')
34471             .text(t('background.title'));
34472
34473         var opacityList = opa.append('ul')
34474             .attr('class', 'opacity-options');
34475
34476         opacityList.selectAll('div.opacity')
34477             .data(opacities)
34478             .enter()
34479             .append('li')
34480             .attr('data-original-title', function(d) {
34481                 return t('background.percent_brightness', { opacity: (d * 100) });
34482             })
34483             .on('click.set-opacity', setOpacity)
34484             .html('<div class="select-box"></div>')
34485             .call(bootstrap.tooltip()
34486                 .placement('left'))
34487             .append('div')
34488             .attr('class', 'opacity')
34489             .style('opacity', function(d) { return 1.25 - d; });
34490
34491
34492         /* background switcher */
34493
34494         var backgroundList = content.append('ul')
34495             .attr('class', 'layer-list');
34496
34497         var custom = backgroundList.append('li')
34498             .attr('class', 'custom_layer')
34499             .datum(iD.BackgroundSource.Custom());
34500
34501         custom.append('button')
34502             .attr('class', 'layer-browse')
34503             .call(bootstrap.tooltip()
34504                 .title(t('background.custom_button'))
34505                 .placement('left'))
34506             .on('click', editCustom)
34507             .call(iD.svg.Icon('#icon-search'));
34508
34509         var label = custom.append('label');
34510
34511         label.append('input')
34512             .attr('type', 'radio')
34513             .attr('name', 'layers')
34514             .on('change', function () {
34515                 if (customTemplate) {
34516                     setCustom(customTemplate);
34517                 } else {
34518                     editCustom();
34519                 }
34520             });
34521
34522         label.append('span')
34523             .text(t('background.custom'));
34524
34525         content.append('div')
34526             .attr('class', 'imagery-faq')
34527             .append('a')
34528             .attr('target', '_blank')
34529             .attr('tabindex', -1)
34530             .call(iD.svg.Icon('#icon-out-link', 'inline'))
34531             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
34532             .append('span')
34533             .text(t('background.imagery_source_faq'));
34534
34535         var overlayList = content.append('ul')
34536             .attr('class', 'layer-list');
34537
34538         var controls = content.append('div')
34539             .attr('class', 'controls-list');
34540
34541
34542         /* minimap toggle */
34543
34544         var minimapLabel = controls
34545             .append('label')
34546             .call(bootstrap.tooltip()
34547                 .html(true)
34548                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
34549                 .placement('top')
34550             );
34551
34552         minimapLabel.classed('minimap-toggle', true)
34553             .append('input')
34554             .attr('type', 'checkbox')
34555             .on('change', function() {
34556                 iD.ui.MapInMap.toggle();
34557                 d3.event.preventDefault();
34558             });
34559
34560         minimapLabel.append('span')
34561             .text(t('background.minimap.description'));
34562
34563
34564         /* imagery offset controls */
34565
34566         var adjustments = content.append('div')
34567             .attr('class', 'adjustments');
34568
34569         adjustments.append('a')
34570             .text(t('background.fix_misalignment'))
34571             .attr('href', '#')
34572             .classed('hide-toggle', true)
34573             .classed('expanded', false)
34574             .on('click', function() {
34575                 var exp = d3.select(this).classed('expanded');
34576                 nudgeContainer.style('display', exp ? 'none' : 'block');
34577                 d3.select(this).classed('expanded', !exp);
34578                 d3.event.preventDefault();
34579             });
34580
34581         var nudgeContainer = adjustments.append('div')
34582             .attr('class', 'nudge-container cf')
34583             .style('display', 'none');
34584
34585         nudgeContainer.append('div')
34586             .attr('class', 'nudge-instructions')
34587             .text(t('background.offset'));
34588
34589         var nudgeRect = nudgeContainer.append('div')
34590             .attr('class', 'nudge-outer-rect')
34591             .on('mousedown', dragOffset);
34592
34593         nudgeRect.append('div')
34594             .attr('class', 'nudge-inner-rect')
34595             .append('input')
34596             .on('change', inputOffset)
34597             .on('mousedown', function() {
34598                 d3.event.stopPropagation();
34599             });
34600
34601         nudgeContainer.append('div')
34602             .selectAll('button')
34603             .data(directions).enter()
34604             .append('button')
34605             .attr('class', function(d) { return d[0] + ' nudge'; })
34606             .on('mousedown', function(d) {
34607                 buttonOffset(d[1]);
34608             });
34609
34610         nudgeContainer.append('button')
34611             .attr('title', t('background.reset'))
34612             .attr('class', 'nudge-reset disabled')
34613             .on('click', resetOffset)
34614             .call(iD.svg.Icon('#icon-undo'));
34615
34616         context.map()
34617             .on('move.background-update', _.debounce(update, 1000));
34618
34619         context.background()
34620             .on('change.background-update', update);
34621
34622
34623         update();
34624         setOpacity(opacityDefault);
34625
34626         var keybinding = d3.keybinding('background')
34627             .on(key, toggle)
34628             .on(iD.ui.cmd('⌘B'), quickSwitch)
34629             .on('F', hide)
34630             .on('H', hide);
34631
34632         d3.select(document)
34633             .call(keybinding);
34634
34635         context.surface().on('mousedown.background-outside', hide);
34636         context.container().on('mousedown.background-outside', hide);
34637     }
34638
34639     return background;
34640 };
34641 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
34642 // For example, ⌘Z -> Ctrl+Z
34643 iD.ui.cmd = function(code) {
34644     if (iD.detect().os === 'mac') {
34645         return code;
34646     }
34647
34648     if (iD.detect().os === 'win') {
34649         if (code === '⌘⇧Z') return 'Ctrl+Y';
34650     }
34651
34652     var result = '',
34653         replacements = {
34654             '⌘': 'Ctrl',
34655             '⇧': 'Shift',
34656             '⌥': 'Alt',
34657             '⌫': 'Backspace',
34658             '⌦': 'Delete'
34659         };
34660
34661     for (var i = 0; i < code.length; i++) {
34662         if (code[i] in replacements) {
34663             result += replacements[code[i]] + '+';
34664         } else {
34665             result += code[i];
34666         }
34667     }
34668
34669     return result;
34670 };
34671 iD.ui.Commit = function(context) {
34672     var dispatch = d3.dispatch('cancel', 'save');
34673
34674     function commit(selection) {
34675         var changes = context.history().changes(),
34676             summary = context.history().difference().summary();
34677
34678         function zoomToEntity(change) {
34679             var entity = change.entity;
34680             if (change.changeType !== 'deleted' &&
34681                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
34682                 context.map().zoomTo(entity);
34683                 context.surface().selectAll(
34684                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
34685                     .classed('hover', true);
34686             }
34687         }
34688
34689         var header = selection.append('div')
34690             .attr('class', 'header fillL');
34691
34692         header.append('h3')
34693             .text(t('commit.title'));
34694
34695         var body = selection.append('div')
34696             .attr('class', 'body');
34697
34698
34699         // Comment Section
34700         var commentSection = body.append('div')
34701             .attr('class', 'modal-section form-field commit-form');
34702
34703         commentSection.append('label')
34704             .attr('class', 'form-label')
34705             .text(t('commit.message_label'));
34706
34707         var commentField = commentSection.append('textarea')
34708             .attr('placeholder', t('commit.description_placeholder'))
34709             .attr('maxlength', 255)
34710             .property('value', context.storage('comment') || '')
34711             .on('input.save', checkComment)
34712             .on('change.save', checkComment)
34713             .on('blur.save', function() {
34714                 context.storage('comment', this.value);
34715             });
34716
34717         function checkComment() {
34718             d3.selectAll('.save-section .save-button')
34719                 .attr('disabled', (this.value.length ? null : true));
34720
34721             var googleWarning = clippyArea
34722                .html('')
34723                .selectAll('a')
34724                .data(this.value.match(/google/i) ? [true] : []);
34725
34726             googleWarning.exit().remove();
34727
34728             googleWarning.enter()
34729                .append('a')
34730                .attr('target', '_blank')
34731                .attr('tabindex', -1)
34732                .call(iD.svg.Icon('#icon-alert', 'inline'))
34733                .attr('href', t('commit.google_warning_link'))
34734                .append('span')
34735                .text(t('commit.google_warning'));
34736         }
34737
34738         commentField.node().select();
34739
34740         context.connection().userChangesets(function (err, changesets) {
34741             if (err) return;
34742
34743             var comments = [];
34744
34745             for (var i = 0; i < changesets.length; i++) {
34746                 if (changesets[i].tags.comment) {
34747                     comments.push({
34748                         title: changesets[i].tags.comment,
34749                         value: changesets[i].tags.comment
34750                     });
34751                 }
34752             }
34753
34754             commentField.call(d3.combobox().caseSensitive(true).data(comments));
34755         });
34756
34757         var clippyArea = commentSection.append('div')
34758             .attr('class', 'clippy-area');
34759
34760
34761         var changeSetInfo = commentSection.append('div')
34762             .attr('class', 'changeset-info');
34763
34764         changeSetInfo.append('a')
34765             .attr('target', '_blank')
34766             .attr('tabindex', -1)
34767             .call(iD.svg.Icon('#icon-out-link', 'inline'))
34768             .attr('href', t('commit.about_changeset_comments_link'))
34769             .append('span')
34770             .text(t('commit.about_changeset_comments'));
34771
34772         // Warnings
34773         var warnings = body.selectAll('div.warning-section')
34774             .data([context.history().validate(changes)])
34775             .enter()
34776             .append('div')
34777             .attr('class', 'modal-section warning-section fillL2')
34778             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
34779             .style('background', '#ffb');
34780
34781         warnings.append('h3')
34782             .text(t('commit.warnings'));
34783
34784         var warningLi = warnings.append('ul')
34785             .attr('class', 'changeset-list')
34786             .selectAll('li')
34787             .data(function(d) { return d; })
34788             .enter()
34789             .append('li')
34790             .style()
34791             .on('mouseover', mouseover)
34792             .on('mouseout', mouseout)
34793             .on('click', warningClick);
34794
34795         warningLi
34796             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
34797
34798         warningLi
34799             .append('strong').text(function(d) {
34800                 return d.message;
34801             });
34802
34803         warningLi.filter(function(d) { return d.tooltip; })
34804             .call(bootstrap.tooltip()
34805                 .title(function(d) { return d.tooltip; })
34806                 .placement('top')
34807             );
34808
34809
34810         // Upload Explanation
34811         var saveSection = body.append('div')
34812             .attr('class','modal-section save-section fillL cf');
34813
34814         var prose = saveSection.append('p')
34815             .attr('class', 'commit-info')
34816             .html(t('commit.upload_explanation'));
34817
34818         context.connection().userDetails(function(err, user) {
34819             if (err) return;
34820
34821             var userLink = d3.select(document.createElement('div'));
34822
34823             if (user.image_url) {
34824                 userLink.append('img')
34825                     .attr('src', user.image_url)
34826                     .attr('class', 'icon pre-text user-icon');
34827             }
34828
34829             userLink.append('a')
34830                 .attr('class','user-info')
34831                 .text(user.display_name)
34832                 .attr('href', context.connection().userURL(user.display_name))
34833                 .attr('tabindex', -1)
34834                 .attr('target', '_blank');
34835
34836             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
34837         });
34838
34839
34840         // Buttons
34841         var buttonSection = saveSection.append('div')
34842             .attr('class','buttons fillL cf');
34843
34844         var cancelButton = buttonSection.append('button')
34845             .attr('class', 'secondary-action col5 button cancel-button')
34846             .on('click.cancel', function() { dispatch.cancel(); });
34847
34848         cancelButton.append('span')
34849             .attr('class', 'label')
34850             .text(t('commit.cancel'));
34851
34852         var saveButton = buttonSection.append('button')
34853             .attr('class', 'action col5 button save-button')
34854             .attr('disabled', function() {
34855                 var n = d3.select('.commit-form textarea').node();
34856                 return (n && n.value.length) ? null : true;
34857             })
34858             .on('click.save', function() {
34859                 dispatch.save({
34860                     comment: commentField.node().value
34861                 });
34862             });
34863
34864         saveButton.append('span')
34865             .attr('class', 'label')
34866             .text(t('commit.save'));
34867
34868
34869         // Changes
34870         var changeSection = body.selectAll('div.commit-section')
34871             .data([0])
34872             .enter()
34873             .append('div')
34874             .attr('class', 'commit-section modal-section fillL2');
34875
34876         changeSection.append('h3')
34877             .text(t('commit.changes', {count: summary.length}));
34878
34879         var li = changeSection.append('ul')
34880             .attr('class', 'changeset-list')
34881             .selectAll('li')
34882             .data(summary)
34883             .enter()
34884             .append('li')
34885             .on('mouseover', mouseover)
34886             .on('mouseout', mouseout)
34887             .on('click', zoomToEntity);
34888
34889         li.each(function(d) {
34890             d3.select(this)
34891                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
34892         });
34893
34894         li.append('span')
34895             .attr('class', 'change-type')
34896             .text(function(d) {
34897                 return t('commit.' + d.changeType) + ' ';
34898             });
34899
34900         li.append('strong')
34901             .attr('class', 'entity-type')
34902             .text(function(d) {
34903                 return context.presets().match(d.entity, d.graph).name();
34904             });
34905
34906         li.append('span')
34907             .attr('class', 'entity-name')
34908             .text(function(d) {
34909                 var name = iD.util.displayName(d.entity) || '',
34910                     string = '';
34911                 if (name !== '') string += ':';
34912                 return string += ' ' + name;
34913             });
34914
34915         li.style('opacity', 0)
34916             .transition()
34917             .style('opacity', 1);
34918
34919
34920         function mouseover(d) {
34921             if (d.entity) {
34922                 context.surface().selectAll(
34923                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
34924                 ).classed('hover', true);
34925             }
34926         }
34927
34928         function mouseout() {
34929             context.surface().selectAll('.hover')
34930                 .classed('hover', false);
34931         }
34932
34933         function warningClick(d) {
34934             if (d.entity) {
34935                 context.map().zoomTo(d.entity);
34936                 context.enter(
34937                     iD.modes.Select(context, [d.entity.id])
34938                         .suppressMenu(true));
34939             }
34940         }
34941
34942         // Call checkComment off the bat, in case a changeset
34943         // comment is recovered from localStorage
34944         commentField.trigger('input');
34945     }
34946
34947     return d3.rebind(commit, dispatch, 'on');
34948 };
34949 iD.ui.confirm = function(selection) {
34950     var modal = iD.ui.modal(selection);
34951
34952     modal.select('.modal')
34953         .classed('modal-alert', true);
34954
34955     var section = modal.select('.content');
34956
34957     section.append('div')
34958         .attr('class', 'modal-section header');
34959
34960     section.append('div')
34961         .attr('class', 'modal-section message-text');
34962
34963     var buttons = section.append('div')
34964         .attr('class', 'modal-section buttons cf');
34965
34966     modal.okButton = function() {
34967         buttons
34968             .append('button')
34969             .attr('class', 'action col4')
34970             .on('click.confirm', function() {
34971                 modal.remove();
34972             })
34973             .text(t('confirm.okay'));
34974
34975         return modal;
34976     };
34977
34978     return modal;
34979 };
34980 iD.ui.Conflicts = function(context) {
34981     var dispatch = d3.dispatch('download', 'cancel', 'save'),
34982         list;
34983
34984     function conflicts(selection) {
34985         var header = selection
34986             .append('div')
34987             .attr('class', 'header fillL');
34988
34989         header
34990             .append('button')
34991             .attr('class', 'fr')
34992             .on('click', function() { dispatch.cancel(); })
34993             .call(iD.svg.Icon('#icon-close'));
34994
34995         header
34996             .append('h3')
34997             .text(t('save.conflict.header'));
34998
34999         var body = selection
35000             .append('div')
35001             .attr('class', 'body fillL');
35002
35003         body
35004             .append('div')
35005             .attr('class', 'conflicts-help')
35006             .text(t('save.conflict.help'))
35007             .append('a')
35008             .attr('class', 'conflicts-download')
35009             .text(t('save.conflict.download_changes'))
35010             .on('click.download', function() { dispatch.download(); });
35011
35012         body
35013             .append('div')
35014             .attr('class', 'conflict-container fillL3')
35015             .call(showConflict, 0);
35016
35017         body
35018             .append('div')
35019             .attr('class', 'conflicts-done')
35020             .attr('opacity', 0)
35021             .style('display', 'none')
35022             .text(t('save.conflict.done'));
35023
35024         var buttons = body
35025             .append('div')
35026             .attr('class','buttons col12 joined conflicts-buttons');
35027
35028         buttons
35029             .append('button')
35030             .attr('disabled', list.length > 1)
35031             .attr('class', 'action conflicts-button col6')
35032             .text(t('save.title'))
35033             .on('click.try_again', function() { dispatch.save(); });
35034
35035         buttons
35036             .append('button')
35037             .attr('class', 'secondary-action conflicts-button col6')
35038             .text(t('confirm.cancel'))
35039             .on('click.cancel', function() { dispatch.cancel(); });
35040     }
35041
35042
35043     function showConflict(selection, index) {
35044         if (index < 0 || index >= list.length) return;
35045
35046         var parent = d3.select(selection.node().parentNode);
35047
35048         // enable save button if this is the last conflict being reviewed..
35049         if (index === list.length - 1) {
35050             window.setTimeout(function() {
35051                 parent.select('.conflicts-button')
35052                     .attr('disabled', null);
35053
35054                 parent.select('.conflicts-done')
35055                     .transition()
35056                     .attr('opacity', 1)
35057                     .style('display', 'block');
35058             }, 250);
35059         }
35060
35061         var item = selection
35062             .selectAll('.conflict')
35063             .data([list[index]]);
35064
35065         var enter = item.enter()
35066             .append('div')
35067             .attr('class', 'conflict');
35068
35069         enter
35070             .append('h4')
35071             .attr('class', 'conflict-count')
35072             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
35073
35074         enter
35075             .append('a')
35076             .attr('class', 'conflict-description')
35077             .attr('href', '#')
35078             .text(function(d) { return d.name; })
35079             .on('click', function(d) {
35080                 zoomToEntity(d.id);
35081                 d3.event.preventDefault();
35082             });
35083
35084         var details = enter
35085             .append('div')
35086             .attr('class', 'conflict-detail-container');
35087
35088         details
35089             .append('ul')
35090             .attr('class', 'conflict-detail-list')
35091             .selectAll('li')
35092             .data(function(d) { return d.details || []; })
35093             .enter()
35094             .append('li')
35095             .attr('class', 'conflict-detail-item')
35096             .html(function(d) { return d; });
35097
35098         details
35099             .append('div')
35100             .attr('class', 'conflict-choices')
35101             .call(addChoices);
35102
35103         details
35104             .append('div')
35105             .attr('class', 'conflict-nav-buttons joined cf')
35106             .selectAll('button')
35107             .data(['previous', 'next'])
35108             .enter()
35109             .append('button')
35110             .text(function(d) { return t('save.conflict.' + d); })
35111             .attr('class', 'conflict-nav-button action col6')
35112             .attr('disabled', function(d, i) {
35113                 return (i === 0 && index === 0) ||
35114                     (i === 1 && index === list.length - 1) || null;
35115             })
35116             .on('click', function(d, i) {
35117                 var container = parent.select('.conflict-container'),
35118                 sign = (i === 0 ? -1 : 1);
35119
35120                 container
35121                     .selectAll('.conflict')
35122                     .remove();
35123
35124                 container
35125                     .call(showConflict, index + sign);
35126
35127                 d3.event.preventDefault();
35128             });
35129
35130         item.exit()
35131             .remove();
35132
35133     }
35134
35135     function addChoices(selection) {
35136         var choices = selection
35137             .append('ul')
35138             .attr('class', 'layer-list')
35139             .selectAll('li')
35140             .data(function(d) { return d.choices || []; });
35141
35142         var enter = choices.enter()
35143             .append('li')
35144             .attr('class', 'layer');
35145
35146         var label = enter
35147             .append('label');
35148
35149         label
35150             .append('input')
35151             .attr('type', 'radio')
35152             .attr('name', function(d) { return d.id; })
35153             .on('change', function(d, i) {
35154                 var ul = this.parentNode.parentNode.parentNode;
35155                 ul.__data__.chosen = i;
35156                 choose(ul, d);
35157             });
35158
35159         label
35160             .append('span')
35161             .text(function(d) { return d.text; });
35162
35163         choices
35164             .each(function(d, i) {
35165                 var ul = this.parentNode;
35166                 if (ul.__data__.chosen === i) choose(ul, d);
35167             });
35168     }
35169
35170     function choose(ul, datum) {
35171         if (d3.event) d3.event.preventDefault();
35172
35173         d3.select(ul)
35174             .selectAll('li')
35175             .classed('active', function(d) { return d === datum; })
35176             .selectAll('input')
35177             .property('checked', function(d) { return d === datum; });
35178
35179         var extent = iD.geo.Extent(),
35180             entity;
35181
35182         entity = context.graph().hasEntity(datum.id);
35183         if (entity) extent._extend(entity.extent(context.graph()));
35184
35185         datum.action();
35186
35187         entity = context.graph().hasEntity(datum.id);
35188         if (entity) extent._extend(entity.extent(context.graph()));
35189
35190         zoomToEntity(datum.id, extent);
35191     }
35192
35193     function zoomToEntity(id, extent) {
35194         context.surface().selectAll('.hover')
35195             .classed('hover', false);
35196
35197         var entity = context.graph().hasEntity(id);
35198         if (entity) {
35199             if (extent) {
35200                 context.map().trimmedExtent(extent);
35201             } else {
35202                 context.map().zoomTo(entity);
35203             }
35204             context.surface().selectAll(
35205                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
35206                 .classed('hover', true);
35207         }
35208     }
35209
35210
35211     // The conflict list should be an array of objects like:
35212     // {
35213     //     id: id,
35214     //     name: entityName(local),
35215     //     details: merge.conflicts(),
35216     //     chosen: 1,
35217     //     choices: [
35218     //         choice(id, keepMine, forceLocal),
35219     //         choice(id, keepTheirs, forceRemote)
35220     //     ]
35221     // }
35222     conflicts.list = function(_) {
35223         if (!arguments.length) return list;
35224         list = _;
35225         return conflicts;
35226     };
35227
35228     return d3.rebind(conflicts, dispatch, 'on');
35229 };
35230 iD.ui.Contributors = function(context) {
35231     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
35232         limit = 4,
35233         hidden = false,
35234         wrap = d3.select(null);
35235
35236     function update() {
35237         var users = {},
35238             entities = context.intersects(context.map().extent());
35239
35240         entities.forEach(function(entity) {
35241             if (entity && entity.user) users[entity.user] = true;
35242         });
35243
35244         var u = Object.keys(users),
35245             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
35246
35247         wrap.html('')
35248             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
35249
35250         var userList = d3.select(document.createElement('span'));
35251
35252         userList.selectAll()
35253             .data(subset)
35254             .enter()
35255             .append('a')
35256             .attr('class', 'user-link')
35257             .attr('href', function(d) { return context.connection().userURL(d); })
35258             .attr('target', '_blank')
35259             .attr('tabindex', -1)
35260             .text(String);
35261
35262         if (u.length > limit) {
35263             var count = d3.select(document.createElement('span'));
35264
35265             count.append('a')
35266                 .attr('target', '_blank')
35267                 .attr('tabindex', -1)
35268                 .attr('href', function() {
35269                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
35270                 })
35271                 .text(u.length - limit + 1);
35272
35273             wrap.append('span')
35274                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
35275
35276         } else {
35277             wrap.append('span')
35278                 .html(t('contributors.list', { users: userList.html() }));
35279         }
35280
35281         if (!u.length) {
35282             hidden = true;
35283             wrap
35284                 .transition()
35285                 .style('opacity', 0);
35286
35287         } else if (hidden) {
35288             wrap
35289                 .transition()
35290                 .style('opacity', 1);
35291         }
35292     }
35293
35294     return function(selection) {
35295         wrap = selection;
35296         update();
35297
35298         context.connection().on('loaded.contributors', debouncedUpdate);
35299         context.map().on('move.contributors', debouncedUpdate);
35300     };
35301 };
35302 iD.ui.Disclosure = function() {
35303     var dispatch = d3.dispatch('toggled'),
35304         title,
35305         expanded = false,
35306         content = function () {};
35307
35308     var disclosure = function(selection) {
35309         var $link = selection.selectAll('.hide-toggle')
35310             .data([0]);
35311
35312         $link.enter().append('a')
35313             .attr('href', '#')
35314             .attr('class', 'hide-toggle');
35315
35316         $link.text(title)
35317             .on('click', toggle)
35318             .classed('expanded', expanded);
35319
35320         var $body = selection.selectAll('div')
35321             .data([0]);
35322
35323         $body.enter().append('div');
35324
35325         $body.classed('hide', !expanded)
35326             .call(content);
35327
35328         function toggle() {
35329             expanded = !expanded;
35330             $link.classed('expanded', expanded);
35331             $body.call(iD.ui.Toggle(expanded));
35332             dispatch.toggled(expanded);
35333         }
35334     };
35335
35336     disclosure.title = function(_) {
35337         if (!arguments.length) return title;
35338         title = _;
35339         return disclosure;
35340     };
35341
35342     disclosure.expanded = function(_) {
35343         if (!arguments.length) return expanded;
35344         expanded = _;
35345         return disclosure;
35346     };
35347
35348     disclosure.content = function(_) {
35349         if (!arguments.length) return content;
35350         content = _;
35351         return disclosure;
35352     };
35353
35354     return d3.rebind(disclosure, dispatch, 'on');
35355 };
35356 iD.ui.EntityEditor = function(context) {
35357     var dispatch = d3.dispatch('choose'),
35358         state = 'select',
35359         coalesceChanges = false,
35360         modified = false,
35361         base,
35362         id,
35363         preset,
35364         reference;
35365
35366     var presetEditor = iD.ui.preset(context)
35367         .on('change', changeTags);
35368     var rawTagEditor = iD.ui.RawTagEditor(context)
35369         .on('change', changeTags);
35370
35371     function entityEditor(selection) {
35372         var entity = context.entity(id),
35373             tags = _.clone(entity.tags);
35374
35375         var $header = selection.selectAll('.header')
35376             .data([0]);
35377
35378         // Enter
35379         var $enter = $header.enter().append('div')
35380             .attr('class', 'header fillL cf');
35381
35382         $enter.append('button')
35383             .attr('class', 'fl preset-reset preset-choose')
35384             .append('span')
35385             .html('&#9668;');
35386
35387         $enter.append('button')
35388             .attr('class', 'fr preset-close')
35389             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
35390
35391         $enter.append('h3');
35392
35393         // Update
35394         $header.select('h3')
35395             .text(t('inspector.edit'));
35396
35397         $header.select('.preset-close')
35398             .on('click', function() {
35399                 context.enter(iD.modes.Browse(context));
35400             });
35401
35402         var $body = selection.selectAll('.inspector-body')
35403             .data([0]);
35404
35405         // Enter
35406         $enter = $body.enter().append('div')
35407             .attr('class', 'inspector-body');
35408
35409         $enter.append('div')
35410             .attr('class', 'preset-list-item inspector-inner')
35411             .append('div')
35412             .attr('class', 'preset-list-button-wrap')
35413             .append('button')
35414             .attr('class', 'preset-list-button preset-reset')
35415             .call(bootstrap.tooltip()
35416                 .title(t('inspector.back_tooltip'))
35417                 .placement('bottom'))
35418             .append('div')
35419             .attr('class', 'label');
35420
35421         $body.select('.preset-list-button-wrap')
35422             .call(reference.button);
35423
35424         $body.select('.preset-list-item')
35425             .call(reference.body);
35426
35427         $enter.append('div')
35428             .attr('class', 'inspector-border inspector-preset');
35429
35430         $enter.append('div')
35431             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
35432
35433         $enter.append('div')
35434             .attr('class', 'inspector-border raw-member-editor inspector-inner');
35435
35436         $enter.append('div')
35437             .attr('class', 'raw-membership-editor inspector-inner');
35438
35439         selection.selectAll('.preset-reset')
35440             .on('click', function() {
35441                 dispatch.choose(preset);
35442             });
35443
35444         // Update
35445         $body.select('.preset-list-item button')
35446             .call(iD.ui.PresetIcon()
35447                 .geometry(context.geometry(id))
35448                 .preset(preset));
35449
35450         $body.select('.preset-list-item .label')
35451             .text(preset.name());
35452
35453         $body.select('.inspector-preset')
35454             .call(presetEditor
35455                 .preset(preset)
35456                 .entityID(id)
35457                 .tags(tags)
35458                 .state(state));
35459
35460         $body.select('.raw-tag-editor')
35461             .call(rawTagEditor
35462                 .preset(preset)
35463                 .entityID(id)
35464                 .tags(tags)
35465                 .state(state));
35466
35467         if (entity.type === 'relation') {
35468             $body.select('.raw-member-editor')
35469                 .style('display', 'block')
35470                 .call(iD.ui.RawMemberEditor(context)
35471                     .entityID(id));
35472         } else {
35473             $body.select('.raw-member-editor')
35474                 .style('display', 'none');
35475         }
35476
35477         $body.select('.raw-membership-editor')
35478             .call(iD.ui.RawMembershipEditor(context)
35479                 .entityID(id));
35480
35481         function historyChanged() {
35482             if (state === 'hide') return;
35483
35484             var entity = context.hasEntity(id),
35485                 graph = context.graph();
35486             if (!entity) return;
35487
35488             entityEditor.preset(context.presets().match(entity, graph));
35489             entityEditor.modified(base !== graph);
35490             entityEditor(selection);
35491         }
35492
35493         context.history()
35494             .on('change.entity-editor', historyChanged);
35495     }
35496
35497     function clean(o) {
35498
35499         function cleanVal(k, v) {
35500             function keepSpaces(k) {
35501                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
35502                     'operating_times', 'smoking_hours', 'happy_hours'];
35503                 return _.some(whitelist, function(s) { return k.indexOf(s) !== -1; });
35504             }
35505
35506             var blacklist = ['description', 'note', 'fixme'];
35507             if (_.some(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
35508
35509             var cleaned = v.split(';')
35510                 .map(function(s) { return s.trim(); })
35511                 .join(keepSpaces(k) ? '; ' : ';');
35512
35513             // The code below is not intended to validate websites and emails.
35514             // It is only intended to prevent obvious copy-paste errors. (#2323)
35515
35516             // clean website- and email-like tags
35517             if (k.indexOf('website') !== -1 ||
35518                 k.indexOf('email') !== -1 ||
35519                 cleaned.indexOf('http') === 0) {
35520                 cleaned = cleaned
35521                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
35522
35523             }
35524
35525             return cleaned;
35526         }
35527
35528         var out = {}, k, v;
35529         for (k in o) {
35530             if (k && (v = o[k]) !== undefined) {
35531                 out[k] = cleanVal(k, v);
35532             }
35533         }
35534         return out;
35535     }
35536
35537     // Tag changes that fire on input can all get coalesced into a single
35538     // history operation when the user leaves the field.  #2342
35539     function changeTags(changed, onInput) {
35540         var entity = context.entity(id),
35541             annotation = t('operations.change_tags.annotation'),
35542             tags = _.extend({}, entity.tags, changed);
35543
35544         if (!onInput) {
35545             tags = clean(tags);
35546         }
35547         if (!_.isEqual(entity.tags, tags)) {
35548             if (coalesceChanges) {
35549                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
35550             } else {
35551                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
35552                 coalesceChanges = !!onInput;
35553             }
35554         }
35555     }
35556
35557     entityEditor.modified = function(_) {
35558         if (!arguments.length) return modified;
35559         modified = _;
35560         d3.selectAll('button.preset-close use')
35561             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
35562     };
35563
35564     entityEditor.state = function(_) {
35565         if (!arguments.length) return state;
35566         state = _;
35567         return entityEditor;
35568     };
35569
35570     entityEditor.entityID = function(_) {
35571         if (!arguments.length) return id;
35572         id = _;
35573         base = context.graph();
35574         entityEditor.preset(context.presets().match(context.entity(id), base));
35575         entityEditor.modified(false);
35576         coalesceChanges = false;
35577         return entityEditor;
35578     };
35579
35580     entityEditor.preset = function(_) {
35581         if (!arguments.length) return preset;
35582         if (_ !== preset) {
35583             preset = _;
35584             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
35585                 .showing(false);
35586         }
35587         return entityEditor;
35588     };
35589
35590     return d3.rebind(entityEditor, dispatch, 'on');
35591 };
35592 iD.ui.FeatureInfo = function(context) {
35593     function update(selection) {
35594         var features = context.features(),
35595             stats = features.stats(),
35596             count = 0,
35597             hiddenList = _.compact(_.map(features.hidden(), function(k) {
35598                 if (stats[k]) {
35599                     count += stats[k];
35600                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
35601                 }
35602             }));
35603
35604         selection.html('');
35605
35606         if (hiddenList.length) {
35607             var tooltip = bootstrap.tooltip()
35608                     .placement('top')
35609                     .html(true)
35610                     .title(function() {
35611                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
35612                     });
35613
35614             var warning = selection.append('a')
35615                 .attr('href', '#')
35616                 .attr('tabindex', -1)
35617                 .html(t('feature_info.hidden_warning', { count: count }))
35618                 .call(tooltip)
35619                 .on('click', function() {
35620                     tooltip.hide(warning);
35621                     // open map data panel?
35622                     d3.event.preventDefault();
35623                 });
35624         }
35625
35626         selection
35627             .classed('hide', !hiddenList.length);
35628     }
35629
35630     return function(selection) {
35631         update(selection);
35632
35633         context.features().on('change.feature_info', function() {
35634             update(selection);
35635         });
35636     };
35637 };
35638 iD.ui.FeatureList = function(context) {
35639     var geocodeResults;
35640
35641     function featureList(selection) {
35642         var header = selection.append('div')
35643             .attr('class', 'header fillL cf');
35644
35645         header.append('h3')
35646             .text(t('inspector.feature_list'));
35647
35648         function keypress() {
35649             var q = search.property('value'),
35650                 items = list.selectAll('.feature-list-item');
35651             if (d3.event.keyCode === 13 && q.length && items.size()) {
35652                 click(items.datum());
35653             }
35654         }
35655
35656         function inputevent() {
35657             geocodeResults = undefined;
35658             drawList();
35659         }
35660
35661         var searchWrap = selection.append('div')
35662             .attr('class', 'search-header');
35663
35664         var search = searchWrap.append('input')
35665             .attr('placeholder', t('inspector.search'))
35666             .attr('type', 'search')
35667             .on('keypress', keypress)
35668             .on('input', inputevent);
35669
35670         searchWrap
35671             .call(iD.svg.Icon('#icon-search', 'pre-text'));
35672
35673         var listWrap = selection.append('div')
35674             .attr('class', 'inspector-body');
35675
35676         var list = listWrap.append('div')
35677             .attr('class', 'feature-list cf');
35678
35679         context
35680             .on('exit.feature-list', clearSearch);
35681         context.map()
35682             .on('drawn.feature-list', mapDrawn);
35683
35684         function clearSearch() {
35685             search.property('value', '');
35686             drawList();
35687         }
35688
35689         function mapDrawn(e) {
35690             if (e.full) {
35691                 drawList();
35692             }
35693         }
35694
35695         function features() {
35696             var entities = {},
35697                 result = [],
35698                 graph = context.graph(),
35699                 q = search.property('value').toLowerCase();
35700
35701             if (!q) return result;
35702
35703             var idMatch = q.match(/^([nwr])([0-9]+)$/);
35704
35705             if (idMatch) {
35706                 result.push({
35707                     id: idMatch[0],
35708                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
35709                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
35710                     name: idMatch[2]
35711                 });
35712             }
35713
35714             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
35715
35716             if (locationMatch) {
35717                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
35718                 result.push({
35719                     id: -1,
35720                     geometry: 'point',
35721                     type: t('inspector.location'),
35722                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
35723                     location: loc
35724                 });
35725             }
35726
35727             function addEntity(entity) {
35728                 if (entity.id in entities || result.length > 200)
35729                     return;
35730
35731                 entities[entity.id] = true;
35732
35733                 var name = iD.util.displayName(entity) || '';
35734                 if (name.toLowerCase().indexOf(q) >= 0) {
35735                     result.push({
35736                         id: entity.id,
35737                         entity: entity,
35738                         geometry: context.geometry(entity.id),
35739                         type: context.presets().match(entity, graph).name(),
35740                         name: name
35741                     });
35742                 }
35743
35744                 graph.parentRelations(entity).forEach(function(parent) {
35745                     addEntity(parent);
35746                 });
35747             }
35748
35749             var visible = context.surface().selectAll('.point, .line, .area')[0];
35750             for (var i = 0; i < visible.length && result.length <= 200; i++) {
35751                 addEntity(visible[i].__data__);
35752             }
35753
35754             (geocodeResults || []).forEach(function(d) {
35755                 // https://github.com/openstreetmap/iD/issues/1890
35756                 if (d.osm_type && d.osm_id) {
35757                     result.push({
35758                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
35759                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
35760                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
35761                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
35762                         name: d.display_name,
35763                         extent: new iD.geo.Extent(
35764                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
35765                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
35766                     });
35767                 }
35768             });
35769
35770             return result;
35771         }
35772
35773         function drawList() {
35774             var value = search.property('value'),
35775                 results = features();
35776
35777             list.classed('filtered', value.length);
35778
35779             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
35780
35781             var resultsIndicator = list.selectAll('.no-results-item')
35782                 .data([0])
35783                 .enter().append('button')
35784                 .property('disabled', true)
35785                 .attr('class', 'no-results-item')
35786                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
35787
35788             resultsIndicator.append('span')
35789                 .attr('class', 'entity-name');
35790
35791             list.selectAll('.no-results-item .entity-name')
35792                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
35793
35794             list.selectAll('.geocode-item')
35795                 .data([0])
35796                 .enter().append('button')
35797                 .attr('class', 'geocode-item')
35798                 .on('click', geocode)
35799                 .append('div')
35800                 .attr('class', 'label')
35801                 .append('span')
35802                 .attr('class', 'entity-name')
35803                 .text(t('geocoder.search'));
35804
35805             list.selectAll('.no-results-item')
35806                 .style('display', (value.length && !results.length) ? 'block' : 'none');
35807
35808             list.selectAll('.geocode-item')
35809                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
35810
35811             list.selectAll('.feature-list-item')
35812                 .data([-1])
35813                 .remove();
35814
35815             var items = list.selectAll('.feature-list-item')
35816                 .data(results, function(d) { return d.id; });
35817
35818             var enter = items.enter()
35819                 .insert('button', '.geocode-item')
35820                 .attr('class', 'feature-list-item')
35821                 .on('mouseover', mouseover)
35822                 .on('mouseout', mouseout)
35823                 .on('click', click);
35824
35825             var label = enter
35826                 .append('div')
35827                 .attr('class', 'label');
35828
35829             label.each(function(d) {
35830                 d3.select(this)
35831                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
35832             });
35833
35834             label.append('span')
35835                 .attr('class', 'entity-type')
35836                 .text(function(d) { return d.type; });
35837
35838             label.append('span')
35839                 .attr('class', 'entity-name')
35840                 .text(function(d) { return d.name; });
35841
35842             enter.style('opacity', 0)
35843                 .transition()
35844                 .style('opacity', 1);
35845
35846             items.order();
35847
35848             items.exit()
35849                 .remove();
35850         }
35851
35852         function mouseover(d) {
35853             if (d.id === -1) return;
35854
35855             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
35856                 .classed('hover', true);
35857         }
35858
35859         function mouseout() {
35860             context.surface().selectAll('.hover')
35861                 .classed('hover', false);
35862         }
35863
35864         function click(d) {
35865             d3.event.preventDefault();
35866             if (d.location) {
35867                 context.map().centerZoom([d.location[1], d.location[0]], 20);
35868             }
35869             else if (d.entity) {
35870                 if (d.entity.type === 'node') {
35871                     context.map().center(d.entity.loc);
35872                 } else if (d.entity.type === 'way') {
35873                     var center = context.projection(context.map().center()),
35874                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
35875                     context.map().center(edge.loc);
35876                 }
35877                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
35878             } else {
35879                 context.zoomToEntity(d.id);
35880             }
35881         }
35882
35883         function geocode() {
35884             var searchVal = encodeURIComponent(search.property('value'));
35885             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
35886                 geocodeResults = resp || [];
35887                 drawList();
35888             });
35889         }
35890     }
35891
35892     return featureList;
35893 };
35894 iD.ui.flash = function(selection) {
35895     var modal = iD.ui.modal(selection);
35896
35897     modal.select('.modal').classed('modal-flash', true);
35898
35899     modal.select('.content')
35900         .classed('modal-section', true)
35901         .append('div')
35902         .attr('class', 'description');
35903
35904     modal.on('click.flash', function() { modal.remove(); });
35905
35906     setTimeout(function() {
35907         modal.remove();
35908         return true;
35909     }, 1500);
35910
35911     return modal;
35912 };
35913 iD.ui.FullScreen = function(context) {
35914     var element = context.container().node(),
35915         keybinding = d3.keybinding('full-screen');
35916         // button;
35917
35918     function getFullScreenFn() {
35919         if (element.requestFullscreen) {
35920             return element.requestFullscreen;
35921         } else if (element.msRequestFullscreen) {
35922             return  element.msRequestFullscreen;
35923         } else if (element.mozRequestFullScreen) {
35924             return  element.mozRequestFullScreen;
35925         } else if (element.webkitRequestFullscreen) {
35926             return element.webkitRequestFullscreen;
35927         }
35928     }
35929
35930     function getExitFullScreenFn() {
35931         if (document.exitFullscreen) {
35932             return document.exitFullscreen;
35933         } else if (document.msExitFullscreen) {
35934             return  document.msExitFullscreen;
35935         } else if (document.mozCancelFullScreen) {
35936             return  document.mozCancelFullScreen;
35937         } else if (document.webkitExitFullscreen) {
35938             return document.webkitExitFullscreen;
35939         }
35940     }
35941
35942     function isFullScreen() {
35943         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
35944             document.msFullscreenElement;
35945     }
35946
35947     function isSupported() {
35948         return !!getFullScreenFn();
35949     }
35950
35951     function fullScreen() {
35952         d3.event.preventDefault();
35953         if (!isFullScreen()) {
35954             // button.classed('active', true);
35955             getFullScreenFn().apply(element);
35956         } else {
35957             // button.classed('active', false);
35958             getExitFullScreenFn().apply(document);
35959         }
35960     }
35961
35962     return function() { // selection) {
35963         if (!isSupported())
35964             return;
35965
35966         // var tooltip = bootstrap.tooltip()
35967         //     .placement('left');
35968
35969         // button = selection.append('button')
35970         //     .attr('title', t('full_screen'))
35971         //     .attr('tabindex', -1)
35972         //     .on('click', fullScreen)
35973         //     .call(tooltip);
35974
35975         // button.append('span')
35976         //     .attr('class', 'icon full-screen');
35977
35978         keybinding
35979             .on('f11', fullScreen)
35980             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
35981
35982         d3.select(document)
35983             .call(keybinding);
35984     };
35985 };
35986 iD.ui.Geolocate = function(context) {
35987     var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
35988         locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
35989         timeoutId;
35990
35991     function click() {
35992         context.enter(iD.modes.Browse(context));
35993         context.container().call(locating);
35994         navigator.geolocation.getCurrentPosition(success, error, geoOptions);
35995
35996         // This timeout ensures that we still call finish() even if
35997         // the user declines to share their location in Firefox
35998         timeoutId = setTimeout(finish, 10000 /* 10sec */ );
35999     }
36000
36001     function success(position) {
36002         var map = context.map(),
36003             extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
36004                 .padByMeters(position.coords.accuracy);
36005
36006         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
36007         finish();
36008     }
36009
36010     function error() {
36011         finish();
36012     }
36013
36014     function finish() {
36015         locating.close();  // unblock ui
36016         if (timeoutId) { clearTimeout(timeoutId); }
36017         timeoutId = undefined;
36018     }
36019
36020     return function(selection) {
36021         if (!navigator.geolocation) return;
36022
36023         selection.append('button')
36024             .attr('tabindex', -1)
36025             .attr('title', t('geolocate.title'))
36026             .on('click', click)
36027             .call(iD.svg.Icon('#icon-geolocate', 'light'))
36028             .call(bootstrap.tooltip()
36029                 .placement('left'));
36030     };
36031 };
36032 iD.ui.Help = function(context) {
36033     var key = 'H';
36034
36035     var docKeys = [
36036         'help.help',
36037         'help.editing_saving',
36038         'help.roads',
36039         'help.gps',
36040         'help.imagery',
36041         'help.addresses',
36042         'help.inspector',
36043         'help.buildings',
36044         'help.relations'];
36045
36046     var docs = docKeys.map(function(key) {
36047         var text = t(key);
36048         return {
36049             title: text.split('\n')[0].replace('#', '').trim(),
36050             html: marked(text.split('\n').slice(1).join('\n'))
36051         };
36052     });
36053
36054     function help(selection) {
36055
36056         function hide() {
36057             setVisible(false);
36058         }
36059
36060         function toggle() {
36061             if (d3.event) d3.event.preventDefault();
36062             tooltip.hide(button);
36063             setVisible(!button.classed('active'));
36064         }
36065
36066         function setVisible(show) {
36067             if (show !== shown) {
36068                 button.classed('active', show);
36069                 shown = show;
36070
36071                 if (show) {
36072                     selection.on('mousedown.help-inside', function() {
36073                         return d3.event.stopPropagation();
36074                     });
36075                     pane.style('display', 'block')
36076                         .style('right', '-500px')
36077                         .transition()
36078                         .duration(200)
36079                         .style('right', '0px');
36080                 } else {
36081                     pane.style('right', '0px')
36082                         .transition()
36083                         .duration(200)
36084                         .style('right', '-500px')
36085                         .each('end', function() {
36086                             d3.select(this).style('display', 'none');
36087                         });
36088                     selection.on('mousedown.help-inside', null);
36089                 }
36090             }
36091         }
36092
36093         function clickHelp(d, i) {
36094             pane.property('scrollTop', 0);
36095             doctitle.html(d.title);
36096             body.html(d.html);
36097             body.selectAll('a')
36098                 .attr('target', '_blank');
36099             menuItems.classed('selected', function(m) {
36100                 return m.title === d.title;
36101             });
36102
36103             nav.html('');
36104
36105             if (i > 0) {
36106                 var prevLink = nav.append('a')
36107                     .attr('class', 'previous')
36108                     .on('click', function() {
36109                         clickHelp(docs[i - 1], i - 1);
36110                     });
36111                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
36112             }
36113             if (i < docs.length - 1) {
36114                 var nextLink = nav.append('a')
36115                     .attr('class', 'next')
36116                     .on('click', function() {
36117                         clickHelp(docs[i + 1], i + 1);
36118                     });
36119                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
36120             }
36121         }
36122
36123         function clickWalkthrough() {
36124             d3.select(document.body).call(iD.ui.intro(context));
36125             setVisible(false);
36126         }
36127
36128
36129         var pane = selection.append('div')
36130                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
36131             tooltip = bootstrap.tooltip()
36132                 .placement('left')
36133                 .html(true)
36134                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
36135             button = selection.append('button')
36136                 .attr('tabindex', -1)
36137                 .on('click', toggle)
36138                 .call(iD.svg.Icon('#icon-help', 'light'))
36139                 .call(tooltip),
36140             shown = false;
36141
36142
36143         var toc = pane.append('ul')
36144             .attr('class', 'toc');
36145
36146         var menuItems = toc.selectAll('li')
36147             .data(docs)
36148             .enter()
36149             .append('li')
36150             .append('a')
36151             .html(function(d) { return d.title; })
36152             .on('click', clickHelp);
36153
36154         toc.append('li')
36155             .attr('class','walkthrough')
36156             .append('a')
36157             .text(t('splash.walkthrough'))
36158             .on('click', clickWalkthrough);
36159
36160         var content = pane.append('div')
36161             .attr('class', 'left-content');
36162
36163         var doctitle = content.append('h2')
36164             .text(t('help.title'));
36165
36166         var body = content.append('div')
36167             .attr('class', 'body');
36168
36169         var nav = content.append('div')
36170             .attr('class', 'nav');
36171
36172         clickHelp(docs[0], 0);
36173
36174         var keybinding = d3.keybinding('help')
36175             .on(key, toggle)
36176             .on('B', hide)
36177             .on('F', hide);
36178
36179         d3.select(document)
36180             .call(keybinding);
36181
36182         context.surface().on('mousedown.help-outside', hide);
36183         context.container().on('mousedown.help-outside', hide);
36184     }
36185
36186     return help;
36187 };
36188 iD.ui.Info = function(context) {
36189     var key = iD.ui.cmd('⌘I'),
36190         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
36191         hidden = true;
36192
36193     function info(selection) {
36194         function radiansToMeters(r) {
36195             // using WGS84 authalic radius (6371007.1809 m)
36196             return r * 6371007.1809;
36197         }
36198
36199         function steradiansToSqmeters(r) {
36200             // http://gis.stackexchange.com/a/124857/40446
36201             return r / 12.56637 * 510065621724000;
36202         }
36203
36204         function toLineString(feature) {
36205             if (feature.type === 'LineString') return feature;
36206
36207             var result = { type: 'LineString', coordinates: [] };
36208             if (feature.type === 'Polygon') {
36209                 result.coordinates = feature.coordinates[0];
36210             } else if (feature.type === 'MultiPolygon') {
36211                 result.coordinates = feature.coordinates[0][0];
36212             }
36213
36214             return result;
36215         }
36216
36217         function displayLength(m) {
36218             var d = m * (imperial ? 3.28084 : 1),
36219                 p, unit;
36220
36221             if (imperial) {
36222                 if (d >= 5280) {
36223                     d /= 5280;
36224                     unit = 'mi';
36225                 } else {
36226                     unit = 'ft';
36227                 }
36228             } else {
36229                 if (d >= 1000) {
36230                     d /= 1000;
36231                     unit = 'km';
36232                 } else {
36233                     unit = 'm';
36234                 }
36235             }
36236
36237             // drop unnecessary precision
36238             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
36239
36240             return String(d.toFixed(p)) + ' ' + unit;
36241         }
36242
36243         function displayArea(m2) {
36244             var d = m2 * (imperial ? 10.7639111056 : 1),
36245                 d1, d2, p1, p2, unit1, unit2;
36246
36247             if (imperial) {
36248                 if (d >= 6969600) {     // > 0.25mi² show mi²
36249                     d1 = d / 27878400;
36250                     unit1 = 'mi²';
36251                 } else {
36252                     d1 = d;
36253                     unit1 = 'ft²';
36254                 }
36255
36256                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
36257                     d2 = d / 43560;
36258                     unit2 = 'ac';
36259                 }
36260
36261             } else {
36262                 if (d >= 250000) {    // > 0.25km² show km²
36263                     d1 = d / 1000000;
36264                     unit1 = 'km²';
36265                 } else {
36266                     d1 = d;
36267                     unit1 = 'm²';
36268                 }
36269
36270                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
36271                     d2 = d / 10000;
36272                     unit2 = 'ha';
36273                 }
36274             }
36275
36276             // drop unnecessary precision
36277             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
36278             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
36279
36280             return String(d1.toFixed(p1)) + ' ' + unit1 +
36281                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
36282         }
36283
36284
36285         function redraw() {
36286             if (hidden) return;
36287
36288             var resolver = context.graph(),
36289                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
36290                 singular = selected.length === 1 ? selected[0] : null,
36291                 extent = iD.geo.Extent(),
36292                 entity;
36293
36294             wrap.html('');
36295             wrap.append('h4')
36296                 .attr('class', 'infobox-heading fillD')
36297                 .text(singular || t('infobox.selected', { n: selected.length }));
36298
36299             if (!selected.length) return;
36300
36301             var center;
36302             for (var i = 0; i < selected.length; i++) {
36303                 entity = context.entity(selected[i]);
36304                 extent._extend(entity.extent(resolver));
36305             }
36306             center = extent.center();
36307
36308
36309             var list = wrap.append('ul');
36310
36311             // multiple wrap, just display extent center..
36312             if (!singular) {
36313                 list.append('li')
36314                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
36315                 return;
36316             }
36317
36318             // single wrap, display details..
36319             if (!entity) return;
36320             var geometry = entity.geometry(resolver);
36321
36322             if (geometry === 'line' || geometry === 'area') {
36323                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
36324                     feature = entity.asGeoJSON(resolver),
36325                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
36326                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
36327                     centroid = d3.geo.centroid(feature);
36328
36329                 list.append('li')
36330                     .text(t('infobox.geometry') + ': ' +
36331                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
36332
36333                 if (closed) {
36334                     var area = steradiansToSqmeters(entity.area(resolver));
36335                     list.append('li')
36336                         .text(t('infobox.area') + ': ' + displayArea(area));
36337                 }
36338
36339                 list.append('li')
36340                     .text(lengthLabel + ': ' + displayLength(length));
36341
36342                 list.append('li')
36343                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
36344
36345
36346                 var toggle  = imperial ? 'imperial' : 'metric';
36347                 wrap.append('a')
36348                     .text(t('infobox.' + toggle))
36349                     .attr('href', '#')
36350                     .attr('class', 'button')
36351                     .on('click', function() {
36352                         d3.event.preventDefault();
36353                         imperial = !imperial;
36354                         redraw();
36355                     });
36356
36357             } else {
36358                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
36359
36360                 list.append('li')
36361                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
36362
36363                 list.append('li')
36364                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
36365             }
36366         }
36367
36368
36369         function toggle() {
36370             if (d3.event) d3.event.preventDefault();
36371
36372             hidden = !hidden;
36373
36374             if (hidden) {
36375                 wrap
36376                     .style('display', 'block')
36377                     .style('opacity', 1)
36378                     .transition()
36379                     .duration(200)
36380                     .style('opacity', 0)
36381                     .each('end', function() {
36382                         d3.select(this).style('display', 'none');
36383                     });
36384             } else {
36385                 wrap
36386                     .style('display', 'block')
36387                     .style('opacity', 0)
36388                     .transition()
36389                     .duration(200)
36390                     .style('opacity', 1);
36391
36392                 redraw();
36393             }
36394         }
36395
36396
36397         var wrap = selection.selectAll('.infobox')
36398             .data([0]);
36399
36400         wrap.enter()
36401             .append('div')
36402             .attr('class', 'infobox fillD2')
36403             .style('display', (hidden ? 'none' : 'block'));
36404
36405         context.map()
36406             .on('drawn.info', redraw);
36407
36408         redraw();
36409
36410         var keybinding = d3.keybinding('info')
36411             .on(key, toggle);
36412
36413         d3.select(document)
36414             .call(keybinding);
36415     }
36416
36417     return info;
36418 };
36419 iD.ui.Inspector = function(context) {
36420     var presetList = iD.ui.PresetList(context),
36421         entityEditor = iD.ui.EntityEditor(context),
36422         state = 'select',
36423         entityID,
36424         newFeature = false;
36425
36426     function inspector(selection) {
36427         presetList
36428             .entityID(entityID)
36429             .autofocus(newFeature)
36430             .on('choose', setPreset);
36431
36432         entityEditor
36433             .state(state)
36434             .entityID(entityID)
36435             .on('choose', showList);
36436
36437         var $wrap = selection.selectAll('.panewrap')
36438             .data([0]);
36439
36440         var $enter = $wrap.enter().append('div')
36441             .attr('class', 'panewrap');
36442
36443         $enter.append('div')
36444             .attr('class', 'preset-list-pane pane');
36445
36446         $enter.append('div')
36447             .attr('class', 'entity-editor-pane pane');
36448
36449         var $presetPane = $wrap.select('.preset-list-pane');
36450         var $editorPane = $wrap.select('.entity-editor-pane');
36451
36452         var graph = context.graph(),
36453             entity = context.entity(entityID),
36454             showEditor = state === 'hover' ||
36455                 entity.isUsed(graph) ||
36456                 entity.isHighwayIntersection(graph);
36457
36458         if (showEditor) {
36459             $wrap.style('right', '0%');
36460             $editorPane.call(entityEditor);
36461         } else {
36462             $wrap.style('right', '-100%');
36463             $presetPane.call(presetList);
36464         }
36465
36466         var $footer = selection.selectAll('.footer')
36467             .data([0]);
36468
36469         $footer.enter().append('div')
36470             .attr('class', 'footer');
36471
36472         selection.select('.footer')
36473             .call(iD.ui.ViewOnOSM(context)
36474                 .entityID(entityID));
36475
36476         function showList(preset) {
36477             $wrap.transition()
36478                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
36479
36480             $presetPane.call(presetList
36481                 .preset(preset)
36482                 .autofocus(true));
36483         }
36484
36485         function setPreset(preset) {
36486             $wrap.transition()
36487                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
36488
36489             $editorPane.call(entityEditor
36490                 .preset(preset));
36491         }
36492     }
36493
36494     inspector.state = function(_) {
36495         if (!arguments.length) return state;
36496         state = _;
36497         entityEditor.state(state);
36498         return inspector;
36499     };
36500
36501     inspector.entityID = function(_) {
36502         if (!arguments.length) return entityID;
36503         entityID = _;
36504         return inspector;
36505     };
36506
36507     inspector.newFeature = function(_) {
36508         if (!arguments.length) return newFeature;
36509         newFeature = _;
36510         return inspector;
36511     };
36512
36513     return inspector;
36514 };
36515 iD.ui.intro = function(context) {
36516     var step;
36517
36518     function intro(selection) {
36519
36520         function localizedName(id) {
36521             var features = {
36522                 n2140018997: 'city_hall',
36523                 n367813436: 'fire_department',
36524                 w203988286: 'memory_isle_park',
36525                 w203972937: 'riverwalk_trail',
36526                 w203972938: 'riverwalk_trail',
36527                 w203972940: 'riverwalk_trail',
36528                 w41785752: 'w_michigan_ave',
36529                 w134150789: 'w_michigan_ave',
36530                 w134150795: 'w_michigan_ave',
36531                 w134150800: 'w_michigan_ave',
36532                 w134150811: 'w_michigan_ave',
36533                 w134150802: 'e_michigan_ave',
36534                 w134150836: 'e_michigan_ave',
36535                 w41074896: 'e_michigan_ave',
36536                 w17965834: 'spring_st',
36537                 w203986457: 'scidmore_park',
36538                 w203049587: 'petting_zoo',
36539                 w17967397: 'n_andrews_st',
36540                 w17967315: 's_andrews_st',
36541                 w17967326: 'n_constantine_st',
36542                 w17966400: 's_constantine_st',
36543                 w170848823: 'rocky_river',
36544                 w170848824: 'rocky_river',
36545                 w170848331: 'rocky_river',
36546                 w17967752: 'railroad_dr',
36547                 w17965998: 'conrail_rr',
36548                 w134150845: 'conrail_rr',
36549                 w170989131: 'st_joseph_river',
36550                 w143497377: 'n_main_st',
36551                 w134150801: 's_main_st',
36552                 w134150830: 's_main_st',
36553                 w17966462: 's_main_st',
36554                 w17967734: 'water_st',
36555                 w17964996: 'foster_st',
36556                 w170848330: 'portage_river',
36557                 w17965351: 'flower_st',
36558                 w17965502: 'elm_st',
36559                 w17965402: 'walnut_st',
36560                 w17964793: 'morris_ave',
36561                 w17967444: 'east_st',
36562                 w17966984: 'portage_ave'
36563             };
36564             return features[id] && t('intro.graph.' + features[id]);
36565         }
36566
36567         context.enter(iD.modes.Browse(context));
36568
36569         // Save current map state
36570         var history = context.history().toJSON(),
36571             hash = window.location.hash,
36572             center = context.map().center(),
36573             zoom = context.map().zoom(),
36574             background = context.background().baseLayerSource(),
36575             opacity = d3.selectAll('#map .layer-background').style('opacity'),
36576             loadedTiles = context.connection().loadedTiles(),
36577             baseEntities = context.history().graph().base().entities,
36578             introGraph, name;
36579
36580         // Block saving
36581         context.inIntro(true);
36582
36583         // Load semi-real data used in intro
36584         context.connection().toggle(false).flush();
36585         context.history().reset();
36586
36587         introGraph = JSON.parse(iD.introGraph);
36588         for (var key in introGraph) {
36589             introGraph[key] = iD.Entity(introGraph[key]);
36590             name = localizedName(key);
36591             if (name) {
36592                 introGraph[key].tags.name = name;
36593             }
36594         }
36595         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
36596         context.background().bing();
36597
36598         d3.selectAll('#map .layer-background').style('opacity', 1);
36599
36600         var curtain = d3.curtain();
36601         selection.call(curtain);
36602
36603         function reveal(box, text, options) {
36604             options = options || {};
36605             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
36606             else curtain.reveal(box, '', '', options.duration);
36607         }
36608
36609         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
36610             var s = iD.ui.intro[step](context, reveal)
36611                 .on('done', function() {
36612                     entered.filter(function(d) {
36613                         return d.title === s.title;
36614                     }).classed('finished', true);
36615                     enter(steps[i + 1]);
36616                 });
36617             return s;
36618         });
36619
36620         steps[steps.length - 1].on('startEditing', function() {
36621             curtain.remove();
36622             navwrap.remove();
36623             d3.selectAll('#map .layer-background').style('opacity', opacity);
36624             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
36625             context.history().reset().merge(d3.values(baseEntities));
36626             context.background().baseLayerSource(background);
36627             if (history) context.history().fromJSON(history, false);
36628             context.map().centerZoom(center, zoom);
36629             window.location.replace(hash);
36630             context.inIntro(false);
36631         });
36632
36633         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
36634
36635         var buttonwrap = navwrap.append('div')
36636             .attr('class', 'joined')
36637             .selectAll('button.step');
36638
36639         var entered = buttonwrap
36640             .data(steps)
36641             .enter()
36642             .append('button')
36643             .attr('class', 'step')
36644             .on('click', enter);
36645
36646         entered
36647             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
36648
36649         entered
36650             .append('label')
36651             .text(function(d) { return t(d.title); });
36652
36653         enter(steps[0]);
36654
36655         function enter (newStep) {
36656             if (step) { step.exit(); }
36657
36658             context.enter(iD.modes.Browse(context));
36659
36660             step = newStep;
36661             step.enter();
36662
36663             entered.classed('active', function(d) {
36664                 return d.title === step.title;
36665             });
36666         }
36667
36668     }
36669     return intro;
36670 };
36671
36672 iD.ui.intro.pointBox = function(point, context) {
36673     var rect = context.surfaceRect();
36674     point = context.projection(point);
36675     return {
36676         left: point[0] + rect.left - 30,
36677         top: point[1] + rect.top - 50,
36678         width: 60,
36679         height: 70
36680     };
36681 };
36682
36683 iD.ui.intro.pad = function(box, padding, context) {
36684     if (box instanceof Array) {
36685         var rect = context.surfaceRect();
36686         box = context.projection(box);
36687         box = {
36688             left: box[0] + rect.left,
36689             top: box[1] + rect.top
36690         };
36691     }
36692     return {
36693         left: box.left - padding,
36694         top: box.top - padding,
36695         width: (box.width || 0) + 2 * padding,
36696         height: (box.width || 0) + 2 * padding
36697     };
36698 };
36699
36700 iD.ui.intro.icon = function(name, svgklass) {
36701     return '<svg class="icon ' + (svgklass || '') + '">' +
36702         '<use xlink:href="' + name + '"></use></svg>';
36703 };
36704 iD.ui.Lasso = function(context) {
36705     var group, polygon;
36706
36707     lasso.coordinates = [];
36708
36709     function lasso(selection) {
36710
36711         context.container().classed('lasso', true);
36712
36713         group = selection.append('g')
36714             .attr('class', 'lasso hide');
36715
36716         polygon = group.append('path')
36717             .attr('class', 'lasso-path');
36718
36719         group.call(iD.ui.Toggle(true));
36720
36721     }
36722
36723     function draw() {
36724         if (polygon) {
36725             polygon.data([lasso.coordinates])
36726                 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
36727         }
36728     }
36729
36730     lasso.extent = function () {
36731         return lasso.coordinates.reduce(function(extent, point) {
36732             return extent.extend(iD.geo.Extent(point));
36733         }, iD.geo.Extent());
36734     };
36735
36736     lasso.p = function(_) {
36737         if (!arguments.length) return lasso;
36738         lasso.coordinates.push(_);
36739         draw();
36740         return lasso;
36741     };
36742
36743     lasso.close = function() {
36744         if (group) {
36745             group.call(iD.ui.Toggle(false, function() {
36746                 d3.select(this).remove();
36747             }));
36748         }
36749         context.container().classed('lasso', false);
36750     };
36751
36752     return lasso;
36753 };
36754 iD.ui.Loading = function(context) {
36755     var message = '',
36756         blocking = false,
36757         modal;
36758
36759     var loading = function(selection) {
36760         modal = iD.ui.modal(selection, blocking);
36761
36762         var loadertext = modal.select('.content')
36763             .classed('loading-modal', true)
36764             .append('div')
36765             .attr('class', 'modal-section fillL');
36766
36767         loadertext.append('img')
36768             .attr('class', 'loader')
36769             .attr('src', context.imagePath('loader-white.gif'));
36770
36771         loadertext.append('h3')
36772             .text(message);
36773
36774         modal.select('button.close')
36775             .attr('class', 'hide');
36776
36777         return loading;
36778     };
36779
36780     loading.message = function(_) {
36781         if (!arguments.length) return message;
36782         message = _;
36783         return loading;
36784     };
36785
36786     loading.blocking = function(_) {
36787         if (!arguments.length) return blocking;
36788         blocking = _;
36789         return loading;
36790     };
36791
36792     loading.close = function() {
36793         modal.remove();
36794     };
36795
36796     return loading;
36797 };
36798 iD.ui.MapData = function(context) {
36799     var key = 'F',
36800         features = context.features().keys(),
36801         layers = context.layers(),
36802         fills = ['wireframe', 'partial', 'full'],
36803         fillDefault = context.storage('area-fill') || 'partial',
36804         fillSelected = fillDefault;
36805
36806
36807     function map_data(selection) {
36808
36809         function showsFeature(d) {
36810             return context.features().enabled(d);
36811         }
36812
36813         function autoHiddenFeature(d) {
36814             return context.features().autoHidden(d);
36815         }
36816
36817         function clickFeature(d) {
36818             context.features().toggle(d);
36819             update();
36820         }
36821
36822         function showsFill(d) {
36823             return fillSelected === d;
36824         }
36825
36826         function setFill(d) {
36827             _.each(fills, function(opt) {
36828                 context.surface().classed('fill-' + opt, Boolean(opt === d));
36829             });
36830
36831             fillSelected = d;
36832             if (d !== 'wireframe') {
36833                 fillDefault = d;
36834                 context.storage('area-fill', d);
36835             }
36836             update();
36837         }
36838
36839         function showsLayer(which) {
36840             var layer = layers.layer(which);
36841             if (layer) {
36842                 return layer.enabled();
36843             }
36844             return false;
36845         }
36846
36847         function setLayer(which, enabled) {
36848             var layer = layers.layer(which);
36849             if (layer) {
36850                 layer.enabled(enabled);
36851                 update();
36852             }
36853         }
36854
36855         function toggleLayer(which) {
36856             setLayer(which, !showsLayer(which));
36857         }
36858
36859         function clickGpx() {
36860             toggleLayer('gpx');
36861         }
36862
36863         function clickMapillaryImages() {
36864             toggleLayer('mapillary-images');
36865             if (!showsLayer('mapillary-images')) {
36866                 setLayer('mapillary-signs', false);
36867             }
36868         }
36869
36870         function clickMapillarySigns() {
36871             toggleLayer('mapillary-signs');
36872         }
36873
36874
36875         function drawMapillaryItems(selection) {
36876             var mapillaryImages = layers.layer('mapillary-images'),
36877                 mapillarySigns = layers.layer('mapillary-signs'),
36878                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
36879                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
36880                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
36881                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
36882
36883             var mapillaryList = selection
36884                 .selectAll('.layer-list-mapillary')
36885                 .data([0]);
36886
36887             // Enter
36888             mapillaryList
36889                 .enter()
36890                 .append('ul')
36891                 .attr('class', 'layer-list layer-list-mapillary');
36892
36893             var mapillaryImageLayerItem = mapillaryList
36894                 .selectAll('.list-item-mapillary-images')
36895                 .data(supportsMapillaryImages ? [0] : []);
36896
36897             var enterImages = mapillaryImageLayerItem.enter()
36898                 .append('li')
36899                 .attr('class', 'list-item-mapillary-images');
36900
36901             var labelImages = enterImages.append('label')
36902                 .call(bootstrap.tooltip()
36903                     .title(t('mapillary_images.tooltip'))
36904                     .placement('top'));
36905
36906             labelImages.append('input')
36907                 .attr('type', 'checkbox')
36908                 .on('change', clickMapillaryImages);
36909
36910             labelImages.append('span')
36911                 .text(t('mapillary_images.title'));
36912
36913
36914             var mapillarySignLayerItem = mapillaryList
36915                 .selectAll('.list-item-mapillary-signs')
36916                 .data(supportsMapillarySigns ? [0] : []);
36917
36918             var enterSigns = mapillarySignLayerItem.enter()
36919                 .append('li')
36920                 .attr('class', 'list-item-mapillary-signs');
36921
36922             var labelSigns = enterSigns.append('label')
36923                 .call(bootstrap.tooltip()
36924                     .title(t('mapillary_signs.tooltip'))
36925                     .placement('top'));
36926
36927             labelSigns.append('input')
36928                 .attr('type', 'checkbox')
36929                 .on('change', clickMapillarySigns);
36930
36931             labelSigns.append('span')
36932                 .text(t('mapillary_signs.title'));
36933
36934             // Update
36935             mapillaryImageLayerItem
36936                 .classed('active', showsMapillaryImages)
36937                 .selectAll('input')
36938                 .property('checked', showsMapillaryImages);
36939
36940             mapillarySignLayerItem
36941                 .classed('active', showsMapillarySigns)
36942                 .selectAll('input')
36943                 .property('disabled', !showsMapillaryImages)
36944                 .property('checked', showsMapillarySigns);
36945
36946             mapillarySignLayerItem
36947                 .selectAll('label')
36948                 .classed('deemphasize', !showsMapillaryImages);
36949
36950             // Exit
36951             mapillaryImageLayerItem.exit()
36952                 .remove();
36953             mapillarySignLayerItem.exit()
36954                 .remove();
36955         }
36956
36957
36958         function drawGpxItem(selection) {
36959             var gpx = layers.layer('gpx'),
36960                 hasGpx = gpx && gpx.hasGpx(),
36961                 showsGpx = hasGpx && gpx.enabled();
36962
36963             var gpxLayerItem = selection
36964                 .selectAll('.layer-list-gpx')
36965                 .data(gpx ? [0] : []);
36966
36967             // Enter
36968             var enter = gpxLayerItem.enter()
36969                 .append('ul')
36970                 .attr('class', 'layer-list layer-list-gpx')
36971                 .append('li')
36972                 .classed('list-item-gpx', true);
36973
36974             enter.append('button')
36975                 .attr('class', 'list-item-gpx-extent')
36976                 .call(bootstrap.tooltip()
36977                     .title(t('gpx.zoom'))
36978                     .placement('left'))
36979                 .on('click', function() {
36980                     d3.event.preventDefault();
36981                     d3.event.stopPropagation();
36982                     gpx.fitZoom();
36983                 })
36984                 .call(iD.svg.Icon('#icon-search'));
36985
36986             enter.append('button')
36987                 .attr('class', 'list-item-gpx-browse')
36988                 .call(bootstrap.tooltip()
36989                     .title(t('gpx.browse'))
36990                     .placement('left'))
36991                 .on('click', function() {
36992                     d3.select(document.createElement('input'))
36993                         .attr('type', 'file')
36994                         .on('change', function() {
36995                             gpx.files(d3.event.target.files);
36996                         })
36997                         .node().click();
36998                 })
36999                 .call(iD.svg.Icon('#icon-geolocate'));
37000
37001             var labelGpx = enter.append('label')
37002                 .call(bootstrap.tooltip()
37003                     .title(t('gpx.drag_drop'))
37004                     .placement('top'));
37005
37006             labelGpx.append('input')
37007                 .attr('type', 'checkbox')
37008                 .on('change', clickGpx);
37009
37010             labelGpx.append('span')
37011                 .text(t('gpx.local_layer'));
37012
37013             // Update
37014             gpxLayerItem
37015                 .classed('active', showsGpx)
37016                 .selectAll('input')
37017                 .property('disabled', !hasGpx)
37018                 .property('checked', showsGpx);
37019
37020             gpxLayerItem
37021                 .selectAll('label')
37022                 .classed('deemphasize', !hasGpx);
37023
37024             // Exit
37025             gpxLayerItem.exit()
37026                 .remove();
37027         }
37028
37029
37030         function drawList(selection, data, type, name, change, active) {
37031             var items = selection.selectAll('li')
37032                 .data(data);
37033
37034             // Enter
37035             var enter = items.enter()
37036                 .append('li')
37037                 .attr('class', 'layer')
37038                 .call(bootstrap.tooltip()
37039                     .html(true)
37040                     .title(function(d) {
37041                         var tip = t(name + '.' + d + '.tooltip'),
37042                             key = (d === 'wireframe' ? 'W' : null);
37043
37044                         if (name === 'feature' && autoHiddenFeature(d)) {
37045                             tip += '<div>' + t('map_data.autohidden') + '</div>';
37046                         }
37047                         return iD.ui.tooltipHtml(tip, key);
37048                     })
37049                     .placement('top')
37050                 );
37051
37052             var label = enter.append('label');
37053
37054             label.append('input')
37055                 .attr('type', type)
37056                 .attr('name', name)
37057                 .on('change', change);
37058
37059             label.append('span')
37060                 .text(function(d) { return t(name + '.' + d + '.description'); });
37061
37062             // Update
37063             items
37064                 .classed('active', active)
37065                 .selectAll('input')
37066                 .property('checked', active)
37067                 .property('indeterminate', function(d) {
37068                     return (name === 'feature' && autoHiddenFeature(d));
37069                 });
37070
37071             // Exit
37072             items.exit()
37073                 .remove();
37074         }
37075
37076
37077         function update() {
37078             dataLayerContainer.call(drawMapillaryItems);
37079             dataLayerContainer.call(drawGpxItem);
37080
37081             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
37082
37083             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
37084         }
37085
37086         function hidePanel() {
37087             setVisible(false);
37088         }
37089
37090         function togglePanel() {
37091             if (d3.event) d3.event.preventDefault();
37092             tooltip.hide(button);
37093             setVisible(!button.classed('active'));
37094         }
37095
37096         function toggleWireframe() {
37097             if (d3.event) {
37098                 d3.event.preventDefault();
37099                 d3.event.stopPropagation();
37100             }
37101             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
37102             context.map().pan([0,0]);  // trigger a redraw
37103         }
37104
37105         function setVisible(show) {
37106             if (show !== shown) {
37107                 button.classed('active', show);
37108                 shown = show;
37109
37110                 if (show) {
37111                     update();
37112                     selection.on('mousedown.map_data-inside', function() {
37113                         return d3.event.stopPropagation();
37114                     });
37115                     content.style('display', 'block')
37116                         .style('right', '-300px')
37117                         .transition()
37118                         .duration(200)
37119                         .style('right', '0px');
37120                 } else {
37121                     content.style('display', 'block')
37122                         .style('right', '0px')
37123                         .transition()
37124                         .duration(200)
37125                         .style('right', '-300px')
37126                         .each('end', function() {
37127                             d3.select(this).style('display', 'none');
37128                         });
37129                     selection.on('mousedown.map_data-inside', null);
37130                 }
37131             }
37132         }
37133
37134
37135         var content = selection.append('div')
37136                 .attr('class', 'fillL map-overlay col3 content hide'),
37137             tooltip = bootstrap.tooltip()
37138                 .placement('left')
37139                 .html(true)
37140                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
37141             button = selection.append('button')
37142                 .attr('tabindex', -1)
37143                 .on('click', togglePanel)
37144                 .call(iD.svg.Icon('#icon-data', 'light'))
37145                 .call(tooltip),
37146             shown = false;
37147
37148         content.append('h4')
37149             .text(t('map_data.title'));
37150
37151
37152         // data layers
37153         content.append('a')
37154             .text(t('map_data.data_layers'))
37155             .attr('href', '#')
37156             .classed('hide-toggle', true)
37157             .classed('expanded', true)
37158             .on('click', function() {
37159                 var exp = d3.select(this).classed('expanded');
37160                 dataLayerContainer.style('display', exp ? 'none' : 'block');
37161                 d3.select(this).classed('expanded', !exp);
37162                 d3.event.preventDefault();
37163             });
37164
37165         var dataLayerContainer = content.append('div')
37166             .attr('class', 'data-data-layers')
37167             .style('display', 'block');
37168
37169
37170         // area fills
37171         content.append('a')
37172             .text(t('map_data.fill_area'))
37173             .attr('href', '#')
37174             .classed('hide-toggle', true)
37175             .classed('expanded', false)
37176             .on('click', function() {
37177                 var exp = d3.select(this).classed('expanded');
37178                 fillContainer.style('display', exp ? 'none' : 'block');
37179                 d3.select(this).classed('expanded', !exp);
37180                 d3.event.preventDefault();
37181             });
37182
37183         var fillContainer = content.append('div')
37184             .attr('class', 'data-area-fills')
37185             .style('display', 'none');
37186
37187         var fillList = fillContainer.append('ul')
37188             .attr('class', 'layer-list layer-fill-list');
37189
37190
37191         // feature filters
37192         content.append('a')
37193             .text(t('map_data.map_features'))
37194             .attr('href', '#')
37195             .classed('hide-toggle', true)
37196             .classed('expanded', false)
37197             .on('click', function() {
37198                 var exp = d3.select(this).classed('expanded');
37199                 featureContainer.style('display', exp ? 'none' : 'block');
37200                 d3.select(this).classed('expanded', !exp);
37201                 d3.event.preventDefault();
37202             });
37203
37204         var featureContainer = content.append('div')
37205             .attr('class', 'data-feature-filters')
37206             .style('display', 'none');
37207
37208         var featureList = featureContainer.append('ul')
37209             .attr('class', 'layer-list layer-feature-list');
37210
37211
37212         context.features()
37213             .on('change.map_data-update', update);
37214
37215         setFill(fillDefault);
37216
37217         var keybinding = d3.keybinding('features')
37218             .on(key, togglePanel)
37219             .on('W', toggleWireframe)
37220             .on('B', hidePanel)
37221             .on('H', hidePanel);
37222
37223         d3.select(document)
37224             .call(keybinding);
37225
37226         context.surface().on('mousedown.map_data-outside', hidePanel);
37227         context.container().on('mousedown.map_data-outside', hidePanel);
37228     }
37229
37230     return map_data;
37231 };
37232 iD.ui.MapInMap = function(context) {
37233     var key = '/';
37234
37235     function map_in_map(selection) {
37236         var backgroundLayer = iD.TileLayer(context),
37237             overlayLayers = {},
37238             projection = iD.geo.RawMercator(),
37239             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
37240             debugLayer = iD.svg.Debug(projection, context),
37241             zoom = d3.behavior.zoom()
37242                 .scaleExtent([ztok(0.5), ztok(24)])
37243                 .on('zoom', zoomPan),
37244             transformed = false,
37245             panning = false,
37246             hidden = true,
37247             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
37248             tStart, tLast, tCurr, kLast, kCurr, tiles, viewport, timeoutId;
37249
37250         function ztok(z) { return 256 * Math.pow(2, z); }
37251         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
37252
37253
37254         function startMouse() {
37255             context.surface().on('mouseup.map-in-map-outside', endMouse);
37256             context.container().on('mouseup.map-in-map-outside', endMouse);
37257
37258             tStart = tLast = tCurr = projection.translate();
37259             panning = true;
37260         }
37261
37262
37263         function zoomPan() {
37264             var e = d3.event.sourceEvent,
37265                 t = d3.event.translate,
37266                 k = d3.event.scale,
37267                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
37268                 zMini = ktoz(k);
37269
37270             // restrict minimap zoom to < (main zoom - 3)
37271             if (zMini > zMain - 3) {
37272                 zMini = zMain - 3;
37273                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
37274                 return;
37275             }
37276
37277             tCurr = t;
37278             kCurr = k;
37279             zDiff = zMain - zMini;
37280
37281             var scale = kCurr / kLast,
37282                 tX = (tCurr[0] / scale - tLast[0]) * scale,
37283                 tY = (tCurr[1] / scale - tLast[1]) * scale;
37284
37285             iD.util.setTransform(tiles, tX, tY, scale);
37286             iD.util.setTransform(viewport, 0, 0, scale);
37287             transformed = true;
37288
37289             queueRedraw();
37290
37291             e.preventDefault();
37292             e.stopPropagation();
37293         }
37294
37295
37296         function endMouse() {
37297             context.surface().on('mouseup.map-in-map-outside', null);
37298             context.container().on('mouseup.map-in-map-outside', null);
37299
37300             updateProjection();
37301             panning = false;
37302
37303             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
37304                 var dMini = wrap.dimensions(),
37305                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
37306
37307                 context.map().center(projection.invert(cMini));
37308             }
37309         }
37310
37311
37312         function updateProjection() {
37313             var loc = context.map().center(),
37314                 dMini = wrap.dimensions(),
37315                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
37316                 tMain = context.projection.translate(),
37317                 kMain = context.projection.scale(),
37318                 zMain = ktoz(kMain * 2 * Math.PI),
37319                 zMini = Math.max(zMain - zDiff, 0.5),
37320                 kMini = ztok(zMini);
37321
37322             projection
37323                 .translate(tMain)
37324                 .scale(kMini / (2 * Math.PI));
37325
37326             var s = projection(loc),
37327                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
37328                 tMini = [
37329                     cMini[0] - s[0] + tMain[0] + mouse[0],
37330                     cMini[1] - s[1] + tMain[1] + mouse[1]
37331                 ];
37332
37333             projection
37334                 .translate(tMini)
37335                 .clipExtent([[0, 0], dMini]);
37336
37337             zoom
37338                 .center(cMini)
37339                 .translate(tMini)
37340                 .scale(kMini);
37341
37342             tLast = tCurr = tMini;
37343             kLast = kCurr = kMini;
37344
37345             if (transformed) {
37346                 iD.util.setTransform(tiles, 0, 0);
37347                 iD.util.setTransform(viewport, 0, 0);
37348                 transformed = false;
37349             }
37350         }
37351
37352
37353         function redraw() {
37354             if (hidden) return;
37355
37356             updateProjection();
37357
37358             var dMini = wrap.dimensions(),
37359                 zMini = ktoz(projection.scale() * 2 * Math.PI);
37360
37361             // setup tile container
37362             tiles = wrap
37363                 .selectAll('.map-in-map-tiles')
37364                 .data([0]);
37365
37366             tiles
37367                 .enter()
37368                 .append('div')
37369                 .attr('class', 'map-in-map-tiles');
37370
37371             // redraw background
37372             backgroundLayer
37373                 .source(context.background().baseLayerSource())
37374                 .projection(projection)
37375                 .dimensions(dMini);
37376
37377             var background = tiles
37378                 .selectAll('.map-in-map-background')
37379                 .data([0]);
37380
37381             background.enter()
37382                 .append('div')
37383                 .attr('class', 'map-in-map-background');
37384
37385             background
37386                 .call(backgroundLayer);
37387
37388
37389             // redraw overlay
37390             var overlaySources = context.background().overlayLayerSources();
37391             var activeOverlayLayers = [];
37392             for (var i = 0; i < overlaySources.length; i++) {
37393                 if (overlaySources[i].validZoom(zMini)) {
37394                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer(context);
37395                     activeOverlayLayers.push(overlayLayers[i]
37396                         .source(overlaySources[i])
37397                         .projection(projection)
37398                         .dimensions(dMini));
37399                 }
37400             }
37401
37402             var overlay = tiles
37403                 .selectAll('.map-in-map-overlay')
37404                 .data([0]);
37405
37406             overlay.enter()
37407                 .append('div')
37408                 .attr('class', 'map-in-map-overlay');
37409
37410             var overlays = overlay
37411                 .selectAll('div')
37412                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
37413
37414             overlays.enter().append('div');
37415             overlays.each(function(layer) {
37416                 d3.select(this).call(layer);
37417             });
37418
37419             overlays.exit()
37420                 .remove();
37421
37422
37423             var dataLayers = tiles
37424                 .selectAll('.map-in-map-data')
37425                 .data([0]);
37426
37427             dataLayers.enter()
37428                 .append('svg')
37429                 .attr('class', 'map-in-map-data');
37430
37431             dataLayers.exit()
37432                 .remove();
37433
37434             dataLayers
37435                 .call(gpxLayer)
37436                 .call(debugLayer);
37437
37438
37439             // redraw viewport bounding box
37440             if (!panning) {
37441                 var getPath = d3.geo.path().projection(projection),
37442                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
37443
37444                 viewport = wrap.selectAll('.map-in-map-viewport')
37445                     .data([0]);
37446
37447                 viewport.enter()
37448                     .append('svg')
37449                     .attr('class', 'map-in-map-viewport');
37450
37451                 var path = viewport.selectAll('.map-in-map-bbox')
37452                     .data([bbox]);
37453
37454                 path.enter()
37455                     .append('path')
37456                     .attr('class', 'map-in-map-bbox');
37457
37458                 path
37459                     .attr('d', getPath)
37460                     .classed('thick', function(d) { return getPath.area(d) < 30; });
37461             }
37462         }
37463
37464
37465         function queueRedraw() {
37466             clearTimeout(timeoutId);
37467             timeoutId = setTimeout(function() { redraw(); }, 300);
37468         }
37469
37470
37471         function toggle() {
37472             if (d3.event) d3.event.preventDefault();
37473
37474             hidden = !hidden;
37475
37476             var label = d3.select('.minimap-toggle');
37477             label.classed('active', !hidden)
37478                 .select('input').property('checked', !hidden);
37479
37480             if (hidden) {
37481                 wrap
37482                     .style('display', 'block')
37483                     .style('opacity', 1)
37484                     .transition()
37485                     .duration(200)
37486                     .style('opacity', 0)
37487                     .each('end', function() {
37488                         d3.select(this).style('display', 'none');
37489                     });
37490             } else {
37491                 wrap
37492                     .style('display', 'block')
37493                     .style('opacity', 0)
37494                     .transition()
37495                     .duration(200)
37496                     .style('opacity', 1);
37497
37498                 redraw();
37499             }
37500         }
37501
37502         iD.ui.MapInMap.toggle = toggle;
37503
37504         var wrap = selection.selectAll('.map-in-map')
37505             .data([0]);
37506
37507         wrap.enter()
37508             .append('div')
37509             .attr('class', 'map-in-map')
37510             .style('display', (hidden ? 'none' : 'block'))
37511             .on('mousedown.map-in-map', startMouse)
37512             .on('mouseup.map-in-map', endMouse)
37513             .call(zoom)
37514             .on('dblclick.zoom', null);
37515
37516         context.map()
37517             .on('drawn.map-in-map', function(drawn) {
37518                 if (drawn.full === true) redraw();
37519             });
37520
37521         redraw();
37522
37523         var keybinding = d3.keybinding('map-in-map')
37524             .on(key, toggle);
37525
37526         d3.select(document)
37527             .call(keybinding);
37528     }
37529
37530     return map_in_map;
37531 };
37532 iD.ui.modal = function(selection, blocking) {
37533     var keybinding = d3.keybinding('modal');
37534     var previous = selection.select('div.modal');
37535     var animate = previous.empty();
37536
37537     previous.transition()
37538         .duration(200)
37539         .style('opacity', 0)
37540         .remove();
37541
37542     var shaded = selection
37543         .append('div')
37544         .attr('class', 'shaded')
37545         .style('opacity', 0);
37546
37547     shaded.close = function() {
37548         shaded
37549             .transition()
37550             .duration(200)
37551             .style('opacity',0)
37552             .remove();
37553         modal
37554             .transition()
37555             .duration(200)
37556             .style('top','0px');
37557
37558         keybinding.off();
37559     };
37560
37561
37562     var modal = shaded.append('div')
37563         .attr('class', 'modal fillL col6');
37564
37565     if (!blocking) {
37566         shaded.on('click.remove-modal', function() {
37567             if (d3.event.target === this) {
37568                 shaded.close();
37569             }
37570         });
37571
37572         modal.append('button')
37573             .attr('class', 'close')
37574             .on('click', shaded.close)
37575             .call(iD.svg.Icon('#icon-close'));
37576
37577         keybinding
37578             .on('⌫', shaded.close)
37579             .on('⎋', shaded.close);
37580
37581         d3.select(document).call(keybinding);
37582     }
37583
37584     modal.append('div')
37585         .attr('class', 'content');
37586
37587     if (animate) {
37588         shaded.transition().style('opacity', 1);
37589     } else {
37590         shaded.style('opacity', 1);
37591     }
37592
37593     return shaded;
37594 };
37595 iD.ui.Modes = function(context) {
37596     var modes = [
37597         iD.modes.AddPoint(context),
37598         iD.modes.AddLine(context),
37599         iD.modes.AddArea(context)];
37600
37601     function editable() {
37602         return context.editable() && context.mode().id !== 'save';
37603     }
37604
37605     return function(selection) {
37606         var buttons = selection.selectAll('button.add-button')
37607             .data(modes);
37608
37609        buttons.enter().append('button')
37610            .attr('tabindex', -1)
37611            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
37612            .on('click.mode-buttons', function(mode) {
37613                if (mode.id === context.mode().id) {
37614                    context.enter(iD.modes.Browse(context));
37615                } else {
37616                    context.enter(mode);
37617                }
37618            })
37619            .call(bootstrap.tooltip()
37620                .placement('bottom')
37621                .html(true)
37622                .title(function(mode) {
37623                    return iD.ui.tooltipHtml(mode.description, mode.key);
37624                }));
37625
37626         context.map()
37627             .on('move.modes', _.debounce(update, 500));
37628
37629         context
37630             .on('enter.modes', update);
37631
37632         buttons.each(function(d) {
37633             d3.select(this)
37634                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
37635         });
37636
37637         buttons.append('span')
37638             .attr('class', 'label')
37639             .text(function(mode) { return mode.title; });
37640
37641         context.on('enter.editor', function(entered) {
37642             buttons.classed('active', function(mode) { return entered.button === mode.button; });
37643             context.container()
37644                 .classed('mode-' + entered.id, true);
37645         });
37646
37647         context.on('exit.editor', function(exited) {
37648             context.container()
37649                 .classed('mode-' + exited.id, false);
37650         });
37651
37652         var keybinding = d3.keybinding('mode-buttons');
37653
37654         modes.forEach(function(m) {
37655             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
37656         });
37657
37658         d3.select(document)
37659             .call(keybinding);
37660
37661         function update() {
37662             buttons.property('disabled', !editable());
37663         }
37664     };
37665 };
37666 iD.ui.Notice = function(context) {
37667     return function(selection) {
37668         var div = selection.append('div')
37669             .attr('class', 'notice');
37670
37671         var button = div.append('button')
37672             .attr('class', 'zoom-to notice')
37673             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
37674
37675         button
37676             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
37677             .append('span')
37678             .attr('class', 'label')
37679             .text(t('zoom_in_edit'));
37680
37681         function disableTooHigh() {
37682             div.style('display', context.editable() ? 'none' : 'block');
37683         }
37684
37685         context.map()
37686             .on('move.notice', _.debounce(disableTooHigh, 500));
37687
37688         disableTooHigh();
37689     };
37690 };
37691 iD.ui.PresetIcon = function() {
37692     var preset, geometry;
37693
37694     function presetIcon(selection) {
37695         selection.each(render);
37696     }
37697
37698     function render() {
37699         var selection = d3.select(this),
37700             p = preset.apply(this, arguments),
37701             geom = geometry.apply(this, arguments),
37702             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
37703             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
37704
37705         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
37706
37707         function tag_classes(p) {
37708             var s = '';
37709             for (var i in p.tags) {
37710                 s += ' tag-' + i;
37711                 if (p.tags[i] !== '*') {
37712                     s += ' tag-' + i + '-' + p.tags[i];
37713                 }
37714             }
37715             return s;
37716         }
37717
37718         var $fill = selection.selectAll('.preset-icon-fill')
37719             .data([0]);
37720
37721         $fill.enter().append('div');
37722
37723         $fill.attr('class', function() {
37724             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
37725         });
37726
37727         var $frame = selection.selectAll('.preset-icon-frame')
37728             .data([0]);
37729
37730         $frame.enter()
37731             .append('div')
37732             .call(iD.svg.Icon('#preset-icon-frame'));
37733
37734         $frame.attr('class', function() {
37735             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
37736         });
37737
37738
37739         var $icon = selection.selectAll('.preset-icon')
37740             .data([0]);
37741
37742         $icon.enter()
37743             .append('div')
37744             .attr('class', 'preset-icon')
37745             .call(iD.svg.Icon(''));
37746
37747         $icon
37748             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
37749
37750         $icon.selectAll('svg')
37751             .attr('class', function() {
37752                 return 'icon ' + icon + tag_classes(p);
37753             });
37754
37755         $icon.selectAll('use')       // workaround: maki parking-24 broken?
37756             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
37757     }
37758
37759     presetIcon.preset = function(_) {
37760         if (!arguments.length) return preset;
37761         preset = d3.functor(_);
37762         return presetIcon;
37763     };
37764
37765     presetIcon.geometry = function(_) {
37766         if (!arguments.length) return geometry;
37767         geometry = d3.functor(_);
37768         return presetIcon;
37769     };
37770
37771     return presetIcon;
37772 };
37773 iD.ui.preset = function(context) {
37774     var event = d3.dispatch('change'),
37775         state,
37776         fields,
37777         preset,
37778         tags,
37779         id;
37780
37781     function UIField(field, entity, show) {
37782         field = _.clone(field);
37783
37784         field.input = iD.ui.preset[field.type](field, context)
37785             .on('change', event.change);
37786
37787         if (field.input.entity) field.input.entity(entity);
37788
37789         field.keys = field.keys || [field.key];
37790
37791         field.show = show;
37792
37793         field.shown = function() {
37794             return field.id === 'name' || field.show || _.some(field.keys, function(key) { return !!tags[key]; });
37795         };
37796
37797         field.modified = function() {
37798             var original = context.graph().base().entities[entity.id];
37799             return _.some(field.keys, function(key) {
37800                 return original ? tags[key] !== original.tags[key] : tags[key];
37801             });
37802         };
37803
37804         field.revert = function() {
37805             var original = context.graph().base().entities[entity.id],
37806                 t = {};
37807             field.keys.forEach(function(key) {
37808                 t[key] = original ? original.tags[key] : undefined;
37809             });
37810             return t;
37811         };
37812
37813         field.present = function() {
37814             return _.some(field.keys, function(key) {
37815                 return tags[key];
37816             });
37817         };
37818
37819         field.remove = function() {
37820             var t = {};
37821             field.keys.forEach(function(key) {
37822                 t[key] = undefined;
37823             });
37824             return t;
37825         };
37826
37827         return field;
37828     }
37829
37830     function fieldKey(field) {
37831         return field.id;
37832     }
37833
37834     function presets(selection) {
37835         selection.call(iD.ui.Disclosure()
37836             .title(t('inspector.all_fields'))
37837             .expanded(context.storage('preset_fields.expanded') !== 'false')
37838             .on('toggled', toggled)
37839             .content(content));
37840
37841         function toggled(expanded) {
37842             context.storage('preset_fields.expanded', expanded);
37843         }
37844     }
37845
37846     function content(selection) {
37847         if (!fields) {
37848             var entity = context.entity(id),
37849                 geometry = context.geometry(id);
37850
37851             fields = [UIField(context.presets().field('name'), entity)];
37852
37853             preset.fields.forEach(function(field) {
37854                 if (field.matchGeometry(geometry)) {
37855                     fields.push(UIField(field, entity, true));
37856                 }
37857             });
37858
37859             if (entity.isHighwayIntersection(context.graph())) {
37860                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
37861             }
37862
37863             context.presets().universal().forEach(function(field) {
37864                 if (preset.fields.indexOf(field) < 0) {
37865                     fields.push(UIField(field, entity));
37866                 }
37867             });
37868         }
37869
37870         var shown = fields.filter(function(field) { return field.shown(); }),
37871             notShown = fields.filter(function(field) { return !field.shown(); });
37872
37873         var $form = selection.selectAll('.preset-form')
37874             .data([0]);
37875
37876         $form.enter().append('div')
37877             .attr('class', 'preset-form inspector-inner fillL3');
37878
37879         var $fields = $form.selectAll('.form-field')
37880             .data(shown, fieldKey);
37881
37882         // Enter
37883
37884         var $enter = $fields.enter()
37885             .append('div')
37886             .attr('class', function(field) {
37887                 return 'form-field form-field-' + field.id;
37888             });
37889
37890         var $label = $enter.append('label')
37891             .attr('class', 'form-label')
37892             .attr('for', function(field) { return 'preset-input-' + field.id; })
37893             .text(function(field) { return field.label(); });
37894
37895         var wrap = $label.append('div')
37896             .attr('class', 'form-label-button-wrap');
37897
37898         wrap.append('button')
37899             .attr('class', 'remove-icon')
37900             .attr('tabindex', -1)
37901             .call(iD.svg.Icon('#operation-delete'));
37902
37903         wrap.append('button')
37904             .attr('class', 'modified-icon')
37905             .attr('tabindex', -1)
37906             .call(iD.svg.Icon('#icon-undo'));
37907
37908         // Update
37909
37910         $fields.select('.form-label-button-wrap .remove-icon')
37911             .on('click', remove);
37912
37913         $fields.select('.modified-icon')
37914             .on('click', revert);
37915
37916         $fields
37917             .order()
37918             .classed('modified', function(field) {
37919                 return field.modified();
37920             })
37921             .classed('present', function(field) {
37922                 return field.present();
37923             })
37924             .each(function(field) {
37925                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
37926
37927                 if (state === 'hover') {
37928                     reference.showing(false);
37929                 }
37930
37931                 d3.select(this)
37932                     .call(field.input)
37933                     .selectAll('input')
37934                     .on('keydown', function() {
37935                         // if user presses enter, and combobox is not active, accept edits..
37936                         if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
37937                             context.enter(iD.modes.Browse(context));
37938                         }
37939                     })
37940                     .call(reference.body)
37941                     .select('.form-label-button-wrap')
37942                     .call(reference.button);
37943
37944                 field.input.tags(tags);
37945             });
37946
37947         $fields.exit()
37948             .remove();
37949
37950         notShown = notShown.map(function(field) {
37951             return {
37952                 title: field.label(),
37953                 value: field.label(),
37954                 field: field
37955             };
37956         });
37957
37958         var $more = selection.selectAll('.more-fields')
37959             .data((notShown.length > 0) ? [0] : []);
37960
37961         $more.enter().append('div')
37962             .attr('class', 'more-fields')
37963             .append('label')
37964                 .text(t('inspector.add_fields'));
37965
37966         var $input = $more.selectAll('.value')
37967             .data([0]);
37968
37969         $input.enter().append('input')
37970             .attr('class', 'value')
37971             .attr('type', 'text');
37972
37973         $input.value('')
37974             .attr('placeholder', function() {
37975                 var placeholder = [];
37976                 for (var field in notShown) {
37977                     placeholder.push(notShown[field].title);
37978                 }
37979                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
37980             })
37981             .call(d3.combobox().data(notShown)
37982                 .minItems(1)
37983                 .on('accept', show));
37984
37985         $more.exit()
37986             .remove();
37987
37988         $input.exit()
37989             .remove();
37990
37991         function show(field) {
37992             field = field.field;
37993             field.show = true;
37994             content(selection);
37995             field.input.focus();
37996         }
37997
37998         function revert(field) {
37999             d3.event.stopPropagation();
38000             d3.event.preventDefault();
38001             event.change(field.revert());
38002         }
38003
38004         function remove(field) {
38005             d3.event.stopPropagation();
38006             d3.event.preventDefault();
38007             event.change(field.remove());
38008         }
38009     }
38010
38011     presets.preset = function(_) {
38012         if (!arguments.length) return preset;
38013         if (preset && preset.id === _.id) return presets;
38014         preset = _;
38015         fields = null;
38016         return presets;
38017     };
38018
38019     presets.state = function(_) {
38020         if (!arguments.length) return state;
38021         state = _;
38022         return presets;
38023     };
38024
38025     presets.tags = function(_) {
38026         if (!arguments.length) return tags;
38027         tags = _;
38028         // Don't reset fields here.
38029         return presets;
38030     };
38031
38032     presets.entityID = function(_) {
38033         if (!arguments.length) return id;
38034         if (id === _) return presets;
38035         id = _;
38036         fields = null;
38037         return presets;
38038     };
38039
38040     return d3.rebind(presets, event, 'on');
38041 };
38042 iD.ui.PresetList = function(context) {
38043     var dispatch = d3.dispatch('choose'),
38044         id,
38045         currentPreset,
38046         autofocus = false;
38047
38048     function presetList(selection) {
38049         var entity = context.entity(id),
38050             geometry = context.geometry(id);
38051
38052         // Treat entities on addr:interpolation lines as points, not vertices (#3241)
38053         if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
38054             geometry = 'point';
38055         }
38056
38057         var presets = context.presets().matchGeometry(geometry);
38058
38059         selection.html('');
38060
38061         var messagewrap = selection.append('div')
38062             .attr('class', 'header fillL cf');
38063
38064         var message = messagewrap.append('h3')
38065             .text(t('inspector.choose'));
38066
38067         if (context.entity(id).isUsed(context.graph())) {
38068             messagewrap.append('button')
38069                 .attr('class', 'preset-choose')
38070                 .on('click', function() { dispatch.choose(currentPreset); })
38071                 .append('span')
38072                 .html('&#9658;');
38073         } else {
38074             messagewrap.append('button')
38075                 .attr('class', 'close')
38076                 .on('click', function() {
38077                     context.enter(iD.modes.Browse(context));
38078                 })
38079                 .call(iD.svg.Icon('#icon-close'));
38080         }
38081
38082         function keydown() {
38083             // hack to let delete shortcut work when search is autofocused
38084             if (search.property('value').length === 0 &&
38085                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
38086                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
38087                 d3.event.preventDefault();
38088                 d3.event.stopPropagation();
38089                 iD.operations.Delete([id], context)();
38090             } else if (search.property('value').length === 0 &&
38091                 (d3.event.ctrlKey || d3.event.metaKey) &&
38092                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
38093                 d3.event.preventDefault();
38094                 d3.event.stopPropagation();
38095                 context.undo();
38096             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
38097                 d3.select(this).on('keydown', null);
38098             }
38099         }
38100
38101         function keypress() {
38102             // enter
38103             var value = search.property('value');
38104             if (d3.event.keyCode === 13 && value.length) {
38105                 list.selectAll('.preset-list-item:first-child').datum().choose();
38106             }
38107         }
38108
38109         function inputevent() {
38110             var value = search.property('value');
38111             list.classed('filtered', value.length);
38112             if (value.length) {
38113                 var results = presets.search(value, geometry);
38114                 message.text(t('inspector.results', {
38115                     n: results.collection.length,
38116                     search: value
38117                 }));
38118                 list.call(drawList, results);
38119             } else {
38120                 list.call(drawList, context.presets().defaults(geometry, 36));
38121                 message.text(t('inspector.choose'));
38122             }
38123         }
38124
38125         var searchWrap = selection.append('div')
38126             .attr('class', 'search-header');
38127
38128         var search = searchWrap.append('input')
38129             .attr('class', 'preset-search-input')
38130             .attr('placeholder', t('inspector.search'))
38131             .attr('type', 'search')
38132             .on('keydown', keydown)
38133             .on('keypress', keypress)
38134             .on('input', inputevent);
38135
38136         searchWrap
38137             .call(iD.svg.Icon('#icon-search', 'pre-text'));
38138
38139         if (autofocus) {
38140             search.node().focus();
38141         }
38142
38143         var listWrap = selection.append('div')
38144             .attr('class', 'inspector-body');
38145
38146         var list = listWrap.append('div')
38147             .attr('class', 'preset-list fillL cf')
38148             .call(drawList, context.presets().defaults(geometry, 36));
38149     }
38150
38151     function drawList(list, presets) {
38152         var collection = presets.collection.map(function(preset) {
38153             return preset.members ? CategoryItem(preset) : PresetItem(preset);
38154         });
38155
38156         var items = list.selectAll('.preset-list-item')
38157             .data(collection, function(d) { return d.preset.id; });
38158
38159         items.enter().append('div')
38160             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
38161             .classed('current', function(item) { return item.preset === currentPreset; })
38162             .each(function(item) {
38163                 d3.select(this).call(item);
38164             })
38165             .style('opacity', 0)
38166             .transition()
38167             .style('opacity', 1);
38168
38169         items.order();
38170
38171         items.exit()
38172             .remove();
38173     }
38174
38175     function CategoryItem(preset) {
38176         var box, sublist, shown = false;
38177
38178         function item(selection) {
38179             var wrap = selection.append('div')
38180                 .attr('class', 'preset-list-button-wrap category col12');
38181
38182             wrap.append('button')
38183                 .attr('class', 'preset-list-button')
38184                 .classed('expanded', false)
38185                 .call(iD.ui.PresetIcon()
38186                     .geometry(context.geometry(id))
38187                     .preset(preset))
38188                 .on('click', function() {
38189                     var isExpanded = d3.select(this).classed('expanded');
38190                     var triangle = isExpanded ? '▶ ' :  '▼ ';
38191                     d3.select(this).classed('expanded', !isExpanded);
38192                     d3.select(this).selectAll('.label').text(triangle + preset.name());
38193                     item.choose();
38194                 })
38195                 .append('div')
38196                 .attr('class', 'label')
38197                 .text(function() {
38198                   return '▶ ' + preset.name();
38199                 });
38200
38201             box = selection.append('div')
38202                 .attr('class', 'subgrid col12')
38203                 .style('max-height', '0px')
38204                 .style('opacity', 0);
38205
38206             box.append('div')
38207                 .attr('class', 'arrow');
38208
38209             sublist = box.append('div')
38210                 .attr('class', 'preset-list fillL3 cf fl');
38211         }
38212
38213         item.choose = function() {
38214             if (!box || !sublist) return;
38215
38216             if (shown) {
38217                 shown = false;
38218                 box.transition()
38219                     .duration(200)
38220                     .style('opacity', '0')
38221                     .style('max-height', '0px')
38222                     .style('padding-bottom', '0px');
38223             } else {
38224                 shown = true;
38225                 sublist.call(drawList, preset.members);
38226                 box.transition()
38227                     .duration(200)
38228                     .style('opacity', '1')
38229                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
38230                     .style('padding-bottom', '20px');
38231             }
38232         };
38233
38234         item.preset = preset;
38235
38236         return item;
38237     }
38238
38239     function PresetItem(preset) {
38240         function item(selection) {
38241             var wrap = selection.append('div')
38242                 .attr('class', 'preset-list-button-wrap col12');
38243
38244             wrap.append('button')
38245                 .attr('class', 'preset-list-button')
38246                 .call(iD.ui.PresetIcon()
38247                     .geometry(context.geometry(id))
38248                     .preset(preset))
38249                 .on('click', item.choose)
38250                 .append('div')
38251                 .attr('class', 'label')
38252                 .text(preset.name());
38253
38254             wrap.call(item.reference.button);
38255             selection.call(item.reference.body);
38256         }
38257
38258         item.choose = function() {
38259             context.presets().choose(preset);
38260
38261             context.perform(
38262                 iD.actions.ChangePreset(id, currentPreset, preset),
38263                 t('operations.change_tags.annotation'));
38264
38265             dispatch.choose(preset);
38266         };
38267
38268         item.help = function() {
38269             d3.event.stopPropagation();
38270             item.reference.toggle();
38271         };
38272
38273         item.preset = preset;
38274         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
38275
38276         return item;
38277     }
38278
38279     presetList.autofocus = function(_) {
38280         if (!arguments.length) return autofocus;
38281         autofocus = _;
38282         return presetList;
38283     };
38284
38285     presetList.entityID = function(_) {
38286         if (!arguments.length) return id;
38287         id = _;
38288         presetList.preset(context.presets().match(context.entity(id), context.graph()));
38289         return presetList;
38290     };
38291
38292     presetList.preset = function(_) {
38293         if (!arguments.length) return currentPreset;
38294         currentPreset = _;
38295         return presetList;
38296     };
38297
38298     return d3.rebind(presetList, dispatch, 'on');
38299 };
38300 iD.ui.RadialMenu = function(context, operations) {
38301     var menu,
38302         center = [0, 0],
38303         tooltip;
38304
38305     var radialMenu = function(selection) {
38306         if (!operations.length)
38307             return;
38308
38309         selection.node().parentNode.focus();
38310
38311         function click(operation) {
38312             d3.event.stopPropagation();
38313             if (operation.disabled())
38314                 return;
38315             operation();
38316             radialMenu.close();
38317         }
38318
38319         menu = selection.append('g')
38320             .attr('class', 'radial-menu')
38321             .attr('transform', 'translate(' + center + ')')
38322             .attr('opacity', 0);
38323
38324         menu.transition()
38325             .attr('opacity', 1);
38326
38327         var r = 50,
38328             a = Math.PI / 4,
38329             a0 = -Math.PI / 4,
38330             a1 = a0 + (operations.length - 1) * a;
38331
38332         menu.append('path')
38333             .attr('class', 'radial-menu-background')
38334             .attr('d', 'M' + r * Math.sin(a0) + ',' +
38335                              r * Math.cos(a0) +
38336                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
38337                              (r * Math.sin(a1) + 1e-3) + ',' +
38338                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
38339             .attr('stroke-width', 50)
38340             .attr('stroke-linecap', 'round');
38341
38342         var button = menu.selectAll()
38343             .data(operations)
38344             .enter()
38345             .append('g')
38346             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
38347             .classed('disabled', function(d) { return d.disabled(); })
38348             .attr('transform', function(d, i) {
38349                 return 'translate(' + iD.geo.roundCoords([
38350                         r * Math.sin(a0 + i * a),
38351                         r * Math.cos(a0 + i * a)]).join(',') + ')';
38352             });
38353
38354         button.append('circle')
38355             .attr('r', 15)
38356             .on('click', click)
38357             .on('mousedown', mousedown)
38358             .on('mouseover', mouseover)
38359             .on('mouseout', mouseout);
38360
38361         button.append('use')
38362             .attr('transform', 'translate(-10,-10)')
38363             .attr('width', '20')
38364             .attr('height', '20')
38365             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
38366
38367         tooltip = d3.select(document.body)
38368             .append('div')
38369             .attr('class', 'tooltip-inner radial-menu-tooltip');
38370
38371         function mousedown() {
38372             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
38373         }
38374
38375         function mouseover(d, i) {
38376             var rect = context.surfaceRect(),
38377                 angle = a0 + i * a,
38378                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
38379                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
38380                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
38381                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
38382
38383             tooltip
38384                 .style('top', null)
38385                 .style('left', null)
38386                 .style('bottom', null)
38387                 .style('right', null)
38388                 .style('display', 'block')
38389                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
38390
38391             if (i === 0) {
38392                 tooltip
38393                     .style('right', right)
38394                     .style('top', top);
38395             } else if (i >= 4) {
38396                 tooltip
38397                     .style('left', left)
38398                     .style('bottom', bottom);
38399             } else {
38400                 tooltip
38401                     .style('left', left)
38402                     .style('top', top);
38403             }
38404         }
38405
38406         function mouseout() {
38407             tooltip.style('display', 'none');
38408         }
38409     };
38410
38411     radialMenu.close = function() {
38412         if (menu) {
38413             menu
38414                 .style('pointer-events', 'none')
38415                 .transition()
38416                 .attr('opacity', 0)
38417                 .remove();
38418         }
38419
38420         if (tooltip) {
38421             tooltip.remove();
38422         }
38423     };
38424
38425     radialMenu.center = function(_) {
38426         if (!arguments.length) return center;
38427         center = _;
38428         return radialMenu;
38429     };
38430
38431     return radialMenu;
38432 };
38433 iD.ui.RawMemberEditor = function(context) {
38434     var id;
38435
38436     function selectMember(d) {
38437         d3.event.preventDefault();
38438         context.enter(iD.modes.Select(context, [d.id]));
38439     }
38440
38441     function changeRole(d) {
38442         var role = d3.select(this).property('value');
38443         var member = {id: d.id, type: d.type, role: role};
38444         context.perform(
38445             iD.actions.ChangeMember(d.relation.id, member, d.index),
38446             t('operations.change_role.annotation'));
38447     }
38448
38449     function deleteMember(d) {
38450         context.perform(
38451             iD.actions.DeleteMember(d.relation.id, d.index),
38452             t('operations.delete_member.annotation'));
38453
38454         if (!context.hasEntity(d.relation.id)) {
38455             context.enter(iD.modes.Browse(context));
38456         }
38457     }
38458
38459     function rawMemberEditor(selection) {
38460         var entity = context.entity(id),
38461             memberships = [];
38462
38463         entity.members.forEach(function(member, index) {
38464             memberships.push({
38465                 index: index,
38466                 id: member.id,
38467                 type: member.type,
38468                 role: member.role,
38469                 relation: entity,
38470                 member: context.hasEntity(member.id)
38471             });
38472         });
38473
38474         selection.call(iD.ui.Disclosure()
38475             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
38476             .expanded(true)
38477             .on('toggled', toggled)
38478             .content(content));
38479
38480         function toggled(expanded) {
38481             if (expanded) {
38482                 selection.node().parentNode.scrollTop += 200;
38483             }
38484         }
38485
38486         function content($wrap) {
38487             var $list = $wrap.selectAll('.member-list')
38488                 .data([0]);
38489
38490             $list.enter().append('ul')
38491                 .attr('class', 'member-list');
38492
38493             var $items = $list.selectAll('li')
38494                 .data(memberships, function(d) {
38495                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
38496                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
38497                 });
38498
38499             var $enter = $items.enter().append('li')
38500                 .attr('class', 'member-row form-field')
38501                 .classed('member-incomplete', function(d) { return !d.member; });
38502
38503             $enter.each(function(d) {
38504                 if (d.member) {
38505                     var $label = d3.select(this).append('label')
38506                         .attr('class', 'form-label')
38507                         .append('a')
38508                         .attr('href', '#')
38509                         .on('click', selectMember);
38510
38511                     $label.append('span')
38512                         .attr('class', 'member-entity-type')
38513                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
38514
38515                     $label.append('span')
38516                         .attr('class', 'member-entity-name')
38517                         .text(function(d) { return iD.util.displayName(d.member); });
38518
38519                 } else {
38520                     d3.select(this).append('label')
38521                         .attr('class', 'form-label')
38522                         .text(t('inspector.incomplete'));
38523                 }
38524             });
38525
38526             $enter.append('input')
38527                 .attr('class', 'member-role')
38528                 .property('type', 'text')
38529                 .attr('maxlength', 255)
38530                 .attr('placeholder', t('inspector.role'))
38531                 .property('value', function(d) { return d.role; })
38532                 .on('change', changeRole);
38533
38534             $enter.append('button')
38535                 .attr('tabindex', -1)
38536                 .attr('class', 'remove button-input-action member-delete minor')
38537                 .on('click', deleteMember)
38538                 .call(iD.svg.Icon('#operation-delete'));
38539
38540             $items.exit()
38541                 .remove();
38542         }
38543     }
38544
38545     rawMemberEditor.entityID = function(_) {
38546         if (!arguments.length) return id;
38547         id = _;
38548         return rawMemberEditor;
38549     };
38550
38551     return rawMemberEditor;
38552 };
38553 iD.ui.RawMembershipEditor = function(context) {
38554     var id, showBlank;
38555
38556     function selectRelation(d) {
38557         d3.event.preventDefault();
38558         context.enter(iD.modes.Select(context, [d.relation.id]));
38559     }
38560
38561     function changeRole(d) {
38562         var role = d3.select(this).property('value');
38563         context.perform(
38564             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
38565             t('operations.change_role.annotation'));
38566     }
38567
38568     function addMembership(d, role) {
38569         showBlank = false;
38570
38571         if (d.relation) {
38572             context.perform(
38573                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
38574                 t('operations.add_member.annotation'));
38575
38576         } else {
38577             var relation = iD.Relation();
38578
38579             context.perform(
38580                 iD.actions.AddEntity(relation),
38581                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
38582                 t('operations.add.annotation.relation'));
38583
38584             context.enter(iD.modes.Select(context, [relation.id]));
38585         }
38586     }
38587
38588     function deleteMembership(d) {
38589         context.perform(
38590             iD.actions.DeleteMember(d.relation.id, d.index),
38591             t('operations.delete_member.annotation'));
38592     }
38593
38594     function relations(q) {
38595         var newRelation = {
38596                 relation: null,
38597                 value: t('inspector.new_relation')
38598             },
38599             result = [],
38600             graph = context.graph();
38601
38602         context.intersects(context.extent()).forEach(function(entity) {
38603             if (entity.type !== 'relation' || entity.id === id)
38604                 return;
38605
38606             var presetName = context.presets().match(entity, graph).name(),
38607                 entityName = iD.util.displayName(entity) || '';
38608
38609             var value = presetName + ' ' + entityName;
38610             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
38611                 return;
38612
38613             result.push({
38614                 relation: entity,
38615                 value: value
38616             });
38617         });
38618
38619         result.sort(function(a, b) {
38620             return iD.Relation.creationOrder(a.relation, b.relation);
38621         });
38622
38623         // Dedupe identical names by appending relation id - see #2891
38624         var dupeGroups = _(result)
38625             .groupBy('value')
38626             .filter(function(v) { return v.length > 1; })
38627             .value();
38628
38629         dupeGroups.forEach(function(group) {
38630             group.forEach(function(obj) {
38631                 obj.value += ' ' + obj.relation.id;
38632             });
38633         });
38634
38635         result.unshift(newRelation);
38636
38637         return result;
38638     }
38639
38640     function rawMembershipEditor(selection) {
38641         var entity = context.entity(id),
38642             memberships = [];
38643
38644         context.graph().parentRelations(entity).forEach(function(relation) {
38645             relation.members.forEach(function(member, index) {
38646                 if (member.id === entity.id) {
38647                     memberships.push({relation: relation, member: member, index: index});
38648                 }
38649             });
38650         });
38651
38652         selection.call(iD.ui.Disclosure()
38653             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
38654             .expanded(true)
38655             .on('toggled', toggled)
38656             .content(content));
38657
38658         function toggled(expanded) {
38659             if (expanded) {
38660                 selection.node().parentNode.scrollTop += 200;
38661             }
38662         }
38663
38664         function content($wrap) {
38665             var $list = $wrap.selectAll('.member-list')
38666                 .data([0]);
38667
38668             $list.enter().append('ul')
38669                 .attr('class', 'member-list');
38670
38671             var $items = $list.selectAll('li.member-row-normal')
38672                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
38673
38674             var $enter = $items.enter().append('li')
38675                 .attr('class', 'member-row member-row-normal form-field');
38676
38677             var $label = $enter.append('label')
38678                 .attr('class', 'form-label')
38679                 .append('a')
38680                 .attr('href', '#')
38681                 .on('click', selectRelation);
38682
38683             $label.append('span')
38684                 .attr('class', 'member-entity-type')
38685                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
38686
38687             $label.append('span')
38688                 .attr('class', 'member-entity-name')
38689                 .text(function(d) { return iD.util.displayName(d.relation); });
38690
38691             $enter.append('input')
38692                 .attr('class', 'member-role')
38693                 .property('type', 'text')
38694                 .attr('maxlength', 255)
38695                 .attr('placeholder', t('inspector.role'))
38696                 .property('value', function(d) { return d.member.role; })
38697                 .on('change', changeRole);
38698
38699             $enter.append('button')
38700                 .attr('tabindex', -1)
38701                 .attr('class', 'remove button-input-action member-delete minor')
38702                 .on('click', deleteMembership)
38703                 .call(iD.svg.Icon('#operation-delete'));
38704
38705             $items.exit()
38706                 .remove();
38707
38708             if (showBlank) {
38709                 var $new = $list.selectAll('.member-row-new')
38710                     .data([0]);
38711
38712                 $enter = $new.enter().append('li')
38713                     .attr('class', 'member-row member-row-new form-field');
38714
38715                 $enter.append('input')
38716                     .attr('type', 'text')
38717                     .attr('class', 'member-entity-input')
38718                     .call(d3.combobox()
38719                         .minItems(1)
38720                         .fetcher(function(value, callback) {
38721                             callback(relations(value));
38722                         })
38723                         .on('accept', function(d) {
38724                             addMembership(d, $new.select('.member-role').property('value'));
38725                         }));
38726
38727                 $enter.append('input')
38728                     .attr('class', 'member-role')
38729                     .property('type', 'text')
38730                     .attr('maxlength', 255)
38731                     .attr('placeholder', t('inspector.role'))
38732                     .on('change', changeRole);
38733
38734                 $enter.append('button')
38735                     .attr('tabindex', -1)
38736                     .attr('class', 'remove button-input-action member-delete minor')
38737                     .on('click', deleteMembership)
38738                     .call(iD.svg.Icon('#operation-delete'));
38739
38740             } else {
38741                 $list.selectAll('.member-row-new')
38742                     .remove();
38743             }
38744
38745             var $add = $wrap.selectAll('.add-relation')
38746                 .data([0]);
38747
38748             $add.enter()
38749                 .append('button')
38750                 .attr('class', 'add-relation')
38751                 .call(iD.svg.Icon('#icon-plus', 'light'));
38752
38753             $wrap.selectAll('.add-relation')
38754                 .on('click', function() {
38755                     showBlank = true;
38756                     content($wrap);
38757                     $list.selectAll('.member-entity-input').node().focus();
38758                 });
38759         }
38760     }
38761
38762     rawMembershipEditor.entityID = function(_) {
38763         if (!arguments.length) return id;
38764         id = _;
38765         return rawMembershipEditor;
38766     };
38767
38768     return rawMembershipEditor;
38769 };
38770 iD.ui.RawTagEditor = function(context) {
38771     var event = d3.dispatch('change'),
38772         showBlank = false,
38773         state,
38774         preset,
38775         tags,
38776         id;
38777
38778     function rawTagEditor(selection) {
38779         var count = Object.keys(tags).filter(function(d) { return d; }).length;
38780
38781         selection.call(iD.ui.Disclosure()
38782             .title(t('inspector.all_tags') + ' (' + count + ')')
38783             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
38784             .on('toggled', toggled)
38785             .content(content));
38786
38787         function toggled(expanded) {
38788             context.storage('raw_tag_editor.expanded', expanded);
38789             if (expanded) {
38790                 selection.node().parentNode.scrollTop += 200;
38791             }
38792         }
38793     }
38794
38795     function content($wrap) {
38796         var entries = d3.entries(tags);
38797
38798         if (!entries.length || showBlank) {
38799             showBlank = false;
38800             entries.push({key: '', value: ''});
38801         }
38802
38803         var $list = $wrap.selectAll('.tag-list')
38804             .data([0]);
38805
38806         $list.enter().append('ul')
38807             .attr('class', 'tag-list');
38808
38809         var $newTag = $wrap.selectAll('.add-tag')
38810             .data([0]);
38811
38812         $newTag.enter()
38813             .append('button')
38814             .attr('class', 'add-tag')
38815             .call(iD.svg.Icon('#icon-plus', 'light'));
38816
38817         $newTag.on('click', addTag);
38818
38819         var $items = $list.selectAll('li')
38820             .data(entries, function(d) { return d.key; });
38821
38822         // Enter
38823
38824         var $enter = $items.enter().append('li')
38825             .attr('class', 'tag-row cf');
38826
38827         $enter.append('div')
38828             .attr('class', 'key-wrap')
38829             .append('input')
38830             .property('type', 'text')
38831             .attr('class', 'key')
38832             .attr('maxlength', 255);
38833
38834         $enter.append('div')
38835             .attr('class', 'input-wrap-position')
38836             .append('input')
38837             .property('type', 'text')
38838             .attr('class', 'value')
38839             .attr('maxlength', 255);
38840
38841         $enter.append('button')
38842             .attr('tabindex', -1)
38843             .attr('class', 'remove minor')
38844             .call(iD.svg.Icon('#operation-delete'));
38845
38846         if (context.taginfo()) {
38847             $enter.each(bindTypeahead);
38848         }
38849
38850         // Update
38851
38852         $items.order();
38853
38854         $items.each(function(tag) {
38855             var isRelation = (context.entity(id).type === 'relation'),
38856                 reference;
38857             if (isRelation && tag.key === 'type')
38858                 reference = iD.ui.TagReference({rtype: tag.value}, context);
38859             else
38860                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
38861
38862             if (state === 'hover') {
38863                 reference.showing(false);
38864             }
38865
38866             d3.select(this)
38867                 .call(reference.button)
38868                 .call(reference.body);
38869         });
38870
38871         $items.select('input.key')
38872             .attr('title', function(d) { return d.key; })
38873             .value(function(d) { return d.key; })
38874             .on('blur', keyChange)
38875             .on('change', keyChange);
38876
38877         $items.select('input.value')
38878             .attr('title', function(d) { return d.value; })
38879             .value(function(d) { return d.value; })
38880             .on('blur', valueChange)
38881             .on('change', valueChange)
38882             .on('keydown.push-more', pushMore);
38883
38884         $items.select('button.remove')
38885             .on('click', removeTag);
38886
38887         $items.exit()
38888             .each(unbind)
38889             .remove();
38890
38891         function pushMore() {
38892             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
38893                 $list.selectAll('li:last-child input.value').node() === this) {
38894                 addTag();
38895             }
38896         }
38897
38898         function bindTypeahead() {
38899             var row = d3.select(this),
38900                 key = row.selectAll('input.key'),
38901                 value = row.selectAll('input.value');
38902
38903             function sort(value, data) {
38904                 var sameletter = [],
38905                     other = [];
38906                 for (var i = 0; i < data.length; i++) {
38907                     if (data[i].value.substring(0, value.length) === value) {
38908                         sameletter.push(data[i]);
38909                     } else {
38910                         other.push(data[i]);
38911                     }
38912                 }
38913                 return sameletter.concat(other);
38914             }
38915
38916             key.call(d3.combobox()
38917                 .fetcher(function(value, callback) {
38918                     context.taginfo().keys({
38919                         debounce: true,
38920                         geometry: context.geometry(id),
38921                         query: value
38922                     }, function(err, data) {
38923                         if (!err) callback(sort(value, data));
38924                     });
38925                 }));
38926
38927             value.call(d3.combobox()
38928                 .fetcher(function(value, callback) {
38929                     context.taginfo().values({
38930                         debounce: true,
38931                         key: key.value(),
38932                         geometry: context.geometry(id),
38933                         query: value
38934                     }, function(err, data) {
38935                         if (!err) callback(sort(value, data));
38936                     });
38937                 }));
38938         }
38939
38940         function unbind() {
38941             var row = d3.select(this);
38942
38943             row.selectAll('input.key')
38944                 .call(d3.combobox.off);
38945
38946             row.selectAll('input.value')
38947                 .call(d3.combobox.off);
38948         }
38949
38950         function keyChange(d) {
38951             var kOld = d.key,
38952                 kNew = this.value.trim(),
38953                 tag = {};
38954
38955             if (kNew && kNew !== kOld) {
38956                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
38957                     base = match[1],
38958                     suffix = +(match[2] || 1);
38959                 while (tags[kNew]) {  // rename key if already in use
38960                     kNew = base + '_' + suffix++;
38961                 }
38962             }
38963             tag[kOld] = undefined;
38964             tag[kNew] = d.value;
38965             d.key = kNew; // Maintain DOM identity through the subsequent update.
38966             this.value = kNew;
38967             event.change(tag);
38968         }
38969
38970         function valueChange(d) {
38971             var tag = {};
38972             tag[d.key] = this.value;
38973             event.change(tag);
38974         }
38975
38976         function removeTag(d) {
38977             var tag = {};
38978             tag[d.key] = undefined;
38979             event.change(tag);
38980             d3.select(this.parentNode).remove();
38981         }
38982
38983         function addTag() {
38984             // Wrapped in a setTimeout in case it's being called from a blur
38985             // handler. Without the setTimeout, the call to `content` would
38986             // wipe out the pending value change.
38987             setTimeout(function() {
38988                 showBlank = true;
38989                 content($wrap);
38990                 $list.selectAll('li:last-child input.key').node().focus();
38991             }, 0);
38992         }
38993     }
38994
38995     rawTagEditor.state = function(_) {
38996         if (!arguments.length) return state;
38997         state = _;
38998         return rawTagEditor;
38999     };
39000
39001     rawTagEditor.preset = function(_) {
39002         if (!arguments.length) return preset;
39003         preset = _;
39004         return rawTagEditor;
39005     };
39006
39007     rawTagEditor.tags = function(_) {
39008         if (!arguments.length) return tags;
39009         tags = _;
39010         return rawTagEditor;
39011     };
39012
39013     rawTagEditor.entityID = function(_) {
39014         if (!arguments.length) return id;
39015         id = _;
39016         return rawTagEditor;
39017     };
39018
39019     return d3.rebind(rawTagEditor, event, 'on');
39020 };
39021 iD.ui.Restore = function(context) {
39022     return function(selection) {
39023         if (!context.history().lock() || !context.history().restorableChanges())
39024             return;
39025
39026         var modal = iD.ui.modal(selection, true);
39027
39028         modal.select('.modal')
39029             .attr('class', 'modal fillL col6');
39030
39031         var introModal = modal.select('.content');
39032
39033         introModal.attr('class','cf');
39034
39035         introModal.append('div')
39036             .attr('class', 'modal-section')
39037             .append('h3')
39038             .text(t('restore.heading'));
39039
39040         introModal.append('div')
39041             .attr('class','modal-section')
39042             .append('p')
39043             .text(t('restore.description'));
39044
39045         var buttonWrap = introModal.append('div')
39046             .attr('class', 'modal-actions cf');
39047
39048         var restore = buttonWrap.append('button')
39049             .attr('class', 'restore col6')
39050             .text(t('restore.restore'))
39051             .on('click', function() {
39052                 context.history().restore();
39053                 modal.remove();
39054             });
39055
39056         buttonWrap.append('button')
39057             .attr('class', 'reset col6')
39058             .text(t('restore.reset'))
39059             .on('click', function() {
39060                 context.history().clearSaved();
39061                 modal.remove();
39062             });
39063
39064         restore.node().focus();
39065     };
39066 };
39067 iD.ui.Save = function(context) {
39068     var history = context.history(),
39069         key = iD.ui.cmd('⌘S');
39070
39071
39072     function saving() {
39073         return context.mode().id === 'save';
39074     }
39075
39076     function save() {
39077         d3.event.preventDefault();
39078         if (!context.inIntro() && !saving() && history.hasChanges()) {
39079             context.enter(iD.modes.Save(context));
39080         }
39081     }
39082
39083     function getBackground(numChanges) {
39084         var step;
39085         if (numChanges === 0) {
39086             return null;
39087         } else if (numChanges <= 50) {
39088             step = numChanges / 50;
39089             return d3.interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
39090         } else {
39091             step = Math.min((numChanges - 50) / 50, 1.0);
39092             return d3.interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
39093         }
39094     }
39095
39096     return function(selection) {
39097         var tooltip = bootstrap.tooltip()
39098             .placement('bottom')
39099             .html(true)
39100             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
39101
39102         var button = selection.append('button')
39103             .attr('class', 'save col12 disabled')
39104             .attr('tabindex', -1)
39105             .on('click', save)
39106             .call(tooltip);
39107
39108         button.append('span')
39109             .attr('class', 'label')
39110             .text(t('save.title'));
39111
39112         button.append('span')
39113             .attr('class', 'count')
39114             .text('0');
39115
39116         var keybinding = d3.keybinding('undo-redo')
39117             .on(key, save, true);
39118
39119         d3.select(document)
39120             .call(keybinding);
39121
39122         var numChanges = 0;
39123
39124         context.history().on('change.save', function() {
39125             var _ = history.difference().summary().length;
39126             if (_ === numChanges)
39127                 return;
39128             numChanges = _;
39129
39130             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
39131                     'save.help' : 'save.no_changes'), key));
39132
39133             var background = getBackground(numChanges);
39134
39135             button
39136                 .classed('disabled', numChanges === 0)
39137                 .classed('has-count', numChanges > 0)
39138                 .style('background', background);
39139
39140             button.select('span.count')
39141                 .text(numChanges)
39142                 .style('background', background)
39143                 .style('border-color', background);
39144         });
39145
39146         context.on('enter.save', function() {
39147             button.property('disabled', saving());
39148             if (saving()) button.call(tooltip.hide);
39149         });
39150     };
39151 };
39152 iD.ui.Scale = function(context) {
39153     var projection = context.projection,
39154         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
39155         maxLength = 180,
39156         tickHeight = 8;
39157
39158     function scaleDefs(loc1, loc2) {
39159         var lat = (loc2[1] + loc1[1]) / 2,
39160             conversion = (imperial ? 3.28084 : 1),
39161             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
39162             scale = { dist: 0, px: 0, text: '' },
39163             buckets, i, val, dLon;
39164
39165         if (imperial) {
39166             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
39167         } else {
39168             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
39169         }
39170
39171         // determine a user-friendly endpoint for the scale
39172         for (i = 0; i < buckets.length; i++) {
39173             val = buckets[i];
39174             if (dist >= val) {
39175                 scale.dist = Math.floor(dist / val) * val;
39176                 break;
39177             }
39178         }
39179
39180         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
39181         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
39182
39183         if (imperial) {
39184             if (scale.dist >= 5280) {
39185                 scale.dist /= 5280;
39186                 scale.text = String(scale.dist) + ' mi';
39187             } else {
39188                 scale.text = String(scale.dist) + ' ft';
39189             }
39190         } else {
39191             if (scale.dist >= 1000) {
39192                 scale.dist /= 1000;
39193                 scale.text = String(scale.dist) + ' km';
39194             } else {
39195                 scale.text = String(scale.dist) + ' m';
39196             }
39197         }
39198
39199         return scale;
39200     }
39201
39202     function update(selection) {
39203         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
39204         var dims = context.map().dimensions(),
39205             loc1 = projection.invert([0, dims[1]]),
39206             loc2 = projection.invert([maxLength, dims[1]]),
39207             scale = scaleDefs(loc1, loc2);
39208
39209         selection.select('#scalepath')
39210             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
39211
39212         selection.select('#scaletext')
39213             .attr('x', scale.px + 8)
39214             .attr('y', tickHeight)
39215             .text(scale.text);
39216     }
39217
39218
39219     return function(selection) {
39220         function switchUnits() {
39221             imperial = !imperial;
39222             selection.call(update);
39223         }
39224
39225         var g = selection.append('svg')
39226             .attr('id', 'scale')
39227             .on('click', switchUnits)
39228             .append('g')
39229             .attr('transform', 'translate(10,11)');
39230
39231         g.append('path').attr('id', 'scalepath');
39232         g.append('text').attr('id', 'scaletext');
39233
39234         selection.call(update);
39235
39236         context.map().on('move.scale', function() {
39237             update(selection);
39238         });
39239     };
39240 };
39241 iD.ui.SelectionList = function(context, selectedIDs) {
39242
39243     function selectEntity(entity) {
39244         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
39245     }
39246
39247
39248     function selectionList(selection) {
39249         selection.classed('selection-list-pane', true);
39250
39251         var header = selection.append('div')
39252             .attr('class', 'header fillL cf');
39253
39254         header.append('h3')
39255             .text(t('inspector.multiselect'));
39256
39257         var listWrap = selection.append('div')
39258             .attr('class', 'inspector-body');
39259
39260         var list = listWrap.append('div')
39261             .attr('class', 'feature-list cf');
39262
39263         context.history().on('change.selection-list', drawList);
39264         drawList();
39265
39266         function drawList() {
39267             var entities = selectedIDs
39268                 .map(function(id) { return context.hasEntity(id); })
39269                 .filter(function(entity) { return entity; });
39270
39271             var items = list.selectAll('.feature-list-item')
39272                 .data(entities, iD.Entity.key);
39273
39274             var enter = items.enter().append('button')
39275                 .attr('class', 'feature-list-item')
39276                 .on('click', selectEntity);
39277
39278             // Enter
39279             var label = enter.append('div')
39280                 .attr('class', 'label')
39281                 .call(iD.svg.Icon('', 'pre-text'));
39282
39283             label.append('span')
39284                 .attr('class', 'entity-type');
39285
39286             label.append('span')
39287                 .attr('class', 'entity-name');
39288
39289             // Update
39290             items.selectAll('use')
39291                 .attr('href', function() {
39292                     var entity = this.parentNode.parentNode.__data__;
39293                     return '#icon-' + context.geometry(entity.id);
39294                 });
39295
39296             items.selectAll('.entity-type')
39297                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
39298
39299             items.selectAll('.entity-name')
39300                 .text(function(entity) { return iD.util.displayName(entity); });
39301
39302             // Exit
39303             items.exit()
39304                 .remove();
39305         }
39306     }
39307
39308     return selectionList;
39309
39310 };
39311 iD.ui.Sidebar = function(context) {
39312     var inspector = iD.ui.Inspector(context),
39313         current;
39314
39315     function sidebar(selection) {
39316         var featureListWrap = selection.append('div')
39317             .attr('class', 'feature-list-pane')
39318             .call(iD.ui.FeatureList(context));
39319
39320         selection.call(iD.ui.Notice(context));
39321
39322         var inspectorWrap = selection.append('div')
39323             .attr('class', 'inspector-hidden inspector-wrap fr');
39324
39325         function hover(id) {
39326             if (!current && context.hasEntity(id)) {
39327                 featureListWrap.classed('inspector-hidden', true);
39328                 inspectorWrap.classed('inspector-hidden', false)
39329                     .classed('inspector-hover', true);
39330
39331                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
39332                     inspector
39333                         .state('hover')
39334                         .entityID(id);
39335
39336                     inspectorWrap.call(inspector);
39337                 }
39338             } else if (!current) {
39339                 featureListWrap.classed('inspector-hidden', false);
39340                 inspectorWrap.classed('inspector-hidden', true);
39341                 inspector.state('hide');
39342             }
39343         }
39344
39345         sidebar.hover = _.throttle(hover, 200);
39346
39347         sidebar.select = function(id, newFeature) {
39348             if (!current && id) {
39349                 featureListWrap.classed('inspector-hidden', true);
39350                 inspectorWrap.classed('inspector-hidden', false)
39351                     .classed('inspector-hover', false);
39352
39353                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
39354                     inspector
39355                         .state('select')
39356                         .entityID(id)
39357                         .newFeature(newFeature);
39358
39359                     inspectorWrap.call(inspector);
39360                 }
39361             } else if (!current) {
39362                 featureListWrap.classed('inspector-hidden', false);
39363                 inspectorWrap.classed('inspector-hidden', true);
39364                 inspector.state('hide');
39365             }
39366         };
39367
39368         sidebar.show = function(component) {
39369             featureListWrap.classed('inspector-hidden', true);
39370             inspectorWrap.classed('inspector-hidden', true);
39371             if (current) current.remove();
39372             current = selection.append('div')
39373                 .attr('class', 'sidebar-component')
39374                 .call(component);
39375         };
39376
39377         sidebar.hide = function() {
39378             featureListWrap.classed('inspector-hidden', false);
39379             inspectorWrap.classed('inspector-hidden', true);
39380             if (current) current.remove();
39381             current = null;
39382         };
39383     }
39384
39385     sidebar.hover = function() {};
39386     sidebar.hover.cancel = function() {};
39387     sidebar.select = function() {};
39388     sidebar.show = function() {};
39389     sidebar.hide = function() {};
39390
39391     return sidebar;
39392 };
39393 iD.ui.SourceSwitch = function(context) {
39394     var keys;
39395
39396     function click() {
39397         d3.event.preventDefault();
39398
39399         if (context.history().hasChanges() &&
39400             !window.confirm(t('source_switch.lose_changes'))) return;
39401
39402         var live = d3.select(this)
39403             .classed('live');
39404
39405         context.connection()
39406             .switch(live ? keys[1] : keys[0]);
39407
39408         context.enter(iD.modes.Browse(context));
39409         context.flush();
39410
39411         d3.select(this)
39412             .text(live ? t('source_switch.dev') : t('source_switch.live'))
39413             .classed('live', !live);
39414     }
39415
39416     var sourceSwitch = function(selection) {
39417         selection.append('a')
39418             .attr('href', '#')
39419             .text(t('source_switch.live'))
39420             .classed('live', true)
39421             .attr('tabindex', -1)
39422             .on('click', click);
39423     };
39424
39425     sourceSwitch.keys = function(_) {
39426         if (!arguments.length) return keys;
39427         keys = _;
39428         return sourceSwitch;
39429     };
39430
39431     return sourceSwitch;
39432 };
39433 iD.ui.Spinner = function(context) {
39434     var connection = context.connection();
39435
39436     return function(selection) {
39437         var img = selection.append('img')
39438             .attr('src', context.imagePath('loader-black.gif'))
39439             .style('opacity', 0);
39440
39441         connection.on('loading.spinner', function() {
39442             img.transition()
39443                 .style('opacity', 1);
39444         });
39445
39446         connection.on('loaded.spinner', function() {
39447             img.transition()
39448                 .style('opacity', 0);
39449         });
39450     };
39451 };
39452 iD.ui.Splash = function(context) {
39453     return function(selection) {
39454         if (context.storage('sawSplash'))
39455              return;
39456
39457         context.storage('sawSplash', true);
39458
39459         var modal = iD.ui.modal(selection);
39460
39461         modal.select('.modal')
39462             .attr('class', 'modal-splash modal col6');
39463
39464         var introModal = modal.select('.content')
39465             .append('div')
39466             .attr('class', 'fillL');
39467
39468         introModal.append('div')
39469             .attr('class','modal-section cf')
39470             .append('h3').text(t('splash.welcome'));
39471
39472         introModal.append('div')
39473             .attr('class','modal-section')
39474             .append('p')
39475             .html(t('splash.text', {
39476                 version: iD.version,
39477                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
39478                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
39479             }));
39480
39481         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
39482
39483         buttons.append('button')
39484             .attr('class', 'col6 walkthrough')
39485             .text(t('splash.walkthrough'))
39486             .on('click', function() {
39487                 d3.select(document.body).call(iD.ui.intro(context));
39488                 modal.close();
39489             });
39490
39491         buttons.append('button')
39492             .attr('class', 'col6 start')
39493             .text(t('splash.start'))
39494             .on('click', modal.close);
39495
39496         modal.select('button.close').attr('class','hide');
39497
39498     };
39499 };
39500 iD.ui.Status = function(context) {
39501     var connection = context.connection(),
39502         errCount = 0;
39503
39504     return function(selection) {
39505
39506         function update() {
39507
39508             connection.status(function(err, apiStatus) {
39509
39510                 selection.html('');
39511
39512                 if (err && errCount++ < 2) return;
39513
39514                 if (err) {
39515                     selection.text(t('status.error'));
39516
39517                 } else if (apiStatus === 'readonly') {
39518                     selection.text(t('status.readonly'));
39519
39520                 } else if (apiStatus === 'offline') {
39521                     selection.text(t('status.offline'));
39522                 }
39523
39524                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
39525                 if (!err) errCount = 0;
39526
39527             });
39528         }
39529
39530         connection.on('auth', function() { update(selection); });
39531         window.setInterval(update, 90000);
39532         update(selection);
39533     };
39534 };
39535 iD.ui.Success = function(context) {
39536     var dispatch = d3.dispatch('cancel'),
39537         changeset;
39538
39539     function success(selection) {
39540         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
39541             ' ' + context.connection().changesetURL(changeset.id);
39542
39543         var header = selection.append('div')
39544             .attr('class', 'header fillL');
39545
39546         header.append('button')
39547             .attr('class', 'fr')
39548             .on('click', function() { dispatch.cancel(); })
39549             .call(iD.svg.Icon('#icon-close'));
39550
39551         header.append('h3')
39552             .text(t('success.just_edited'));
39553
39554         var body = selection.append('div')
39555             .attr('class', 'body save-success fillL');
39556
39557         body.append('p')
39558             .html(t('success.help_html'));
39559
39560         body.append('a')
39561             .attr('class', 'details')
39562             .attr('target', '_blank')
39563             .attr('tabindex', -1)
39564             .call(iD.svg.Icon('#icon-out-link', 'inline'))
39565             .attr('href', t('success.help_link_url'))
39566             .append('span')
39567             .text(t('success.help_link_text'));
39568
39569         var changesetURL = context.connection().changesetURL(changeset.id);
39570
39571         body.append('a')
39572             .attr('class', 'button col12 osm')
39573             .attr('target', '_blank')
39574             .attr('href', changesetURL)
39575             .text(t('success.view_on_osm'));
39576
39577         var sharing = {
39578             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
39579             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
39580             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
39581         };
39582
39583         body.selectAll('.button.social')
39584             .data(d3.entries(sharing))
39585             .enter()
39586             .append('a')
39587             .attr('class', 'button social col4')
39588             .attr('target', '_blank')
39589             .attr('href', function(d) { return d.value; })
39590             .call(bootstrap.tooltip()
39591                 .title(function(d) { return t('success.' + d.key); })
39592                 .placement('bottom'))
39593             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
39594     }
39595
39596     success.changeset = function(_) {
39597         if (!arguments.length) return changeset;
39598         changeset = _;
39599         return success;
39600     };
39601
39602     return d3.rebind(success, dispatch, 'on');
39603 };
39604 iD.ui.TagReference = function(tag, context) {
39605     var tagReference = {},
39606         button,
39607         body,
39608         loaded,
39609         showing;
39610
39611     function findLocal(data) {
39612         var locale = iD.detect().locale.toLowerCase(),
39613             localized;
39614
39615         localized = _.find(data, function(d) {
39616             return d.lang.toLowerCase() === locale;
39617         });
39618         if (localized) return localized;
39619
39620         // try the non-regional version of a language, like
39621         // 'en' if the language is 'en-US'
39622         if (locale.indexOf('-') !== -1) {
39623             var first = locale.split('-')[0];
39624             localized = _.find(data, function(d) {
39625                 return d.lang.toLowerCase() === first;
39626             });
39627             if (localized) return localized;
39628         }
39629
39630         // finally fall back to english
39631         return _.find(data, function(d) {
39632             return d.lang.toLowerCase() === 'en';
39633         });
39634     }
39635
39636     function load(param) {
39637         button.classed('tag-reference-loading', true);
39638
39639         context.taginfo().docs(param, function show(err, data) {
39640             var docs;
39641             if (!err && data) {
39642                 docs = findLocal(data);
39643             }
39644
39645             body.html('');
39646
39647             if (!docs || !docs.description) {
39648                 if (param.hasOwnProperty('value')) {
39649                     load(_.omit(param, 'value'));   // retry with key only
39650                 } else {
39651                     body.append('p').text(t('inspector.no_documentation_key'));
39652                     done();
39653                 }
39654                 return;
39655             }
39656
39657             if (docs.image && docs.image.thumb_url_prefix) {
39658                 body
39659                     .append('img')
39660                     .attr('class', 'wiki-image')
39661                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
39662                     .on('load', function() { done(); })
39663                     .on('error', function() { d3.select(this).remove(); done(); });
39664             } else {
39665                 done();
39666             }
39667
39668             body
39669                 .append('p')
39670                 .text(docs.description);
39671
39672             body
39673                 .append('a')
39674                 .attr('target', '_blank')
39675                 .attr('tabindex', -1)
39676                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
39677                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
39678                 .append('span')
39679                 .text(t('inspector.reference'));
39680         });
39681     }
39682
39683     function done() {
39684         loaded = true;
39685
39686         button.classed('tag-reference-loading', false);
39687
39688         body.transition()
39689             .duration(200)
39690             .style('max-height', '200px')
39691             .style('opacity', '1');
39692
39693         showing = true;
39694     }
39695
39696     function hide(selection) {
39697         selection = selection || body.transition().duration(200);
39698
39699         selection
39700             .style('max-height', '0px')
39701             .style('opacity', '0');
39702
39703         showing = false;
39704     }
39705
39706     tagReference.button = function(selection) {
39707         button = selection.selectAll('.tag-reference-button')
39708             .data([0]);
39709
39710         button.enter()
39711             .append('button')
39712             .attr('class', 'tag-reference-button')
39713             .attr('tabindex', -1)
39714             .call(iD.svg.Icon('#icon-inspect'));
39715
39716         button.on('click', function () {
39717             d3.event.stopPropagation();
39718             d3.event.preventDefault();
39719             if (showing) {
39720                 hide();
39721             } else if (loaded) {
39722                 done();
39723             } else {
39724                 if (context.taginfo()) {
39725                     load(tag);
39726                 }
39727             }
39728         });
39729     };
39730
39731     tagReference.body = function(selection) {
39732         body = selection.selectAll('.tag-reference-body')
39733             .data([0]);
39734
39735         body.enter().append('div')
39736             .attr('class', 'tag-reference-body cf')
39737             .style('max-height', '0')
39738             .style('opacity', '0');
39739
39740         if (showing === false) {
39741             hide(body);
39742         }
39743     };
39744
39745     tagReference.showing = function(_) {
39746         if (!arguments.length) return showing;
39747         showing = _;
39748         return tagReference;
39749     };
39750
39751     return tagReference;
39752 };
39753 // toggles the visibility of ui elements, using a combination of the
39754 // hide class, which sets display=none, and a d3 transition for opacity.
39755 // this will cause blinking when called repeatedly, so check that the
39756 // value actually changes between calls.
39757 iD.ui.Toggle = function(show, callback) {
39758     return function(selection) {
39759         selection
39760             .style('opacity', show ? 0 : 1)
39761             .classed('hide', false)
39762             .transition()
39763             .style('opacity', show ? 1 : 0)
39764             .each('end', function() {
39765                 d3.select(this)
39766                     .classed('hide', !show)
39767                     .style('opacity', null);
39768                 if (callback) callback.apply(this);
39769             });
39770     };
39771 };
39772 iD.ui.UndoRedo = function(context) {
39773     var commands = [{
39774         id: 'undo',
39775         cmd: iD.ui.cmd('⌘Z'),
39776         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
39777         annotation: function() { return context.history().undoAnnotation(); }
39778     }, {
39779         id: 'redo',
39780         cmd: iD.ui.cmd('⌘⇧Z'),
39781         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
39782         annotation: function() { return context.history().redoAnnotation(); }
39783     }];
39784
39785     function saving() {
39786         return context.mode().id === 'save';
39787     }
39788
39789     return function(selection) {
39790         var tooltip = bootstrap.tooltip()
39791             .placement('bottom')
39792             .html(true)
39793             .title(function (d) {
39794                 return iD.ui.tooltipHtml(d.annotation() ?
39795                     t(d.id + '.tooltip', {action: d.annotation()}) :
39796                     t(d.id + '.nothing'), d.cmd);
39797             });
39798
39799         var buttons = selection.selectAll('button')
39800             .data(commands)
39801             .enter().append('button')
39802             .attr('class', 'col6 disabled')
39803             .on('click', function(d) { return d.action(); })
39804             .call(tooltip);
39805
39806         buttons.each(function(d) {
39807             d3.select(this)
39808                 .call(iD.svg.Icon('#icon-' + d.id));
39809         });
39810
39811         var keybinding = d3.keybinding('undo')
39812             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
39813             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
39814
39815         d3.select(document)
39816             .call(keybinding);
39817
39818         context.history()
39819             .on('change.undo_redo', update);
39820
39821         context
39822             .on('enter.undo_redo', update);
39823
39824         function update() {
39825             buttons
39826                 .property('disabled', saving())
39827                 .classed('disabled', function(d) { return !d.annotation(); })
39828                 .each(function() {
39829                     var selection = d3.select(this);
39830                     if (selection.property('tooltipVisible')) {
39831                         selection.call(tooltip.show);
39832                     }
39833                 });
39834         }
39835     };
39836 };
39837 iD.ui.ViewOnOSM = function(context) {
39838     var id;
39839
39840     function viewOnOSM(selection) {
39841         var entity = context.entity(id);
39842
39843         selection.style('display', entity.isNew() ? 'none' : null);
39844
39845         var $link = selection.selectAll('.view-on-osm')
39846             .data([0]);
39847
39848         $link.enter()
39849             .append('a')
39850             .attr('class', 'view-on-osm')
39851             .attr('target', '_blank')
39852             .call(iD.svg.Icon('#icon-out-link', 'inline'))
39853             .append('span')
39854             .text(t('inspector.view_on_osm'));
39855
39856         $link
39857             .attr('href', context.connection().entityURL(entity));
39858     }
39859
39860     viewOnOSM.entityID = function(_) {
39861         if (!arguments.length) return id;
39862         id = _;
39863         return viewOnOSM;
39864     };
39865
39866     return viewOnOSM;
39867 };
39868 iD.ui.Zoom = function(context) {
39869     var zooms = [{
39870         id: 'zoom-in',
39871         icon: 'plus',
39872         title: t('zoom.in'),
39873         action: context.zoomIn,
39874         key: '+'
39875     }, {
39876         id: 'zoom-out',
39877         icon: 'minus',
39878         title: t('zoom.out'),
39879         action: context.zoomOut,
39880         key: '-'
39881     }];
39882
39883     function zoomIn() {
39884         d3.event.preventDefault();
39885         if (!context.inIntro()) context.zoomIn();
39886     }
39887
39888     function zoomOut() {
39889         d3.event.preventDefault();
39890         if (!context.inIntro()) context.zoomOut();
39891     }
39892
39893     function zoomInFurther() {
39894         d3.event.preventDefault();
39895         if (!context.inIntro()) context.zoomInFurther();
39896     }
39897
39898     function zoomOutFurther() {
39899         d3.event.preventDefault();
39900         if (!context.inIntro()) context.zoomOutFurther();
39901     }
39902
39903
39904     return function(selection) {
39905         var button = selection.selectAll('button')
39906             .data(zooms)
39907             .enter().append('button')
39908             .attr('tabindex', -1)
39909             .attr('class', function(d) { return d.id; })
39910             .on('click.editor', function(d) { d.action(); })
39911             .call(bootstrap.tooltip()
39912                 .placement('left')
39913                 .html(true)
39914                 .title(function(d) {
39915                     return iD.ui.tooltipHtml(d.title, d.key);
39916                 }));
39917
39918         button.each(function(d) {
39919             d3.select(this)
39920                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
39921         });
39922
39923         var keybinding = d3.keybinding('zoom');
39924
39925         _.each(['=','ffequals','plus','ffplus'], function(key) {
39926             keybinding.on(key, zoomIn);
39927             keybinding.on('⇧' + key, zoomIn);
39928             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
39929             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
39930         });
39931         _.each(['-','ffminus','_','dash'], function(key) {
39932             keybinding.on(key, zoomOut);
39933             keybinding.on('⇧' + key, zoomOut);
39934             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
39935             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
39936         });
39937
39938         d3.select(document)
39939             .call(keybinding);
39940     };
39941 };
39942 iD.ui.preset.access = function(field) {
39943     var dispatch = d3.dispatch('change'),
39944         items;
39945
39946     function access(selection) {
39947         var wrap = selection.selectAll('.preset-input-wrap')
39948             .data([0]);
39949
39950         wrap.enter().append('div')
39951             .attr('class', 'cf preset-input-wrap')
39952             .append('ul');
39953
39954         items = wrap.select('ul').selectAll('li')
39955             .data(field.keys);
39956
39957         // Enter
39958
39959         var enter = items.enter().append('li')
39960             .attr('class', function(d) { return 'cf preset-access-' + d; });
39961
39962         enter.append('span')
39963             .attr('class', 'col6 label preset-label-access')
39964             .attr('for', function(d) { return 'preset-input-access-' + d; })
39965             .text(function(d) { return field.t('types.' + d); });
39966
39967         enter.append('div')
39968             .attr('class', 'col6 preset-input-access-wrap')
39969             .append('input')
39970             .attr('type', 'text')
39971             .attr('class', 'preset-input-access')
39972             .attr('id', function(d) { return 'preset-input-access-' + d; })
39973             .each(function(d) {
39974                 d3.select(this)
39975                     .call(d3.combobox()
39976                         .data(access.options(d)));
39977             });
39978
39979         // Update
39980
39981         wrap.selectAll('.preset-input-access')
39982             .on('change', change)
39983             .on('blur', change);
39984     }
39985
39986     function change(d) {
39987         var tag = {};
39988         tag[d] = d3.select(this).value() || undefined;
39989         dispatch.change(tag);
39990     }
39991
39992     access.options = function(type) {
39993         var options = ['no', 'permissive', 'private', 'destination'];
39994
39995         if (type !== 'access') {
39996             options.unshift('yes');
39997             options.push('designated');
39998
39999             if (type === 'bicycle') {
40000                 options.push('dismount');
40001             }
40002         }
40003
40004         return options.map(function(option) {
40005             return {
40006                 title: field.t('options.' + option + '.description'),
40007                 value: option
40008             };
40009         });
40010     };
40011
40012     var placeholders = {
40013         footway: {
40014             foot: 'designated',
40015             motor_vehicle: 'no'
40016         },
40017         steps: {
40018             foot: 'yes',
40019             motor_vehicle: 'no',
40020             bicycle: 'no',
40021             horse: 'no'
40022         },
40023         pedestrian: {
40024             foot: 'yes',
40025             motor_vehicle: 'no'
40026         },
40027         cycleway: {
40028             motor_vehicle: 'no',
40029             bicycle: 'designated'
40030         },
40031         bridleway: {
40032             motor_vehicle: 'no',
40033             horse: 'designated'
40034         },
40035         path: {
40036             foot: 'yes',
40037             motor_vehicle: 'no',
40038             bicycle: 'yes',
40039             horse: 'yes'
40040         },
40041         motorway: {
40042             foot: 'no',
40043             motor_vehicle: 'yes',
40044             bicycle: 'no',
40045             horse: 'no'
40046         },
40047         trunk: {
40048             motor_vehicle: 'yes'
40049         },
40050         primary: {
40051             foot: 'yes',
40052             motor_vehicle: 'yes',
40053             bicycle: 'yes',
40054             horse: 'yes'
40055         },
40056         secondary: {
40057             foot: 'yes',
40058             motor_vehicle: 'yes',
40059             bicycle: 'yes',
40060             horse: 'yes'
40061         },
40062         tertiary: {
40063             foot: 'yes',
40064             motor_vehicle: 'yes',
40065             bicycle: 'yes',
40066             horse: 'yes'
40067         },
40068         residential: {
40069             foot: 'yes',
40070             motor_vehicle: 'yes',
40071             bicycle: 'yes',
40072             horse: 'yes'
40073         },
40074         unclassified: {
40075             foot: 'yes',
40076             motor_vehicle: 'yes',
40077             bicycle: 'yes',
40078             horse: 'yes'
40079         },
40080         service: {
40081             foot: 'yes',
40082             motor_vehicle: 'yes',
40083             bicycle: 'yes',
40084             horse: 'yes'
40085         },
40086         motorway_link: {
40087             foot: 'no',
40088             motor_vehicle: 'yes',
40089             bicycle: 'no',
40090             horse: 'no'
40091         },
40092         trunk_link: {
40093             motor_vehicle: 'yes'
40094         },
40095         primary_link: {
40096             foot: 'yes',
40097             motor_vehicle: 'yes',
40098             bicycle: 'yes',
40099             horse: 'yes'
40100         },
40101         secondary_link: {
40102             foot: 'yes',
40103             motor_vehicle: 'yes',
40104             bicycle: 'yes',
40105             horse: 'yes'
40106         },
40107         tertiary_link: {
40108             foot: 'yes',
40109             motor_vehicle: 'yes',
40110             bicycle: 'yes',
40111             horse: 'yes'
40112         }
40113     };
40114
40115     access.tags = function(tags) {
40116         items.selectAll('.preset-input-access')
40117             .value(function(d) { return tags[d] || ''; })
40118             .attr('placeholder', function() {
40119                 return tags.access ? tags.access : field.placeholder();
40120             });
40121
40122         // items.selectAll('#preset-input-access-access')
40123         //     .attr('placeholder', 'yes');
40124
40125         _.forEach(placeholders[tags.highway], function(v, k) {
40126             items.selectAll('#preset-input-access-' + k)
40127                 .attr('placeholder', function() { return (tags.access || v); });
40128         });
40129     };
40130
40131     access.focus = function() {
40132         items.selectAll('.preset-input-access')
40133             .node().focus();
40134     };
40135
40136     return d3.rebind(access, dispatch, 'on');
40137 };
40138 iD.ui.preset.address = function(field, context) {
40139     var dispatch = d3.dispatch('init', 'change'),
40140         wrap,
40141         entity,
40142         isInitialized;
40143
40144     var widths = {
40145         housenumber: 1/3,
40146         street: 2/3,
40147         city: 2/3,
40148         state: 1/4,
40149         postcode: 1/3
40150     };
40151
40152     function getStreets() {
40153         var extent = entity.extent(context.graph()),
40154             l = extent.center(),
40155             box = iD.geo.Extent(l).padByMeters(200);
40156
40157         return context.intersects(box)
40158             .filter(isAddressable)
40159             .map(function(d) {
40160                 var loc = context.projection([
40161                     (extent[0][0] + extent[1][0]) / 2,
40162                     (extent[0][1] + extent[1][1]) / 2]),
40163                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
40164                 return {
40165                     title: d.tags.name,
40166                     value: d.tags.name,
40167                     dist: choice.distance
40168                 };
40169             }).sort(function(a, b) {
40170                 return a.dist - b.dist;
40171             });
40172
40173         function isAddressable(d) {
40174             return d.tags.highway && d.tags.name && d.type === 'way';
40175         }
40176     }
40177
40178     function getCities() {
40179         var extent = entity.extent(context.graph()),
40180             l = extent.center(),
40181             box = iD.geo.Extent(l).padByMeters(200);
40182
40183         return context.intersects(box)
40184             .filter(isAddressable)
40185             .map(function(d) {
40186                 return {
40187                     title: d.tags['addr:city'] || d.tags.name,
40188                     value: d.tags['addr:city'] || d.tags.name,
40189                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
40190                 };
40191             }).sort(function(a, b) {
40192                 return a.dist - b.dist;
40193             });
40194
40195         function isAddressable(d) {
40196             if (d.tags.name &&
40197                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
40198                 return true;
40199
40200             if (d.tags.place && d.tags.name && (
40201                     d.tags.place === 'city' ||
40202                     d.tags.place === 'town' ||
40203                     d.tags.place === 'village'))
40204                 return true;
40205
40206             if (d.tags['addr:city']) return true;
40207
40208             return false;
40209         }
40210     }
40211
40212     function getPostCodes() {
40213         var extent = entity.extent(context.graph()),
40214             l = extent.center(),
40215             box = iD.geo.Extent(l).padByMeters(200);
40216
40217         return context.intersects(box)
40218             .filter(isAddressable)
40219             .map(function(d) {
40220                 return {
40221                     title: d.tags['addr:postcode'],
40222                     value: d.tags['addr:postcode'],
40223                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
40224                 };
40225             }).sort(function(a, b) {
40226                 return a.dist - b.dist;
40227             });
40228
40229         function isAddressable(d) {
40230             return d.tags['addr:postcode'];
40231         }
40232     }
40233
40234     function address(selection) {
40235         isInitialized = false;
40236
40237         wrap = selection.selectAll('.preset-input-wrap')
40238             .data([0]);
40239
40240         // Enter
40241
40242         wrap.enter()
40243             .append('div')
40244             .attr('class', 'preset-input-wrap');
40245
40246         var center = entity.extent(context.graph()).center(),
40247             addressFormat;
40248
40249         iD.services.nominatim().countryCode(center, function (err, countryCode) {
40250             addressFormat = _.find(iD.data.addressFormats, function (a) {
40251                 return a && a.countryCodes && _.includes(a.countryCodes, countryCode);
40252             }) || _.first(iD.data.addressFormats);
40253
40254             function row(r) {
40255                 // Normalize widths.
40256                 var total = _.reduce(r, function(sum, field) {
40257                     return sum + (widths[field] || 0.5);
40258                 }, 0);
40259
40260                 return r.map(function (field) {
40261                     return {
40262                         id: field,
40263                         width: (widths[field] || 0.5) / total
40264                     };
40265                 });
40266             }
40267
40268             wrap.selectAll('div')
40269                 .data(addressFormat.format)
40270                 .enter()
40271                 .append('div')
40272                 .attr('class', 'addr-row')
40273                 .selectAll('input')
40274                 .data(row)
40275                 .enter()
40276                 .append('input')
40277                 .property('type', 'text')
40278                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
40279                 .attr('class', function (d) { return 'addr-' + d.id; })
40280                 .style('width', function (d) { return d.width * 100 + '%'; });
40281
40282             // Update
40283
40284             wrap.selectAll('.addr-street')
40285                 .call(d3.combobox()
40286                     .fetcher(function(value, callback) {
40287                         callback(getStreets());
40288                     }));
40289
40290             wrap.selectAll('.addr-city')
40291                 .call(d3.combobox()
40292                     .fetcher(function(value, callback) {
40293                         callback(getCities());
40294                     }));
40295
40296             wrap.selectAll('.addr-postcode')
40297                 .call(d3.combobox()
40298                     .fetcher(function(value, callback) {
40299                         callback(getPostCodes());
40300                     }));
40301
40302             wrap.selectAll('input')
40303                 .on('blur', change())
40304                 .on('change', change());
40305
40306             wrap.selectAll('input:not(.combobox-input)')
40307                 .on('input', change(true));
40308
40309             dispatch.init();
40310             isInitialized = true;
40311         });
40312     }
40313
40314     function change(onInput) {
40315         return function() {
40316             var tags = {};
40317
40318             wrap.selectAll('input')
40319                 .each(function (field) {
40320                     tags['addr:' + field.id] = this.value || undefined;
40321                 });
40322
40323             dispatch.change(tags, onInput);
40324         };
40325     }
40326
40327     function updateTags(tags) {
40328         wrap.selectAll('input')
40329             .value(function (field) {
40330                 return tags['addr:' + field.id] || '';
40331             });
40332     }
40333
40334     address.entity = function(_) {
40335         if (!arguments.length) return entity;
40336         entity = _;
40337         return address;
40338     };
40339
40340     address.tags = function(tags) {
40341         if (isInitialized) {
40342             updateTags(tags);
40343         } else {
40344             dispatch.on('init', function () {
40345                 updateTags(tags);
40346             });
40347         }
40348     };
40349
40350     address.focus = function() {
40351         var node = wrap.selectAll('input').node();
40352         if (node) node.focus();
40353     };
40354
40355     return d3.rebind(address, dispatch, 'on');
40356 };
40357 iD.ui.preset.check =
40358 iD.ui.preset.defaultcheck = function(field) {
40359     var dispatch = d3.dispatch('change'),
40360         options = field.strings && field.strings.options,
40361         values = [],
40362         texts = [],
40363         entity, value, box, text, label;
40364
40365     if (options) {
40366         for (var k in options) {
40367             values.push(k === 'undefined' ? undefined : k);
40368             texts.push(field.t('options.' + k, { 'default': options[k] }));
40369         }
40370     } else {
40371         values = [undefined, 'yes'];
40372         texts = [t('inspector.unknown'), t('inspector.check.yes')];
40373         if (field.type === 'check') {
40374             values.push('no');
40375             texts.push(t('inspector.check.no'));
40376         }
40377     }
40378
40379     var check = function(selection) {
40380         // hack: pretend oneway field is a oneway_yes field
40381         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
40382         if (field.id === 'oneway') {
40383             for (var key in entity.tags) {
40384                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
40385                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
40386                     break;
40387                 }
40388             }
40389         }
40390
40391         selection.classed('checkselect', 'true');
40392
40393         label = selection.selectAll('.preset-input-wrap')
40394             .data([0]);
40395
40396         var enter = label.enter().append('label')
40397             .attr('class', 'preset-input-wrap');
40398
40399         enter.append('input')
40400             .property('indeterminate', field.type === 'check')
40401             .attr('type', 'checkbox')
40402             .attr('id', 'preset-input-' + field.id);
40403
40404         enter.append('span')
40405             .text(texts[0])
40406             .attr('class', 'value');
40407
40408         box = label.select('input')
40409             .on('click', function() {
40410                 var t = {};
40411                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
40412                 dispatch.change(t);
40413                 d3.event.stopPropagation();
40414             });
40415
40416         text = label.select('span.value');
40417     };
40418
40419     check.entity = function(_) {
40420         if (!arguments.length) return entity;
40421         entity = _;
40422         return check;
40423     };
40424
40425     check.tags = function(tags) {
40426         value = tags[field.key];
40427         box.property('indeterminate', field.type === 'check' && !value);
40428         box.property('checked', value === 'yes');
40429         text.text(texts[values.indexOf(value)]);
40430         label.classed('set', !!value);
40431     };
40432
40433     check.focus = function() {
40434         box.node().focus();
40435     };
40436
40437     return d3.rebind(check, dispatch, 'on');
40438 };
40439 iD.ui.preset.combo =
40440 iD.ui.preset.typeCombo =
40441 iD.ui.preset.multiCombo = function(field, context) {
40442     var dispatch = d3.dispatch('change'),
40443         isMulti = (field.type === 'multiCombo'),
40444         optstrings = field.strings && field.strings.options,
40445         optarray = field.options,
40446         snake_case = (field.snake_case || (field.snake_case === undefined)),
40447         combobox = d3.combobox().minItems(isMulti ? 1 : 2),
40448         comboData = [],
40449         multiData = [],
40450         container,
40451         input,
40452         entity;
40453
40454     // ensure multiCombo field.key ends with a ':'
40455     if (isMulti && field.key.match(/:$/) === null) {
40456         field.key += ':';
40457     }
40458
40459
40460     function snake(s) {
40461         return s.replace(/\s+/g, '_');
40462     }
40463
40464     function unsnake(s) {
40465         return s.replace(/_+/g, ' ');
40466     }
40467
40468     function clean(s) {
40469         return s.split(';')
40470             .map(function(s) { return s.trim(); })
40471             .join(';');
40472     }
40473
40474
40475     // returns the tag value for a display value
40476     // (for multiCombo, dval should be the key suffix, not the entire key)
40477     function tagValue(dval) {
40478         dval = clean(dval || '');
40479
40480         if (optstrings) {
40481             var match = _.find(comboData, function(o) {
40482                 return o.key && clean(o.value) === dval;
40483             });
40484             if (match) {
40485                 return match.key;
40486             }
40487         }
40488
40489         if (field.type === 'typeCombo' && !dval) {
40490             return 'yes';
40491         }
40492
40493         return (snake_case ? snake(dval) : dval) || undefined;
40494     }
40495
40496
40497     // returns the display value for a tag value
40498     // (for multiCombo, tval should be the key suffix, not the entire key)
40499     function displayValue(tval) {
40500         tval = tval || '';
40501
40502         if (optstrings) {
40503             var match = _.find(comboData, function(o) { return o.key === tval && o.value; });
40504             if (match) {
40505                 return match.value;
40506             }
40507         }
40508
40509         if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
40510             return '';
40511         }
40512
40513         return snake_case ? unsnake(tval) : tval;
40514     }
40515
40516
40517     function objectDifference(a, b) {
40518         return _.reject(a, function(d1) {
40519             return _.some(b, function(d2) { return d1.value === d2.value; });
40520         });
40521     }
40522
40523
40524     function initCombo(selection, attachTo) {
40525         if (optstrings) {
40526             selection.attr('readonly', 'readonly');
40527             selection.call(combobox, attachTo);
40528             setStaticValues(setPlaceholder);
40529
40530         } else if (optarray) {
40531             selection.call(combobox, attachTo);
40532             setStaticValues(setPlaceholder);
40533
40534         } else if (context.taginfo()) {
40535             selection.call(combobox.fetcher(setTaginfoValues), attachTo);
40536             setTaginfoValues('', setPlaceholder);
40537         }
40538     }
40539
40540
40541     function setStaticValues(callback) {
40542         if (!(optstrings || optarray)) return;
40543
40544         if (optstrings) {
40545             comboData = Object.keys(optstrings).map(function(k) {
40546                 var v = field.t('options.' + k, { 'default': optstrings[k] });
40547                 return {
40548                     key: k,
40549                     value: v,
40550                     title: v
40551                 };
40552             });
40553
40554         } else if (optarray) {
40555             comboData = optarray.map(function(k) {
40556                 var v = snake_case ? unsnake(k) : k;
40557                 return {
40558                     key: k,
40559                     value: v,
40560                     title: v
40561                 };
40562             });
40563         }
40564
40565         combobox.data(objectDifference(comboData, multiData));
40566         if (callback) callback(comboData);
40567     }
40568
40569
40570     function setTaginfoValues(q, callback) {
40571         var fn = isMulti ? 'multikeys' : 'values';
40572         context.taginfo()[fn]({
40573             debounce: true,
40574             key: field.key,
40575             geometry: context.geometry(entity.id),
40576             query: (isMulti ? field.key : '') + q
40577         }, function(err, data) {
40578             if (err) return;
40579             comboData = _.map(data, 'value').map(function(k) {
40580                 if (isMulti) k = k.replace(field.key, '');
40581                 var v = snake_case ? unsnake(k) : k;
40582                 return {
40583                     key: k,
40584                     value: v,
40585                     title: v
40586                 };
40587             });
40588             comboData = objectDifference(comboData, multiData);
40589             if (callback) callback(comboData);
40590         });
40591     }
40592
40593
40594     function setPlaceholder(d) {
40595         var ph;
40596         if (isMulti) {
40597             ph = field.placeholder() || t('inspector.add');
40598         } else {
40599             var vals = _.map(d, 'value').filter(function(s) { return s.length < 20; }),
40600                 placeholders = vals.length > 1 ? vals : _.map(d, 'key');
40601             ph = field.placeholder() || placeholders.slice(0, 3).join(', ');
40602         }
40603
40604         input.attr('placeholder', ph + '…');
40605     }
40606
40607
40608     function change() {
40609         var val = tagValue(input.value()),
40610             t = {};
40611
40612         if (isMulti) {
40613             if (!val) return;
40614             container.classed('active', false);
40615             input.value('');
40616             field.keys.push(field.key + val);
40617             t[field.key + val] = 'yes';
40618             window.setTimeout(function() { input.node().focus(); }, 10);
40619
40620         } else {
40621             t[field.key] = val;
40622         }
40623
40624         dispatch.change(t);
40625     }
40626
40627
40628     function removeMultikey(d) {
40629         d3.event.stopPropagation();
40630         var t = {};
40631         t[d.key] = undefined;
40632         dispatch.change(t);
40633     }
40634
40635
40636     function combo(selection) {
40637         if (isMulti) {
40638             container = selection.selectAll('ul').data([0]);
40639
40640             container.enter()
40641                 .append('ul')
40642                 .attr('class', 'form-field-multicombo')
40643                 .on('click', function() {
40644                     window.setTimeout(function() { input.node().focus(); }, 10);
40645                 });
40646
40647         } else {
40648             container = selection;
40649         }
40650
40651         input = container.selectAll('input')
40652             .data([0]);
40653
40654         input.enter()
40655             .append('input')
40656             .attr('type', 'text')
40657             .attr('id', 'preset-input-' + field.id)
40658             .call(initCombo, selection);
40659
40660         input
40661             .on('change', change)
40662             .on('blur', change);
40663
40664         if (isMulti) {
40665             combobox
40666                 .on('accept', function() {
40667                     input.node().blur();
40668                     input.node().focus();
40669                 });
40670
40671             input
40672                 .on('focus', function() { container.classed('active', true); });
40673         }
40674     }
40675
40676
40677     combo.tags = function(tags) {
40678         if (isMulti) {
40679             multiData = [];
40680
40681             // Build multiData array containing keys already set..
40682             Object.keys(tags).forEach(function(key) {
40683                 if (key.indexOf(field.key) !== 0 || tags[key].toLowerCase() !== 'yes') return;
40684
40685                 var suffix = key.substring(field.key.length);
40686                 multiData.push({
40687                     key: key,
40688                     value: displayValue(suffix)
40689                 });
40690             });
40691
40692             // Set keys for form-field modified (needed for undo and reset buttons)..
40693             field.keys = _.map(multiData, 'key');
40694
40695             // Exclude existing multikeys from combo options..
40696             var available = objectDifference(comboData, multiData);
40697             combobox.data(available);
40698
40699             // Hide "Add" button if this field uses fixed set of
40700             // translateable optstrings and they're all currently used..
40701             container.selectAll('.combobox-input, .combobox-caret')
40702                 .classed('hide', optstrings && !available.length);
40703
40704
40705             // Render chips
40706             var chips = container.selectAll('.chips').data(multiData);
40707
40708             var enter = chips.enter()
40709                 .insert('li', 'input')
40710                 .attr('class', 'chips');
40711
40712             enter.append('span');
40713             enter.append('a');
40714
40715             chips.select('span')
40716                 .text(function(d) { return d.value; });
40717
40718             chips.select('a')
40719                 .on('click', removeMultikey)
40720                 .attr('class', 'remove')
40721                 .text('×');
40722
40723             chips.exit()
40724                 .remove();
40725
40726         } else {
40727             input.value(displayValue(tags[field.key]));
40728         }
40729     };
40730
40731
40732     combo.focus = function() {
40733         input.node().focus();
40734     };
40735
40736
40737     combo.entity = function(_) {
40738         if (!arguments.length) return entity;
40739         entity = _;
40740         return combo;
40741     };
40742
40743
40744     return d3.rebind(combo, dispatch, 'on');
40745 };
40746 iD.ui.preset.cycleway = function(field) {
40747     var dispatch = d3.dispatch('change'),
40748         items;
40749
40750     function cycleway(selection) {
40751         var wrap = selection.selectAll('.preset-input-wrap')
40752             .data([0]);
40753
40754         wrap.enter().append('div')
40755             .attr('class', 'cf preset-input-wrap')
40756             .append('ul');
40757
40758         items = wrap.select('ul').selectAll('li')
40759             .data(field.keys);
40760
40761         // Enter
40762
40763         var enter = items.enter().append('li')
40764             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
40765
40766         enter.append('span')
40767             .attr('class', 'col6 label preset-label-cycleway')
40768             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
40769             .text(function(d) { return field.t('types.' + d); });
40770
40771         enter.append('div')
40772             .attr('class', 'col6 preset-input-cycleway-wrap')
40773             .append('input')
40774             .attr('type', 'text')
40775             .attr('class', 'preset-input-cycleway')
40776             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
40777             .each(function(d) {
40778                 d3.select(this)
40779                     .call(d3.combobox()
40780                         .data(cycleway.options(d)));
40781             });
40782
40783         // Update
40784
40785         wrap.selectAll('.preset-input-cycleway')
40786             .on('change', change)
40787             .on('blur', change);
40788     }
40789
40790     function change() {
40791         var inputs = d3.selectAll('.preset-input-cycleway')[0],
40792             left = d3.select(inputs[0]).value(),
40793             right = d3.select(inputs[1]).value(),
40794             tag = {};
40795         if (left === 'none' || left === '') { left = undefined; }
40796         if (right === 'none' || right === '') { right = undefined; }
40797
40798         // Always set both left and right as changing one can affect the other
40799         tag = {
40800             cycleway: undefined,
40801             'cycleway:left': left,
40802             'cycleway:right': right
40803         };
40804
40805         // If the left and right tags match, use the cycleway tag to tag both
40806         // sides the same way
40807         if (left === right) {
40808             tag = {
40809                 cycleway: left,
40810                 'cycleway:left': undefined,
40811                 'cycleway:right': undefined
40812             };
40813         }
40814
40815         dispatch.change(tag);
40816     }
40817
40818     cycleway.options = function() {
40819         return d3.keys(field.strings.options).map(function(option) {
40820             return {
40821                 title: field.t('options.' + option + '.description'),
40822                 value: option
40823             };
40824         });
40825     };
40826
40827     cycleway.tags = function(tags) {
40828         items.selectAll('.preset-input-cycleway')
40829             .value(function(d) {
40830                 // If cycleway is set, always return that
40831                 if (tags.cycleway) {
40832                     return tags.cycleway;
40833                 }
40834                 return tags[d] || '';
40835             })
40836             .attr('placeholder', field.placeholder());
40837     };
40838
40839     cycleway.focus = function() {
40840         items.selectAll('.preset-input-cycleway')
40841             .node().focus();
40842     };
40843
40844     return d3.rebind(cycleway, dispatch, 'on');
40845 };
40846 iD.ui.preset.text =
40847 iD.ui.preset.number =
40848 iD.ui.preset.tel =
40849 iD.ui.preset.email =
40850 iD.ui.preset.url = function(field, context) {
40851
40852     var dispatch = d3.dispatch('change'),
40853         input,
40854         entity;
40855
40856     function i(selection) {
40857         var fieldId = 'preset-input-' + field.id;
40858
40859         input = selection.selectAll('input')
40860             .data([0]);
40861
40862         input.enter().append('input')
40863             .attr('type', field.type)
40864             .attr('id', fieldId)
40865             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
40866
40867         input
40868             .on('input', change(true))
40869             .on('blur', change())
40870             .on('change', change());
40871
40872         if (field.type === 'tel') {
40873             var center = entity.extent(context.graph()).center();
40874             iD.services.nominatim().countryCode(center, function (err, countryCode) {
40875                 if (err || !iD.data.phoneFormats[countryCode]) return;
40876                 selection.selectAll('#' + fieldId)
40877                     .attr('placeholder', iD.data.phoneFormats[countryCode]);
40878             });
40879
40880         } else if (field.type === 'number') {
40881             input.attr('type', 'text');
40882
40883             var spinControl = selection.selectAll('.spin-control')
40884                 .data([0]);
40885
40886             var enter = spinControl.enter().append('div')
40887                 .attr('class', 'spin-control');
40888
40889             enter.append('button')
40890                 .datum(1)
40891                 .attr('class', 'increment')
40892                 .attr('tabindex', -1);
40893
40894             enter.append('button')
40895                 .datum(-1)
40896                 .attr('class', 'decrement')
40897                 .attr('tabindex', -1);
40898
40899             spinControl.selectAll('button')
40900                 .on('click', function(d) {
40901                     d3.event.preventDefault();
40902                     var num = parseInt(input.node().value || 0, 10);
40903                     if (!isNaN(num)) input.node().value = num + d;
40904                     change()();
40905                 });
40906         }
40907     }
40908
40909     function change(onInput) {
40910         return function() {
40911             var t = {};
40912             t[field.key] = input.value() || undefined;
40913             dispatch.change(t, onInput);
40914         };
40915     }
40916
40917     i.entity = function(_) {
40918         if (!arguments.length) return entity;
40919         entity = _;
40920         return i;
40921     };
40922
40923     i.tags = function(tags) {
40924         input.value(tags[field.key] || '');
40925     };
40926
40927     i.focus = function() {
40928         var node = input.node();
40929         if (node) node.focus();
40930     };
40931
40932     return d3.rebind(i, dispatch, 'on');
40933 };
40934 iD.ui.preset.localized = function(field, context) {
40935     var dispatch = d3.dispatch('change', 'input'),
40936         wikipedia = iD.services.wikipedia(),
40937         input, localizedInputs, wikiTitles,
40938         entity;
40939
40940     function localized(selection) {
40941         input = selection.selectAll('.localized-main')
40942             .data([0]);
40943
40944         input.enter().append('input')
40945             .attr('type', 'text')
40946             .attr('id', 'preset-input-' + field.id)
40947             .attr('class', 'localized-main')
40948             .attr('placeholder', field.placeholder());
40949
40950         if (field.id === 'name') {
40951             var preset = context.presets().match(entity, context.graph());
40952             input.call(d3.combobox().fetcher(
40953                 iD.util.SuggestNames(preset, iD.data.suggestions)
40954             ));
40955         }
40956
40957         input
40958             .on('input', change(true))
40959             .on('blur', change())
40960             .on('change', change());
40961
40962         var translateButton = selection.selectAll('.localized-add')
40963             .data([0]);
40964
40965         translateButton.enter()
40966             .append('button')
40967             .attr('class', 'button-input-action localized-add minor')
40968             .attr('tabindex', -1)
40969             .call(iD.svg.Icon('#icon-plus'))
40970             .call(bootstrap.tooltip()
40971                 .title(t('translate.translate'))
40972                 .placement('left'));
40973
40974         translateButton
40975             .on('click', addNew);
40976
40977         localizedInputs = selection.selectAll('.localized-wrap')
40978             .data([0]);
40979
40980         localizedInputs.enter().append('div')
40981             .attr('class', 'localized-wrap');
40982     }
40983
40984     function addNew() {
40985         d3.event.preventDefault();
40986         var data = localizedInputs.selectAll('div.entry').data();
40987         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
40988         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
40989         var isLangEn = defaultLang.indexOf('en') > -1;
40990         if (isLangEn || langExists) {
40991           defaultLang = '';
40992         }
40993         data.push({ lang: defaultLang, value: '' });
40994         localizedInputs.call(render, data);
40995     }
40996
40997     function change(onInput) {
40998         return function() {
40999             var t = {};
41000             t[field.key] = d3.select(this).value() || undefined;
41001             dispatch.change(t, onInput);
41002         };
41003     }
41004
41005     function key(lang) { return field.key + ':' + lang; }
41006
41007     function changeLang(d) {
41008         var lang = d3.select(this).value(),
41009             t = {},
41010             language = _.find(iD.data.wikipedia, function(d) {
41011                 return d[0].toLowerCase() === lang.toLowerCase() ||
41012                     d[1].toLowerCase() === lang.toLowerCase();
41013             });
41014
41015         if (language) lang = language[2];
41016
41017         if (d.lang && d.lang !== lang) {
41018             t[key(d.lang)] = undefined;
41019         }
41020
41021         var value = d3.select(this.parentNode)
41022             .selectAll('.localized-value')
41023             .value();
41024
41025         if (lang && value) {
41026             t[key(lang)] = value;
41027         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
41028             t[key(lang)] = wikiTitles[d.lang];
41029         }
41030
41031         d.lang = lang;
41032         dispatch.change(t);
41033     }
41034
41035     function changeValue(d) {
41036         if (!d.lang) return;
41037         var t = {};
41038         t[key(d.lang)] = d3.select(this).value() || undefined;
41039         dispatch.change(t);
41040     }
41041
41042     function fetcher(value, cb) {
41043         var v = value.toLowerCase();
41044
41045         cb(iD.data.wikipedia.filter(function(d) {
41046             return d[0].toLowerCase().indexOf(v) >= 0 ||
41047             d[1].toLowerCase().indexOf(v) >= 0 ||
41048             d[2].toLowerCase().indexOf(v) >= 0;
41049         }).map(function(d) {
41050             return { value: d[1] };
41051         }));
41052     }
41053
41054     function render(selection, data) {
41055         var wraps = selection.selectAll('div.entry').
41056             data(data, function(d) { return d.lang; });
41057
41058         var innerWrap = wraps.enter()
41059             .insert('div', ':first-child');
41060
41061         innerWrap.attr('class', 'entry')
41062             .each(function() {
41063                 var wrap = d3.select(this);
41064                 var langcombo = d3.combobox().fetcher(fetcher).minItems(0);
41065
41066                 var label = wrap.append('label')
41067                     .attr('class','form-label')
41068                     .text(t('translate.localized_translation_label'))
41069                     .attr('for','localized-lang');
41070
41071                 label.append('button')
41072                     .attr('class', 'minor remove')
41073                     .on('click', function(d){
41074                         d3.event.preventDefault();
41075                         var t = {};
41076                         t[key(d.lang)] = undefined;
41077                         dispatch.change(t);
41078                         d3.select(this.parentNode.parentNode)
41079                             .style('top','0')
41080                             .style('max-height','240px')
41081                             .transition()
41082                             .style('opacity', '0')
41083                             .style('max-height','0px')
41084                             .remove();
41085                     })
41086                     .call(iD.svg.Icon('#operation-delete'));
41087
41088                 wrap.append('input')
41089                     .attr('class', 'localized-lang')
41090                     .attr('type', 'text')
41091                     .attr('placeholder',t('translate.localized_translation_language'))
41092                     .on('blur', changeLang)
41093                     .on('change', changeLang)
41094                     .call(langcombo);
41095
41096                 wrap.append('input')
41097                     .on('blur', changeValue)
41098                     .on('change', changeValue)
41099                     .attr('type', 'text')
41100                     .attr('placeholder', t('translate.localized_translation_name'))
41101                     .attr('class', 'localized-value');
41102             });
41103
41104         innerWrap
41105             .style('margin-top', '0px')
41106             .style('max-height', '0px')
41107             .style('opacity', '0')
41108             .transition()
41109             .duration(200)
41110             .style('margin-top', '10px')
41111             .style('max-height', '240px')
41112             .style('opacity', '1')
41113             .each('end', function() {
41114                 d3.select(this)
41115                     .style('max-height', '')
41116                     .style('overflow', 'visible');
41117             });
41118
41119         wraps.exit()
41120             .transition()
41121             .duration(200)
41122             .style('max-height','0px')
41123             .style('opacity', '0')
41124             .style('top','-10px')
41125             .remove();
41126
41127         var entry = selection.selectAll('.entry');
41128
41129         entry.select('.localized-lang')
41130             .value(function(d) {
41131                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
41132                 return lang ? lang[1] : d.lang;
41133             });
41134
41135         entry.select('.localized-value')
41136             .value(function(d) { return d.value; });
41137     }
41138
41139     localized.tags = function(tags) {
41140         // Fetch translations from wikipedia
41141         if (tags.wikipedia && !wikiTitles) {
41142             wikiTitles = {};
41143             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
41144             if (wm && wm[0] && wm[1]) {
41145                 wikipedia.translations(wm[1], wm[2], function(d) {
41146                     wikiTitles = d;
41147                 });
41148             }
41149         }
41150
41151         input.value(tags[field.key] || '');
41152
41153         var postfixed = [], k, m;
41154         for (k in tags) {
41155             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
41156             if (m && m[1] === field.key && m[2]) {
41157                 postfixed.push({ lang: m[2], value: tags[k] });
41158             }
41159         }
41160
41161         localizedInputs.call(render, postfixed.reverse());
41162     };
41163
41164     localized.focus = function() {
41165         input.node().focus();
41166     };
41167
41168     localized.entity = function(_) {
41169         if (!arguments.length) return entity;
41170         entity = _;
41171         return localized;
41172     };
41173
41174     return d3.rebind(localized, dispatch, 'on');
41175 };
41176 iD.ui.preset.maxspeed = function(field, context) {
41177     var dispatch = d3.dispatch('change'),
41178         entity,
41179         imperial,
41180         unitInput,
41181         combobox,
41182         input;
41183
41184     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
41185         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
41186
41187     function maxspeed(selection) {
41188         combobox = d3.combobox();
41189         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
41190
41191         input = selection.selectAll('#preset-input-' + field.id)
41192             .data([0]);
41193
41194         input.enter().append('input')
41195             .attr('type', 'text')
41196             .attr('id', 'preset-input-' + field.id)
41197             .attr('placeholder', field.placeholder());
41198
41199         input
41200             .call(combobox)
41201             .on('change', change)
41202             .on('blur', change);
41203
41204         var childNodes = context.graph().childNodes(context.entity(entity.id)),
41205             loc = childNodes[~~(childNodes.length/2)].loc;
41206
41207         imperial = _.some(iD.data.imperial.features, function(f) {
41208             return _.some(f.geometry.coordinates, function(d) {
41209                 return iD.geo.pointInPolygon(loc, d);
41210             });
41211         });
41212
41213         unitInput = selection.selectAll('input.maxspeed-unit')
41214             .data([0]);
41215
41216         unitInput.enter().append('input')
41217             .attr('type', 'text')
41218             .attr('class', 'maxspeed-unit');
41219
41220         unitInput
41221             .on('blur', changeUnits)
41222             .on('change', changeUnits)
41223             .call(unitCombobox);
41224
41225         function changeUnits() {
41226             imperial = unitInput.value() === 'mph';
41227             unitInput.value(imperial ? 'mph' : 'km/h');
41228             setSuggestions();
41229             change();
41230         }
41231
41232     }
41233
41234     function setSuggestions() {
41235         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
41236         unitInput.value(imperial ? 'mph' : 'km/h');
41237     }
41238
41239     function comboValues(d) {
41240         return {
41241             value: d.toString(),
41242             title: d.toString()
41243         };
41244     }
41245
41246     function change() {
41247         var tag = {},
41248             value = input.value();
41249
41250         if (!value) {
41251             tag[field.key] = undefined;
41252         } else if (isNaN(value) || !imperial) {
41253             tag[field.key] = value;
41254         } else {
41255             tag[field.key] = value + ' mph';
41256         }
41257
41258         dispatch.change(tag);
41259     }
41260
41261     maxspeed.tags = function(tags) {
41262         var value = tags[field.key];
41263
41264         if (value && value.indexOf('mph') >= 0) {
41265             value = parseInt(value, 10);
41266             imperial = true;
41267         } else if (value) {
41268             imperial = false;
41269         }
41270
41271         setSuggestions();
41272
41273         input.value(value || '');
41274     };
41275
41276     maxspeed.focus = function() {
41277         input.node().focus();
41278     };
41279
41280     maxspeed.entity = function(_) {
41281         entity = _;
41282     };
41283
41284     return d3.rebind(maxspeed, dispatch, 'on');
41285 };
41286 iD.ui.preset.radio = function(field) {
41287     var dispatch = d3.dispatch('change'),
41288         labels, radios, placeholder;
41289
41290     function radio(selection) {
41291         selection.classed('preset-radio', true);
41292
41293         var wrap = selection.selectAll('.preset-input-wrap')
41294             .data([0]);
41295
41296         var buttonWrap = wrap.enter().append('div')
41297             .attr('class', 'preset-input-wrap toggle-list');
41298
41299         buttonWrap.append('span')
41300             .attr('class', 'placeholder');
41301
41302         placeholder = selection.selectAll('.placeholder');
41303
41304         labels = wrap.selectAll('label')
41305             .data(field.options || field.keys);
41306
41307         var enter = labels.enter().append('label');
41308
41309         enter.append('input')
41310             .attr('type', 'radio')
41311             .attr('name', field.id)
41312             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
41313             .attr('checked', false);
41314
41315         enter.append('span')
41316             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
41317
41318         radios = labels.selectAll('input')
41319             .on('change', change);
41320     }
41321
41322     function change() {
41323         var t = {};
41324         if (field.key) t[field.key] = undefined;
41325         radios.each(function(d) {
41326             var active = d3.select(this).property('checked');
41327             if (field.key) {
41328                 if (active) t[field.key] = d;
41329             } else {
41330                 t[d] = active ? 'yes' : undefined;
41331             }
41332         });
41333         dispatch.change(t);
41334     }
41335
41336     radio.tags = function(tags) {
41337         function checked(d) {
41338             if (field.key) {
41339                 return tags[field.key] === d;
41340             } else {
41341                 return !!(tags[d] && tags[d] !== 'no');
41342             }
41343         }
41344
41345         labels.classed('active', checked);
41346         radios.property('checked', checked);
41347         var selection = radios.filter(function() { return this.checked; });
41348         if (selection.empty()) {
41349             placeholder.text(t('inspector.none'));
41350         } else {
41351             placeholder.text(selection.attr('value'));
41352         }
41353     };
41354
41355     radio.focus = function() {
41356         radios.node().focus();
41357     };
41358
41359     return d3.rebind(radio, dispatch, 'on');
41360 };
41361 iD.ui.preset.restrictions = function(field, context) {
41362     var dispatch = d3.dispatch('change'),
41363         hover = iD.behavior.Hover(context),
41364         vertexID,
41365         fromNodeID;
41366
41367
41368     function restrictions(selection) {
41369         // if form field is hidden or has detached from dom, clean up.
41370         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
41371             selection.call(restrictions.off);
41372             return;
41373         }
41374
41375         var wrap = selection.selectAll('.preset-input-wrap')
41376             .data([0]);
41377
41378         var enter = wrap.enter()
41379             .append('div')
41380             .attr('class', 'preset-input-wrap');
41381
41382         enter
41383             .append('div')
41384             .attr('class', 'restriction-help');
41385
41386
41387         var intersection = iD.geo.Intersection(context.graph(), vertexID),
41388             graph = intersection.graph,
41389             vertex = graph.entity(vertexID),
41390             filter = d3.functor(true),
41391             extent = iD.geo.Extent(),
41392             projection = iD.geo.RawMercator();
41393
41394         var d = wrap.dimensions(),
41395             c = [d[0] / 2, d[1] / 2],
41396             z = 24;
41397
41398         projection
41399             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
41400
41401         var s = projection(vertex.loc);
41402
41403         projection
41404             .translate([c[0] - s[0], c[1] - s[1]])
41405             .clipExtent([[0, 0], d]);
41406
41407         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
41408             drawVertices = iD.svg.Vertices(projection, context),
41409             drawLines = iD.svg.Lines(projection, context),
41410             drawTurns = iD.svg.Turns(projection, context);
41411
41412         enter
41413             .call(drawLayers)
41414             .selectAll('.surface')
41415             .call(hover);
41416
41417
41418         var surface = wrap.selectAll('.surface');
41419
41420         surface
41421             .dimensions(d)
41422             .call(drawVertices, graph, [vertex], filter, extent, z)
41423             .call(drawLines, graph, intersection.ways, filter)
41424             .call(drawTurns, graph, intersection.turns(fromNodeID));
41425
41426         surface
41427             .on('click.restrictions', click)
41428             .on('mouseover.restrictions', mouseover)
41429             .on('mouseout.restrictions', mouseout);
41430
41431         surface
41432             .selectAll('.selected')
41433             .classed('selected', false);
41434
41435         if (fromNodeID) {
41436             surface
41437                 .selectAll('.' + intersection.highways[fromNodeID].id)
41438                 .classed('selected', true);
41439         }
41440
41441         mouseout();
41442
41443         context.history()
41444             .on('change.restrictions', render);
41445
41446         d3.select(window)
41447             .on('resize.restrictions', function() {
41448                 wrap.dimensions(null);
41449                 render();
41450             });
41451
41452         function click() {
41453             var datum = d3.event.target.__data__;
41454             if (datum instanceof iD.Entity) {
41455                 fromNodeID = intersection.adjacentNodeId(datum.id);
41456                 render();
41457             } else if (datum instanceof iD.geo.Turn) {
41458                 if (datum.restriction) {
41459                     context.perform(
41460                         iD.actions.UnrestrictTurn(datum, projection),
41461                         t('operations.restriction.annotation.delete'));
41462                 } else {
41463                     context.perform(
41464                         iD.actions.RestrictTurn(datum, projection),
41465                         t('operations.restriction.annotation.create'));
41466                 }
41467             }
41468         }
41469
41470         function mouseover() {
41471             var datum = d3.event.target.__data__;
41472             if (datum instanceof iD.geo.Turn) {
41473                 var graph = context.graph(),
41474                     presets = context.presets(),
41475                     preset;
41476
41477                 if (datum.restriction) {
41478                     preset = presets.match(graph.entity(datum.restriction), graph);
41479                 } else {
41480                     preset = presets.item('type/restriction/' +
41481                         iD.geo.inferRestriction(
41482                             graph,
41483                             datum.from,
41484                             datum.via,
41485                             datum.to,
41486                             projection));
41487                 }
41488
41489                 wrap.selectAll('.restriction-help')
41490                     .text(t('operations.restriction.help.' +
41491                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
41492                         {restriction: preset.name()}));
41493             }
41494         }
41495
41496         function mouseout() {
41497             wrap.selectAll('.restriction-help')
41498                 .text(t('operations.restriction.help.' +
41499                     (fromNodeID ? 'toggle' : 'select')));
41500         }
41501
41502         function render() {
41503             if (context.hasEntity(vertexID)) {
41504                 restrictions(selection);
41505             }
41506         }
41507     }
41508
41509     restrictions.entity = function(_) {
41510         if (!vertexID || vertexID !== _.id) {
41511             fromNodeID = null;
41512             vertexID = _.id;
41513         }
41514     };
41515
41516     restrictions.tags = function() {};
41517     restrictions.focus = function() {};
41518
41519     restrictions.off = function(selection) {
41520         selection.selectAll('.surface')
41521             .call(hover.off)
41522             .on('click.restrictions', null)
41523             .on('mouseover.restrictions', null)
41524             .on('mouseout.restrictions', null);
41525
41526         context.history()
41527             .on('change.restrictions', null);
41528
41529         d3.select(window)
41530             .on('resize.restrictions', null);
41531     };
41532
41533     return d3.rebind(restrictions, dispatch, 'on');
41534 };
41535 iD.ui.preset.textarea = function(field) {
41536     var dispatch = d3.dispatch('change'),
41537         input;
41538
41539     function textarea(selection) {
41540         input = selection.selectAll('textarea')
41541             .data([0]);
41542
41543         input.enter().append('textarea')
41544             .attr('id', 'preset-input-' + field.id)
41545             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
41546             .attr('maxlength', 255);
41547
41548         input
41549             .on('input', change(true))
41550             .on('blur', change())
41551             .on('change', change());
41552     }
41553
41554     function change(onInput) {
41555         return function() {
41556             var t = {};
41557             t[field.key] = input.value() || undefined;
41558             dispatch.change(t, onInput);
41559         };
41560     }
41561
41562     textarea.tags = function(tags) {
41563         input.value(tags[field.key] || '');
41564     };
41565
41566     textarea.focus = function() {
41567         input.node().focus();
41568     };
41569
41570     return d3.rebind(textarea, dispatch, 'on');
41571 };
41572 iD.ui.preset.wikipedia = function(field, context) {
41573     var dispatch = d3.dispatch('change'),
41574         wikipedia = iD.services.wikipedia(),
41575         wikidata = iD.services.wikidata(),
41576         link, entity, lang, title;
41577
41578     function wiki(selection) {
41579         var langcombo = d3.combobox()
41580             .fetcher(function(value, cb) {
41581                 var v = value.toLowerCase();
41582
41583                 cb(iD.data.wikipedia.filter(function(d) {
41584                     return d[0].toLowerCase().indexOf(v) >= 0 ||
41585                         d[1].toLowerCase().indexOf(v) >= 0 ||
41586                         d[2].toLowerCase().indexOf(v) >= 0;
41587                 }).map(function(d) {
41588                     return { value: d[1] };
41589                 }));
41590             });
41591
41592         var titlecombo = d3.combobox()
41593             .fetcher(function(value, cb) {
41594
41595                 if (!value) value = context.entity(entity.id).tags.name || '';
41596                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
41597
41598                 searchfn(language()[2], value, function(query, data) {
41599                     cb(data.map(function(d) {
41600                         return { value: d };
41601                     }));
41602                 });
41603             });
41604
41605         lang = selection.selectAll('input.wiki-lang')
41606             .data([0]);
41607
41608         lang.enter().append('input')
41609             .attr('type', 'text')
41610             .attr('class', 'wiki-lang')
41611             .attr('placeholder', t('translate.localized_translation_language'))
41612             .value('English');
41613
41614         lang
41615             .call(langcombo)
41616             .on('blur', changeLang)
41617             .on('change', changeLang);
41618
41619         title = selection.selectAll('input.wiki-title')
41620             .data([0]);
41621
41622         title.enter().append('input')
41623             .attr('type', 'text')
41624             .attr('class', 'wiki-title')
41625             .attr('id', 'preset-input-' + field.id);
41626
41627         title
41628             .call(titlecombo)
41629             .on('blur', blur)
41630             .on('change', change);
41631
41632         link = selection.selectAll('a.wiki-link')
41633             .data([0]);
41634
41635         link.enter().append('a')
41636             .attr('class', 'wiki-link button-input-action minor')
41637             .attr('tabindex', -1)
41638             .attr('target', '_blank')
41639             .call(iD.svg.Icon('#icon-out-link', 'inline'));
41640     }
41641
41642     function language() {
41643         var value = lang.value().toLowerCase();
41644         var locale = iD.detect().locale.toLowerCase();
41645         var localeLanguage;
41646         return _.find(iD.data.wikipedia, function(d) {
41647             if (d[2] === locale) localeLanguage = d;
41648             return d[0].toLowerCase() === value ||
41649                 d[1].toLowerCase() === value ||
41650                 d[2] === value;
41651         }) || localeLanguage || ['English', 'English', 'en'];
41652     }
41653
41654     function changeLang() {
41655         lang.value(language()[1]);
41656         change(true);
41657     }
41658
41659     function blur() {
41660         change(true);
41661     }
41662
41663     function change(skipWikidata) {
41664         var value = title.value(),
41665             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
41666             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
41667             anchor,
41668             syncTags = {};
41669
41670         if (l) {
41671             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
41672             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
41673             if (m[3]) {
41674                 try {
41675                     // Best-effort `anchordecode:` implementation
41676                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
41677                 } catch (e) {
41678                     anchor = decodeURIComponent(m[3]);
41679                 }
41680                 value += '#' + anchor.replace(/_/g, ' ');
41681             }
41682             value = value.slice(0, 1).toUpperCase() + value.slice(1);
41683             lang.value(l[1]);
41684             title.value(value);
41685         }
41686
41687         syncTags.wikipedia = value ? language()[2] + ':' + value : undefined;
41688         if (!skipWikidata) {
41689             syncTags.wikidata = undefined;
41690         }
41691
41692         dispatch.change(syncTags);
41693
41694
41695         if (skipWikidata || !value || !language()[2]) return;
41696
41697         // attempt asynchronous update of wikidata tag..
41698         var initEntityId = entity.id,
41699             initWikipedia = context.entity(initEntityId).tags.wikipedia;
41700
41701         wikidata.itemsByTitle(language()[2], value, function (title, data) {
41702             // 1. most recent change was a tag change
41703             var annotation = t('operations.change_tags.annotation'),
41704                 currAnnotation = context.history().undoAnnotation();
41705             if (currAnnotation !== annotation) return;
41706
41707             // 2. same entity exists and still selected
41708             var selectedIds = context.selectedIDs(),
41709                 currEntityId = selectedIds.length > 0 && selectedIds[0];
41710             if (currEntityId !== initEntityId) return;
41711
41712             // 3. wikipedia value has not changed
41713             var currTags = _.clone(context.entity(currEntityId).tags),
41714                 qids = data && Object.keys(data);
41715             if (initWikipedia !== currTags.wikipedia) return;
41716
41717             // ok to coalesce the update of wikidata tag into the previous tag change
41718             currTags.wikidata = qids && _.find(qids, function (id) {
41719                 return id.match(/^Q\d+$/);
41720             });
41721
41722             context.overwrite(iD.actions.ChangeTags(currEntityId, currTags), annotation);
41723             dispatch.change(currTags);
41724         });
41725     }
41726
41727     wiki.tags = function(tags) {
41728         var value = tags[field.key] || '',
41729             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
41730             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
41731             anchor = m && m[3];
41732
41733         // value in correct format
41734         if (l) {
41735             lang.value(l[1]);
41736             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
41737             if (anchor) {
41738                 try {
41739                     // Best-effort `anchorencode:` implementation
41740                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
41741                 } catch (e) {
41742                     anchor = anchor.replace(/ /g, '_');
41743                 }
41744             }
41745             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
41746                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
41747
41748         // unrecognized value format
41749         } else {
41750             title.value(value);
41751             if (value && value !== '') {
41752                 lang.value('');
41753             }
41754             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
41755         }
41756     };
41757
41758     wiki.entity = function(_) {
41759         if (!arguments.length) return entity;
41760         entity = _;
41761         return wiki;
41762     };
41763
41764     wiki.focus = function() {
41765         title.node().focus();
41766     };
41767
41768     return d3.rebind(wiki, dispatch, 'on');
41769 };
41770 iD.ui.intro.area = function(context, reveal) {
41771     var event = d3.dispatch('done'),
41772         timeout;
41773
41774     var step = {
41775         title: 'intro.areas.title'
41776     };
41777
41778     step.enter = function() {
41779         var playground = [-85.63552, 41.94159],
41780             corner = [-85.63565411045074, 41.9417715536927];
41781         context.map().centerZoom(playground, 19);
41782         reveal('button.add-area',
41783             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
41784             { tooltipClass: 'intro-areas-add' });
41785
41786         context.on('enter.intro', addArea);
41787
41788         function addArea(mode) {
41789             if (mode.id !== 'add-area') return;
41790             context.on('enter.intro', drawArea);
41791
41792             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
41793             var pointBox = iD.ui.intro.pad(corner, padding, context);
41794             reveal(pointBox, t('intro.areas.corner'));
41795
41796             context.map().on('move.intro', function() {
41797                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
41798                 pointBox = iD.ui.intro.pad(corner, padding, context);
41799                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
41800             });
41801         }
41802
41803         function drawArea(mode) {
41804             if (mode.id !== 'draw-area') return;
41805             context.on('enter.intro', enterSelect);
41806
41807             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
41808             var pointBox = iD.ui.intro.pad(playground, padding, context);
41809             reveal(pointBox, t('intro.areas.place'));
41810
41811             context.map().on('move.intro', function() {
41812                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
41813                 pointBox = iD.ui.intro.pad(playground, padding, context);
41814                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
41815             });
41816         }
41817
41818         function enterSelect(mode) {
41819             if (mode.id !== 'select') return;
41820             context.map().on('move.intro', null);
41821             context.on('enter.intro', null);
41822
41823             timeout = setTimeout(function() {
41824                 reveal('.preset-search-input',
41825                     t('intro.areas.search',
41826                     { name: context.presets().item('leisure/playground').name() }));
41827                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
41828             }, 500);
41829         }
41830
41831         function keySearch() {
41832             var first = d3.select('.preset-list-item:first-child');
41833             if (first.classed('preset-leisure-playground')) {
41834                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
41835                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
41836                 d3.select('.preset-search-input').on('keyup.intro', null);
41837             }
41838         }
41839
41840         function selectedPreset() {
41841             reveal('.pane',
41842                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41843             context.on('exit.intro', event.done);
41844         }
41845     };
41846
41847     step.exit = function() {
41848         window.clearTimeout(timeout);
41849         context.on('enter.intro', null);
41850         context.on('exit.intro', null);
41851         context.history().on('change.intro', null);
41852         context.map().on('move.intro', null);
41853         d3.select('.preset-search-input').on('keyup.intro', null);
41854     };
41855
41856     return d3.rebind(step, event, 'on');
41857 };
41858 iD.ui.intro.line = function(context, reveal) {
41859     var event = d3.dispatch('done'),
41860         timeouts = [];
41861
41862     var step = {
41863         title: 'intro.lines.title'
41864     };
41865
41866     function timeout(f, t) {
41867         timeouts.push(window.setTimeout(f, t));
41868     }
41869
41870     function eventCancel() {
41871         d3.event.stopPropagation();
41872         d3.event.preventDefault();
41873     }
41874
41875     step.enter = function() {
41876         var centroid = [-85.62830, 41.95699];
41877         var midpoint = [-85.62975395449628, 41.95787501510204];
41878         var start = [-85.6297754121684, 41.95805253325314];
41879         var intersection = [-85.62974496187628, 41.95742515554585];
41880
41881         context.map().centerZoom(start, 18);
41882         reveal('button.add-line',
41883             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
41884             { tooltipClass: 'intro-lines-add' });
41885
41886         context.on('enter.intro', addLine);
41887
41888         function addLine(mode) {
41889             if (mode.id !== 'add-line') return;
41890             context.on('enter.intro', drawLine);
41891
41892             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
41893             var pointBox = iD.ui.intro.pad(start, padding, context);
41894             reveal(pointBox, t('intro.lines.start'));
41895
41896             context.map().on('move.intro', function() {
41897                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
41898                 pointBox = iD.ui.intro.pad(start, padding, context);
41899                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
41900             });
41901         }
41902
41903         function drawLine(mode) {
41904             if (mode.id !== 'draw-line') return;
41905             context.history().on('change.intro', addIntersection);
41906             context.on('enter.intro', retry);
41907
41908             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
41909             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
41910             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
41911
41912             context.map().on('move.intro', function() {
41913                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
41914                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
41915                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
41916             });
41917         }
41918
41919         // ended line before creating intersection
41920         function retry(mode) {
41921             if (mode.id !== 'select') return;
41922             var pointBox = iD.ui.intro.pad(intersection, 30, context),
41923                 ids = mode.selectedIDs();
41924             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
41925             d3.select(window).on('mousedown.intro', eventCancel, true);
41926
41927             timeout(function() {
41928                 context.replace(iD.actions.DeleteMultiple(ids));
41929                 step.exit();
41930                 step.enter();
41931             }, 3000);
41932         }
41933
41934         function addIntersection(changes) {
41935             if ( _.some(changes.created(), function(d) {
41936                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
41937             })) {
41938                 context.history().on('change.intro', null);
41939                 context.on('enter.intro', enterSelect);
41940
41941                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
41942                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
41943                 reveal(pointBox, t('intro.lines.finish'));
41944
41945                 context.map().on('move.intro', function() {
41946                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
41947                     pointBox = iD.ui.intro.pad(centroid, padding, context);
41948                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
41949                 });
41950             }
41951         }
41952
41953         function enterSelect(mode) {
41954             if (mode.id !== 'select') return;
41955             context.map().on('move.intro', null);
41956             context.on('enter.intro', null);
41957             d3.select('#curtain').style('pointer-events', 'all');
41958
41959             presetCategory();
41960         }
41961
41962         function presetCategory() {
41963             timeout(function() {
41964                 d3.select('#curtain').style('pointer-events', 'none');
41965                 var road = d3.select('.preset-category-road .preset-list-button');
41966                 reveal(road.node(), t('intro.lines.road'));
41967                 road.one('click.intro', roadCategory);
41968             }, 500);
41969         }
41970
41971         function roadCategory() {
41972             timeout(function() {
41973                 var grid = d3.select('.subgrid');
41974                 reveal(grid.node(), t('intro.lines.residential'));
41975                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
41976                     .one('click.intro', retryPreset);
41977                 grid.selectAll('.preset-highway-residential .preset-list-button')
41978                     .one('click.intro', roadDetails);
41979             }, 500);
41980         }
41981
41982         // selected wrong road type
41983         function retryPreset() {
41984             timeout(function() {
41985                 var preset = d3.select('.entity-editor-pane .preset-list-button');
41986                 reveal(preset.node(), t('intro.lines.wrong_preset'));
41987                 preset.one('click.intro', presetCategory);
41988             }, 500);
41989         }
41990
41991         function roadDetails() {
41992             reveal('.pane',
41993                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41994             context.on('exit.intro', event.done);
41995         }
41996
41997     };
41998
41999     step.exit = function() {
42000         d3.select(window).on('mousedown.intro', null, true);
42001         d3.select('#curtain').style('pointer-events', 'none');
42002         timeouts.forEach(window.clearTimeout);
42003         context.on('enter.intro', null);
42004         context.on('exit.intro', null);
42005         context.map().on('move.intro', null);
42006         context.history().on('change.intro', null);
42007     };
42008
42009     return d3.rebind(step, event, 'on');
42010 };
42011 iD.ui.intro.navigation = function(context, reveal) {
42012     var event = d3.dispatch('done'),
42013         timeouts = [];
42014
42015     var step = {
42016         title: 'intro.navigation.title'
42017     };
42018
42019     function set(f, t) {
42020         timeouts.push(window.setTimeout(f, t));
42021     }
42022
42023     function eventCancel() {
42024         d3.event.stopPropagation();
42025         d3.event.preventDefault();
42026     }
42027
42028     step.enter = function() {
42029         var rect = context.surfaceRect(),
42030             map = {
42031                 left: rect.left + 10,
42032                 top: rect.top + 70,
42033                 width: rect.width - 70,
42034                 height: rect.height - 170
42035             };
42036
42037         context.map().centerZoom([-85.63591, 41.94285], 19);
42038
42039         reveal(map, t('intro.navigation.drag'));
42040
42041         context.map().on('move.intro', _.debounce(function() {
42042             context.map().on('move.intro', null);
42043             townhall();
42044             context.on('enter.intro', inspectTownHall);
42045         }, 400));
42046
42047         function townhall() {
42048             var hall = [-85.63645945147184, 41.942986488012565];
42049
42050             var point = context.projection(hall);
42051             if (point[0] < 0 || point[0] > rect.width ||
42052                 point[1] < 0 || point[1] > rect.height) {
42053                 context.map().center(hall);
42054             }
42055
42056             var box = iD.ui.intro.pointBox(hall, context);
42057             reveal(box, t('intro.navigation.select'));
42058
42059             context.map().on('move.intro', function() {
42060                 var box = iD.ui.intro.pointBox(hall, context);
42061                 reveal(box, t('intro.navigation.select'), {duration: 0});
42062             });
42063         }
42064
42065         function inspectTownHall(mode) {
42066             if (mode.id !== 'select') return;
42067             context.on('enter.intro', null);
42068             context.map().on('move.intro', null);
42069             set(function() {
42070                 reveal('.entity-editor-pane',
42071                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
42072                 context.on('exit.intro', streetSearch);
42073             }, 700);
42074         }
42075
42076         function streetSearch() {
42077             context.on('exit.intro', null);
42078             reveal('.search-header input',
42079                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
42080             d3.select('.search-header input').on('keyup.intro', searchResult);
42081         }
42082
42083         function searchResult() {
42084             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
42085                 firstName = first.select('.entity-name'),
42086                 name = t('intro.graph.spring_st');
42087
42088             if (!firstName.empty() && firstName.text() === name) {
42089                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
42090                 context.on('exit.intro', selectedStreet);
42091                 d3.select('.search-header input')
42092                     .on('keydown.intro', eventCancel, true)
42093                     .on('keyup.intro', null);
42094             }
42095         }
42096
42097         function selectedStreet() {
42098             var springSt = [-85.63585099140167, 41.942506848938926];
42099             context.map().center(springSt);
42100             context.on('exit.intro', event.done);
42101             set(function() {
42102                 reveal('.entity-editor-pane',
42103                     t('intro.navigation.chosen', {
42104                         name: t('intro.graph.spring_st'),
42105                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
42106                     }));
42107             }, 400);
42108         }
42109     };
42110
42111     step.exit = function() {
42112         timeouts.forEach(window.clearTimeout);
42113         context.map().on('move.intro', null);
42114         context.on('enter.intro', null);
42115         context.on('exit.intro', null);
42116         d3.select('.search-header input')
42117             .on('keydown.intro', null)
42118             .on('keyup.intro', null);
42119     };
42120
42121     return d3.rebind(step, event, 'on');
42122 };
42123 iD.ui.intro.point = function(context, reveal) {
42124     var event = d3.dispatch('done'),
42125         timeouts = [];
42126
42127     var step = {
42128         title: 'intro.points.title'
42129     };
42130
42131     function setTimeout(f, t) {
42132         timeouts.push(window.setTimeout(f, t));
42133     }
42134
42135     function eventCancel() {
42136         d3.event.stopPropagation();
42137         d3.event.preventDefault();
42138     }
42139
42140     step.enter = function() {
42141         context.map().centerZoom([-85.63279, 41.94394], 19);
42142         reveal('button.add-point',
42143             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
42144             { tooltipClass: 'intro-points-add' });
42145
42146         var corner = [-85.632481,41.944094];
42147
42148         context.on('enter.intro', addPoint);
42149
42150         function addPoint(mode) {
42151             if (mode.id !== 'add-point') return;
42152             context.on('enter.intro', enterSelect);
42153
42154             var pointBox = iD.ui.intro.pad(corner, 150, context);
42155             reveal(pointBox, t('intro.points.place'));
42156
42157             context.map().on('move.intro', function() {
42158                 pointBox = iD.ui.intro.pad(corner, 150, context);
42159                 reveal(pointBox, t('intro.points.place'), {duration: 0});
42160             });
42161         }
42162
42163         function enterSelect(mode) {
42164             if (mode.id !== 'select') return;
42165             context.map().on('move.intro', null);
42166             context.on('enter.intro', null);
42167
42168             setTimeout(function() {
42169                 reveal('.preset-search-input',
42170                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
42171                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
42172             }, 500);
42173         }
42174
42175         function keySearch() {
42176             var first = d3.select('.preset-list-item:first-child');
42177             if (first.classed('preset-amenity-cafe')) {
42178                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
42179                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
42180                 d3.select('.preset-search-input')
42181                     .on('keydown.intro', eventCancel, true)
42182                     .on('keyup.intro', null);
42183             }
42184         }
42185
42186         function selectedPreset() {
42187             setTimeout(function() {
42188                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
42189                 context.history().on('change.intro', closeEditor);
42190                 context.on('exit.intro', selectPoint);
42191             }, 400);
42192         }
42193
42194         function closeEditor() {
42195             d3.select('.preset-search-input').on('keydown.intro', null);
42196             context.history().on('change.intro', null);
42197             reveal('.entity-editor-pane',
42198                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
42199         }
42200
42201         function selectPoint() {
42202             context.on('exit.intro', null);
42203             context.history().on('change.intro', null);
42204             context.on('enter.intro', enterReselect);
42205
42206             var pointBox = iD.ui.intro.pad(corner, 150, context);
42207             reveal(pointBox, t('intro.points.reselect'));
42208
42209             context.map().on('move.intro', function() {
42210                 pointBox = iD.ui.intro.pad(corner, 150, context);
42211                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
42212             });
42213         }
42214
42215         function enterReselect(mode) {
42216             if (mode.id !== 'select') return;
42217             context.map().on('move.intro', null);
42218             context.on('enter.intro', null);
42219
42220             setTimeout(function() {
42221                 reveal('.entity-editor-pane',
42222                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
42223                 context.on('exit.intro', deletePoint);
42224             }, 500);
42225         }
42226
42227         function deletePoint() {
42228             context.on('exit.intro', null);
42229             context.on('enter.intro', enterDelete);
42230
42231             var pointBox = iD.ui.intro.pad(corner, 150, context);
42232             reveal(pointBox, t('intro.points.reselect_delete'));
42233
42234             context.map().on('move.intro', function() {
42235                 pointBox = iD.ui.intro.pad(corner, 150, context);
42236                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
42237             });
42238         }
42239
42240         function enterDelete(mode) {
42241             if (mode.id !== 'select') return;
42242             context.map().on('move.intro', null);
42243             context.on('enter.intro', null);
42244             context.on('exit.intro', deletePoint);
42245             context.map().on('move.intro', deletePoint);
42246             context.history().on('change.intro', deleted);
42247
42248             setTimeout(function() {
42249                 var node = d3.select('.radial-menu-item-delete').node();
42250                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
42251                 reveal(pointBox,
42252                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
42253             }, 300);
42254         }
42255
42256         function deleted(changed) {
42257             if (changed.deleted().length) event.done();
42258         }
42259
42260     };
42261
42262     step.exit = function() {
42263         timeouts.forEach(window.clearTimeout);
42264         context.on('exit.intro', null);
42265         context.on('enter.intro', null);
42266         context.map().on('move.intro', null);
42267         context.history().on('change.intro', null);
42268         d3.select('.preset-search-input')
42269             .on('keyup.intro', null)
42270             .on('keydown.intro', null);
42271     };
42272
42273     return d3.rebind(step, event, 'on');
42274 };
42275 iD.ui.intro.startEditing = function(context, reveal) {
42276     var event = d3.dispatch('done', 'startEditing'),
42277         modal,
42278         timeouts = [];
42279
42280     var step = {
42281         title: 'intro.startediting.title'
42282     };
42283
42284     function timeout(f, t) {
42285         timeouts.push(window.setTimeout(f, t));
42286     }
42287
42288     step.enter = function() {
42289         reveal('.map-control.help-control',
42290             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
42291
42292         timeout(function() {
42293             reveal('#bar button.save', t('intro.startediting.save'));
42294         }, 5000);
42295
42296         timeout(function() {
42297             reveal('#surface');
42298         }, 10000);
42299
42300         timeout(function() {
42301             modal = iD.ui.modal(context.container());
42302
42303             modal.select('.modal')
42304                 .attr('class', 'modal-splash modal col6');
42305
42306             modal.selectAll('.close').remove();
42307
42308             var startbutton = modal.select('.content')
42309                 .attr('class', 'fillL')
42310                     .append('button')
42311                         .attr('class', 'modal-section huge-modal-button')
42312                         .on('click', function() {
42313                             modal.remove();
42314                         });
42315
42316                 startbutton.append('div')
42317                     .attr('class','illustration');
42318                 startbutton.append('h2')
42319                     .text(t('intro.startediting.start'));
42320
42321             event.startEditing();
42322         }, 10500);
42323     };
42324
42325     step.exit = function() {
42326         if (modal) modal.remove();
42327         timeouts.forEach(window.clearTimeout);
42328     };
42329
42330     return d3.rebind(step, event, 'on');
42331 };
42332 iD.presets = function() {
42333
42334     // an iD.presets.Collection with methods for
42335     // loading new data and returning defaults
42336
42337     var all = iD.presets.Collection([]),
42338         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
42339         fields = {},
42340         universal = [],
42341         recent = iD.presets.Collection([]);
42342
42343     // Index of presets by (geometry, tag key).
42344     var index = {
42345         point: {},
42346         vertex: {},
42347         line: {},
42348         area: {},
42349         relation: {}
42350     };
42351
42352     all.match = function(entity, resolver) {
42353         var geometry = entity.geometry(resolver);
42354
42355         // Treat entities on addr:interpolation lines as points, not vertices (#3241)
42356         if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
42357             geometry = 'point';
42358         }
42359
42360         var geometryMatches = index[geometry],
42361             best = -1,
42362             match;
42363
42364         for (var k in entity.tags) {
42365             var keyMatches = geometryMatches[k];
42366             if (!keyMatches) continue;
42367
42368             for (var i = 0; i < keyMatches.length; i++) {
42369                 var score = keyMatches[i].matchScore(entity);
42370                 if (score > best) {
42371                     best = score;
42372                     match = keyMatches[i];
42373                 }
42374             }
42375         }
42376
42377         return match || all.item(geometry);
42378     };
42379
42380     // Because of the open nature of tagging, iD will never have a complete
42381     // list of tags used in OSM, so we want it to have logic like "assume
42382     // that a closed way with an amenity tag is an area, unless the amenity
42383     // is one of these specific types". This function computes a structure
42384     // that allows testing of such conditions, based on the presets designated
42385     // as as supporting (or not supporting) the area geometry.
42386     //
42387     // The returned object L is a whitelist/blacklist of tags. A closed way
42388     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
42389     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
42390     // and the subkeys form the blacklist.
42391     all.areaKeys = function() {
42392         var areaKeys = {},
42393             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
42394             presets = _.reject(all.collection, 'suggestion');
42395
42396         // whitelist
42397         presets.forEach(function(d) {
42398             for (var key in d.tags) break;
42399             if (!key) return;
42400             if (ignore.indexOf(key) !== -1) return;
42401
42402             if (d.geometry.indexOf('area') !== -1) {
42403                 areaKeys[key] = areaKeys[key] || {};
42404             }
42405         });
42406
42407         // blacklist
42408         presets.forEach(function(d) {
42409             for (var key in d.tags) break;
42410             if (!key) return;
42411             if (ignore.indexOf(key) !== -1) return;
42412
42413             var value = d.tags[key];
42414             if (d.geometry.indexOf('area') === -1 &&
42415                 d.geometry.indexOf('line') !== -1 &&
42416                 key in areaKeys && value !== '*') {
42417                 areaKeys[key][value] = true;
42418             }
42419         });
42420
42421         return areaKeys;
42422     };
42423
42424     all.load = function(d) {
42425
42426         if (d.fields) {
42427             _.forEach(d.fields, function(d, id) {
42428                 fields[id] = iD.presets.Field(id, d);
42429                 if (d.universal) universal.push(fields[id]);
42430             });
42431         }
42432
42433         if (d.presets) {
42434             _.forEach(d.presets, function(d, id) {
42435                 all.collection.push(iD.presets.Preset(id, d, fields));
42436             });
42437         }
42438
42439         if (d.categories) {
42440             _.forEach(d.categories, function(d, id) {
42441                 all.collection.push(iD.presets.Category(id, d, all));
42442             });
42443         }
42444
42445         if (d.defaults) {
42446             var getItem = _.bind(all.item, all);
42447             defaults = {
42448                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
42449                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
42450                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
42451                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
42452                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
42453             };
42454         }
42455
42456         for (var i = 0; i < all.collection.length; i++) {
42457             var preset = all.collection[i],
42458                 geometry = preset.geometry;
42459
42460             for (var j = 0; j < geometry.length; j++) {
42461                 var g = index[geometry[j]];
42462                 for (var k in preset.tags) {
42463                     (g[k] = g[k] || []).push(preset);
42464                 }
42465             }
42466         }
42467
42468         return all;
42469     };
42470
42471     all.field = function(id) {
42472         return fields[id];
42473     };
42474
42475     all.universal = function() {
42476         return universal;
42477     };
42478
42479     all.defaults = function(geometry, n) {
42480         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
42481             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
42482         return iD.presets.Collection(_.uniq(rec.concat(def).concat(all.item(geometry))));
42483     };
42484
42485     all.choose = function(preset) {
42486         if (!preset.isFallback()) {
42487             recent = iD.presets.Collection(_.uniq([preset].concat(recent.collection)));
42488         }
42489         return all;
42490     };
42491
42492     return all;
42493 };
42494 iD.presets.Category = function(id, category, all) {
42495     category = _.clone(category);
42496
42497     category.id = id;
42498
42499     category.members = iD.presets.Collection(category.members.map(function(id) {
42500         return all.item(id);
42501     }));
42502
42503     category.matchGeometry = function(geometry) {
42504         return category.geometry.indexOf(geometry) >= 0;
42505     };
42506
42507     category.matchScore = function() { return -1; };
42508
42509     category.name = function() {
42510         return t('presets.categories.' + id + '.name', {'default': id});
42511     };
42512
42513     category.terms = function() {
42514         return [];
42515     };
42516
42517     return category;
42518 };
42519 iD.presets.Collection = function(collection) {
42520
42521     var maxSearchResults = 50,
42522         maxSuggestionResults = 10;
42523
42524     var presets = {
42525
42526         collection: collection,
42527
42528         item: function(id) {
42529             return _.find(collection, function(d) {
42530                 return d.id === id;
42531             });
42532         },
42533
42534         matchGeometry: function(geometry) {
42535             return iD.presets.Collection(collection.filter(function(d) {
42536                 return d.matchGeometry(geometry);
42537             }));
42538         },
42539
42540         search: function(value, geometry) {
42541             if (!value) return this;
42542
42543             value = value.toLowerCase();
42544
42545             var searchable = _.filter(collection, function(a) {
42546                     return a.searchable !== false && a.suggestion !== true;
42547                 }),
42548                 suggestions = _.filter(collection, function(a) {
42549                     return a.suggestion === true;
42550                 });
42551
42552             function leading(a) {
42553                 var index = a.indexOf(value);
42554                 return index === 0 || a[index - 1] === ' ';
42555             }
42556
42557             // matches value to preset.name
42558             var leading_name = _.filter(searchable, function(a) {
42559                     return leading(a.name().toLowerCase());
42560                 }).sort(function(a, b) {
42561                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
42562                     if (i === 0) return a.name().length - b.name().length;
42563                     else return i;
42564                 });
42565
42566             // matches value to preset.terms values
42567             var leading_terms = _.filter(searchable, function(a) {
42568                     return _.some(a.terms() || [], leading);
42569                 });
42570
42571             // matches value to preset.tags values
42572             var leading_tag_values = _.filter(searchable, function(a) {
42573                     return _.some(_.without(_.values(a.tags || {}), '*'), leading);
42574                 });
42575
42576
42577             // finds close matches to value in preset.name
42578             var levenstein_name = searchable.map(function(a) {
42579                     return {
42580                         preset: a,
42581                         dist: iD.util.editDistance(value, a.name().toLowerCase())
42582                     };
42583                 }).filter(function(a) {
42584                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
42585                 }).sort(function(a, b) {
42586                     return a.dist - b.dist;
42587                 }).map(function(a) {
42588                     return a.preset;
42589                 });
42590
42591             // finds close matches to value in preset.terms
42592             var leventstein_terms = _.filter(searchable, function(a) {
42593                     return _.some(a.terms() || [], function(b) {
42594                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
42595                     });
42596                 });
42597
42598             function suggestionName(name) {
42599                 var nameArray = name.split(' - ');
42600                 if (nameArray.length > 1) {
42601                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
42602                 }
42603                 return name.toLowerCase();
42604             }
42605
42606             var leading_suggestions = _.filter(suggestions, function(a) {
42607                     return leading(suggestionName(a.name()));
42608                 }).sort(function(a, b) {
42609                     a = suggestionName(a.name());
42610                     b = suggestionName(b.name());
42611                     var i = a.indexOf(value) - b.indexOf(value);
42612                     if (i === 0) return a.length - b.length;
42613                     else return i;
42614                 });
42615
42616             var leven_suggestions = suggestions.map(function(a) {
42617                     return {
42618                         preset: a,
42619                         dist: iD.util.editDistance(value, suggestionName(a.name()))
42620                     };
42621                 }).filter(function(a) {
42622                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
42623                 }).sort(function(a, b) {
42624                     return a.dist - b.dist;
42625                 }).map(function(a) {
42626                     return a.preset;
42627                 });
42628
42629             var other = presets.item(geometry);
42630
42631             var results = leading_name.concat(
42632                             leading_terms,
42633                             leading_tag_values,
42634                             leading_suggestions.slice(0, maxSuggestionResults+5),
42635                             levenstein_name,
42636                             leventstein_terms,
42637                             leven_suggestions.slice(0, maxSuggestionResults)
42638                         ).slice(0, maxSearchResults-1);
42639
42640             return iD.presets.Collection(_.uniq(
42641                     results.concat(other)
42642                 ));
42643         }
42644     };
42645
42646     return presets;
42647 };
42648 iD.presets.Field = function(id, field) {
42649     field = _.clone(field);
42650
42651     field.id = id;
42652
42653     field.matchGeometry = function(geometry) {
42654         return !field.geometry || field.geometry === geometry;
42655     };
42656
42657     field.t = function(scope, options) {
42658         return t('presets.fields.' + id + '.' + scope, options);
42659     };
42660
42661     field.label = function() {
42662         return field.t('label', {'default': id});
42663     };
42664
42665     var placeholder = field.placeholder;
42666     field.placeholder = function() {
42667         return field.t('placeholder', {'default': placeholder});
42668     };
42669
42670     return field;
42671 };
42672 iD.presets.Preset = function(id, preset, fields) {
42673     preset = _.clone(preset);
42674
42675     preset.id = id;
42676     preset.fields = (preset.fields || []).map(getFields);
42677     preset.geometry = (preset.geometry || []);
42678
42679     function getFields(f) {
42680         return fields[f];
42681     }
42682
42683     preset.matchGeometry = function(geometry) {
42684         return preset.geometry.indexOf(geometry) >= 0;
42685     };
42686
42687     var matchScore = preset.matchScore || 1;
42688     preset.matchScore = function(entity) {
42689         var tags = preset.tags,
42690             score = 0;
42691
42692         for (var t in tags) {
42693             if (entity.tags[t] === tags[t]) {
42694                 score += matchScore;
42695             } else if (tags[t] === '*' && t in entity.tags) {
42696                 score += matchScore / 2;
42697             } else {
42698                 return -1;
42699             }
42700         }
42701
42702         return score;
42703     };
42704
42705     preset.t = function(scope, options) {
42706         return t('presets.presets.' + id + '.' + scope, options);
42707     };
42708
42709     var name = preset.name;
42710     preset.name = function() {
42711         if (preset.suggestion) {
42712             id = id.split('/');
42713             id = id[0] + '/' + id[1];
42714             return name + ' - ' + t('presets.presets.' + id + '.name');
42715         }
42716         return preset.t('name', {'default': name});
42717     };
42718
42719     preset.terms = function() {
42720         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
42721     };
42722
42723     preset.isFallback = function() {
42724         var tagCount = Object.keys(preset.tags).length;
42725         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
42726     };
42727
42728     preset.reference = function(geometry) {
42729         var key = Object.keys(preset.tags)[0],
42730             value = preset.tags[key];
42731
42732         if (geometry === 'relation' && key === 'type') {
42733             return { rtype: value };
42734         } else if (value === '*') {
42735             return { key: key };
42736         } else {
42737             return { key: key, value: value };
42738         }
42739     };
42740
42741     var removeTags = preset.removeTags || preset.tags;
42742     preset.removeTags = function(tags, geometry) {
42743         tags = _.omit(tags, _.keys(removeTags));
42744
42745         for (var f in preset.fields) {
42746             var field = preset.fields[f];
42747             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
42748                 delete tags[field.key];
42749             }
42750         }
42751
42752         delete tags.area;
42753         return tags;
42754     };
42755
42756     var applyTags = preset.addTags || preset.tags;
42757     preset.applyTags = function(tags, geometry) {
42758         var k;
42759
42760         tags = _.clone(tags);
42761
42762         for (k in applyTags) {
42763             if (applyTags[k] === '*') {
42764                 tags[k] = 'yes';
42765             } else {
42766                 tags[k] = applyTags[k];
42767             }
42768         }
42769
42770         // Add area=yes if necessary.
42771         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
42772         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
42773         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
42774         if (geometry === 'area') {
42775             var needsAreaTag = true;
42776             if (preset.geometry.indexOf('line') === -1) {
42777                 for (k in applyTags) {
42778                     if (k in iD.areaKeys) {
42779                         needsAreaTag = false;
42780                         break;
42781                     }
42782                 }
42783             }
42784             if (needsAreaTag) {
42785                 tags.area = 'yes';
42786             }
42787         }
42788
42789         for (var f in preset.fields) {
42790             var field = preset.fields[f];
42791             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
42792                 tags[field.key] = field.default;
42793             }
42794         }
42795
42796         return tags;
42797     };
42798
42799     return preset;
42800 };
42801 iD.validations = {};
42802 iD.validations.DeprecatedTag = function() {
42803
42804     var validation = function(changes) {
42805         var warnings = [];
42806         for (var i = 0; i < changes.created.length; i++) {
42807             var change = changes.created[i],
42808                 deprecatedTags = change.deprecatedTags();
42809
42810             if (!_.isEmpty(deprecatedTags)) {
42811                 var tags = iD.util.tagText({ tags: deprecatedTags });
42812                 warnings.push({
42813                     id: 'deprecated_tags',
42814                     message: t('validations.deprecated_tags', { tags: tags }),
42815                     entity: change
42816                 });
42817             }
42818         }
42819         return warnings;
42820     };
42821
42822     return validation;
42823 };
42824 iD.validations.ManyDeletions = function() {
42825     var threshold = 100;
42826
42827     var validation = function(changes) {
42828         var warnings = [];
42829         if (changes.deleted.length > threshold) {
42830             warnings.push({
42831                 id: 'many_deletions',
42832                 message: t('validations.many_deletions', { n: changes.deleted.length })
42833             });
42834         }
42835         return warnings;
42836     };
42837
42838     return validation;
42839 };
42840 iD.validations.MissingTag = function() {
42841
42842     // Slightly stricter check than Entity#isUsed (#3091)
42843     function hasTags(entity, graph) {
42844         return _.without(Object.keys(entity.tags), 'area', 'name').length > 0 ||
42845             graph.parentRelations(entity).length > 0;
42846     }
42847
42848     var validation = function(changes, graph) {
42849         var warnings = [];
42850         for (var i = 0; i < changes.created.length; i++) {
42851             var change = changes.created[i],
42852                 geometry = change.geometry(graph);
42853
42854             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !hasTags(change, graph)) {
42855                 warnings.push({
42856                     id: 'missing_tag',
42857                     message: t('validations.untagged_' + geometry),
42858                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
42859                     entity: change
42860                 });
42861             }
42862         }
42863         return warnings;
42864     };
42865
42866     return validation;
42867 };
42868 iD.validations.TagSuggestsArea = function() {
42869
42870     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
42871     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
42872     function tagSuggestsArea(tags) {
42873         if (_.isEmpty(tags)) return false;
42874
42875         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
42876         for (var i = 0; i < presence.length; i++) {
42877             if (tags[presence[i]] !== undefined) {
42878                 return presence[i] + '=' + tags[presence[i]];
42879             }
42880         }
42881
42882         if (tags.building && tags.building === 'yes') return 'building=yes';
42883     }
42884
42885     var validation = function(changes, graph) {
42886         var warnings = [];
42887         for (var i = 0; i < changes.created.length; i++) {
42888             var change = changes.created[i],
42889                 geometry = change.geometry(graph),
42890                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
42891
42892             if (suggestion) {
42893                 warnings.push({
42894                     id: 'tag_suggests_area',
42895                     message: t('validations.tag_suggests_area', { tag: suggestion }),
42896                     entity: change
42897                 });
42898             }
42899         }
42900         return warnings;
42901     };
42902
42903     return validation;
42904 };
42905 })();
42906 window.locale = { _current: 'en' };
42907
42908 locale.current = function(_) {
42909     if (!arguments.length) return locale._current;
42910     if (locale[_] !== undefined) locale._current = _;
42911     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
42912     return locale;
42913 };
42914
42915 function t(s, o, loc) {
42916     loc = loc || locale._current;
42917
42918     var path = s.split(".").reverse(),
42919         rep = locale[loc];
42920
42921     while (rep !== undefined && path.length) rep = rep[path.pop()];
42922
42923     if (rep !== undefined) {
42924         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
42925         return rep;
42926     }
42927
42928     if (loc !== 'en') {
42929         return t(s, o, 'en');
42930     }
42931
42932     if (o && 'default' in o) {
42933         return o['default'];
42934     }
42935
42936     var missing = 'Missing ' + loc + ' translation: ' + s;
42937     if (typeof console !== "undefined") console.error(missing);
42938
42939     return missing;
42940 }
42941 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":{},"geometry":{"type":"Polygon","coordinates":[[[1.97754,51.13111],[1.8457,63.45051],[-10.45898,57.01681],[-6.82251,55.26503],[-7.25583,55.06525],[-7.26546,55.0665],[-7.26992,55.06419],[-7.2725,55.06346],[-7.27818,55.05362],[-7.2893,55.04735],[-7.29939,55.0561],[-7.31835,55.04475],[-7.3447,55.05079],[-7.38831,55.02389],[-7.40547,55.00292],[-7.39157,54.99507],[-7.40075,54.98892],[-7.40706,54.98291],[-7.40363,54.97963],[-7.40633,54.97813],[-7.39835,54.97013],[-7.40745,54.96357],[-7.40178,54.95961],[-7.40727,54.95515],[-7.3944,54.94376],[-7.44444,54.93858],[-7.45216,54.89793],[-7.44204,54.87532],[-7.4713,54.83431],[-7.48092,54.83093],[-7.49216,54.82185],[-7.55121,54.79054],[-7.5443,54.78609],[-7.54958,54.75653],[-7.5349,54.74917],[-7.54881,54.74068],[-7.55941,54.74556],[-7.57894,54.74221],[-7.57507,54.7494],[-7.58606,54.75039],[-7.58872,54.74377],[-7.60031,54.74603],[-7.60632,54.74405],[-7.61662,54.74459],[-7.63593,54.75108],[-7.68854,54.72968],[-7.72064,54.72155],[-7.75094,54.70469],[-7.79094,54.71942],[-7.8051,54.71932],[-7.83497,54.73632],[-7.85419,54.72745],[-7.91496,54.67582],[-7.90174,54.66182],[-7.83832,54.63401],[-7.7433,54.6188],[-7.70863,54.63485],[-7.70682,54.6189],[-7.69386,54.6188],[-7.69631,54.61125],[-7.75845,54.59509],[-7.78708,54.58],[-7.79446,54.58141],[-7.79969,54.57704],[-7.79673,54.56915],[-7.8184,54.56315],[-7.83334,54.55227],[-7.82737,54.54299],[-7.85007,54.53363],[-7.90741,54.53722],[-7.93213,54.53388],[-8.00487,54.54568],[-8.03727,54.51162],[-8.04285,54.48759],[-8.08027,54.48829],[-8.09988,54.48395],[-8.09126,54.4765],[-8.111,54.47807],[-8.11512,54.46904],[-8.16542,54.46914],[-8.1776,54.46485],[-8.14293,54.45003],[-8.16284,54.4413],[-8.08731,54.4002],[-8.06062,54.37051],[-8.03289,54.35711],[-8.00054,54.34835],[-7.93333,54.30561],[-7.85849,54.29151],[-7.87067,54.28794],[-7.87265,54.26648],[-7.86123,54.25931],[-7.85917,54.21256],[-7.71043,54.20307],[-7.70193,54.20776],[-7.68828,54.202],[-7.67644,54.18906],[-7.66082,54.1871],[-7.62554,54.16545],[-7.62541,54.15319],[-7.61026,54.14353],[-7.57421,54.14142],[-7.57181,54.13287],[-7.56228,54.12704],[-7.51379,54.12998],[-7.47944,54.122],[-7.47169,54.12665],[-7.47075,54.13318],[-7.44684,54.15168],[-7.40792,54.156],[-7.42579,54.14092],[-7.41903,54.13629],[-7.3744,54.14172],[-7.37234,54.13881],[-7.39509,54.12624],[-7.39182,54.12017],[-7.36341,54.13157],[-7.34518,54.11577],[-7.32471,54.12123],[-7.32003,54.11379],[-7.3078,54.11718],[-7.30548,54.12347],[-7.31591,54.12697],[-7.31213,54.13162],[-7.3187,54.13411],[-7.31857,54.13745],[-7.32222,54.13836],[-7.32737,54.13544],[-7.3399,54.14585],[-7.30827,54.16716],[-7.30024,54.16625],[-7.29029,54.1715],[-7.28158,54.16839],[-7.2863,54.14919],[-7.29874,54.14904],[-7.30162,54.14411],[-7.28411,54.13971],[-7.29192,54.13071],[-7.29737,54.133],[-7.30883,54.13242],[-7.30333,54.12251],[-7.29218,54.11929],[-7.27844,54.12282],[-7.27707,54.12986],[-7.26613,54.13624],[-7.2566,54.16354],[-7.24015,54.17125],[-7.2575,54.17678],[-7.2581,54.19257],[-7.25179,54.19403],[-7.23608,54.1935],[-7.23338,54.19792],[-7.24317,54.20076],[-7.24892,54.1977],[-7.25183,54.20201],[-7.24119,54.20623],[-7.23094,54.20578],[-7.23269,54.20912],[-7.22188,54.21607],[-7.20643,54.2117],[-7.18506,54.22485],[-7.17055,54.21742],[-7.14721,54.22488],[-7.14633,54.23008],[-7.15051,54.23165],[-7.14613,54.23983],[-7.15802,54.24434],[-7.13985,54.25298],[-7.15255,54.26235],[-7.16064,54.27405],[-7.17991,54.27144],[-7.17201,54.28627],[-7.21252,54.2985],[-7.19888,54.31117],[-7.17918,54.30946],[-7.1812,54.3397],[-7.15339,54.33514],[-7.10253,54.35811],[-7.10811,54.36677],[-7.06927,54.3899],[-7.05593,54.41056],[-7.02898,54.42135],[-7.00198,54.40832],[-6.98683,54.40829],[-6.97562,54.40014],[-6.96774,54.40145],[-6.90682,54.36966],[-6.89772,54.35075],[-6.87527,54.33853],[-6.86512,54.32568],[-6.85163,54.29137],[-6.87452,54.28677],[-6.87791,54.27918],[-6.86673,54.27522],[-6.85177,54.26489],[-6.83693,54.26658],[-6.82165,54.24346],[-6.81633,54.22299],[-6.80045,54.22108],[-6.80122,54.21338],[-6.77599,54.19965],[-6.75573,54.1987],[-6.74316,54.18258],[-6.73406,54.18566],[-6.72445,54.18127],[-6.70295,54.20036],[-6.69166,54.20018],[-6.68673,54.19398],[-6.669,54.19584],[-6.65248,54.18102],[-6.6433,54.17801],[-6.63467,54.16449],[-6.63179,54.14766],[-6.64081,54.14238],[-6.63935,54.13599],[-6.66149,54.1205],[-6.6481,54.10153],[-6.66119,54.0934],[-6.66458,54.06629],[-6.64681,54.05873],[-6.62501,54.03737],[-6.59291,54.04755],[-6.58905,54.05808],[-6.5597,54.0481],[-6.52897,54.05888],[-6.50442,54.05566],[-6.47824,54.07004],[-6.47919,54.07762],[-6.43601,54.05959],[-6.36314,54.07057],[-6.36589,54.09338],[-6.36293,54.09758],[-6.37104,54.11497],[-6.3522,54.11084],[-6.34242,54.1114],[-6.33589,54.10833],[-6.33636,54.09469],[-6.31808,54.09096],[-6.30903,54.10463],[-6.29165,54.11235],[-6.28246,54.11145],[-6.26272,54.09786],[-5.35583,53.72597],[-7.0752,49.23912],[-1.83472,49.02346],[-2.12036,49.94415],[1.97754,51.13111]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-65.2,18.7],[-65,16.3],[-63.7,19.2],[-65.2,18.7]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-63,-50.5],[-55,-51],[-60,-54],[-63,-50.5]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-139.19952,60.08402],[-141,60.30621],[-141,76],[-169,68.63655],[-169,65.20147],[-180,61],[-180,-4],[-154,9],[-133.76404,54.54021],[-130.73868,54.71986],[-129.96277,55.29163],[-130.15228,55.7758],[-130.01787,55.90688],[-130.00362,56.00798],[-130.10284,56.12336],[-130.24498,56.09656],[-130.42625,56.14249],[-131.87439,56.79787],[-135.02884,59.56285],[-135.11759,59.62306],[-135.15827,59.6261],[-135.47928,59.79822],[-136.28677,59.57955],[-136.30531,59.46462],[-136.36836,59.44898],[-136.47697,59.46558],[-137.19727,59.01935],[-139.19952,60.08402]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-111.96064,48.99841],[-121.22623,49.00049],[-122.26513,49.00246],[-122.7565,49.00208],[-123.32218,49.00218],[-122.97821,48.76524],[-123.2666,48.69821],[-123.21991,48.21186],[-125.80444,48.60749],[-124.32129,31.54109],[-117.125,32.53429],[-116.82417,32.55996],[-115.88036,32.63735],[-115.49738,32.66486],[-114.71984,32.71877],[-114.7649,32.64602],[-114.80885,32.60959],[-114.81481,32.49451],[-112.81743,31.88004],[-111.07481,31.33224],[-109.56051,31.33402],[-108.20847,31.33384],[-108.20838,31.78363],[-106.52847,31.78391],[-106.52781,31.78086],[-106.52249,31.77501],[-106.51249,31.76933],[-106.50988,31.7612],[-106.50709,31.76123],[-106.48896,31.74806],[-106.48473,31.74769],[-106.4719,31.75101],[-106.46816,31.75897],[-106.45434,31.76466],[-106.45035,31.76426],[-106.43516,31.75492],[-106.41484,31.75101],[-106.37864,31.73021],[-106.37225,31.71174],[-106.34924,31.69633],[-106.33289,31.66178],[-106.3068,31.62459],[-106.28079,31.56179],[-106.24775,31.54226],[-106.2329,31.49982],[-106.2105,31.46857],[-106.08201,31.39863],[-106.00554,31.39233],[-105.76401,31.17051],[-105.58548,31.06117],[-105.56419,30.98526],[-104.99153,30.6639],[-104.97162,30.60896],[-104.90639,30.57822],[-104.83772,30.38117],[-104.70177,30.20567],[-104.68048,29.92399],[-104.57611,29.77838],[-104.51157,29.63674],[-104.39758,29.57047],[-104.39278,29.55293],[-104.05769,29.32173],[-103.79883,29.2581],[-103.78196,29.26555],[-103.76759,29.22799],[-103.14102,28.93666],[-102.86087,29.2217],[-102.65076,29.79418],[-101.41068,29.73457],[-101.26511,29.51372],[-101.05997,29.452],[-101.04083,29.38038],[-100.96303,29.34735],[-100.94406,29.34369],[-100.94071,29.33351],[-100.92775,29.32663],[-100.89814,29.30957],[-100.87818,29.28086],[-100.80076,29.2238],[-100.76437,29.15981],[-100.67047,29.08663],[-100.6412,28.91299],[-100.63236,28.90255],[-100.61296,28.89939],[-100.534,28.75622],[-100.51495,28.74531],[-100.50705,28.7143],[-100.51203,28.70666],[-100.51014,28.69127],[-100.50048,28.66186],[-100.45547,28.6381],[-100.44697,28.60743],[-100.35599,28.45239],[-100.34946,28.39653],[-100.29488,28.31315],[-100.29591,28.27324],[-100.17197,28.17493],[-99.93645,27.9568],[-99.87722,27.80173],[-99.79671,27.73338],[-99.772,27.72532],[-99.74556,27.69979],[-99.71947,27.65981],[-99.5957,27.59974],[-99.54094,27.60537],[-99.53055,27.57973],[-99.52034,27.55782],[-99.52802,27.49773],[-99.50141,27.49986],[-99.48755,27.49518],[-99.47897,27.48421],[-99.48661,27.46453],[-99.49534,27.44861],[-99.48927,27.40941],[-99.53957,27.31565],[-99.43588,27.19678],[-99.46404,27.01968],[-99.16698,26.56039],[-99.17474,26.53939],[-99.12698,26.51958],[-99.1135,26.42954],[-99.08355,26.39625],[-99.06007,26.39737],[-99.03634,26.41255],[-99.02042,26.40598],[-99.01291,26.39364],[-98.95686,26.38641],[-98.9566,26.37365],[-98.94523,26.36949],[-98.90013,26.36419],[-98.89905,26.35454],[-98.80305,26.36626],[-98.78254,26.30511],[-98.66667,26.23457],[-98.58496,26.24647],[-98.57951,26.23434],[-98.56519,26.23987],[-98.56294,26.22464],[-98.50599,26.20858],[-98.44806,26.21236],[-98.38617,26.15721],[-98.34176,26.15278],[-98.33579,26.1388],[-98.30626,26.10003],[-98.28841,26.10512],[-98.26524,26.0914],[-98.19898,26.06411],[-98.09577,26.05698],[-98.07568,26.06667],[-98.08302,26.03396],[-97.9771,26.04136],[-97.9532,26.06179],[-97.81643,26.04475],[-97.77017,26.02439],[-97.73884,26.02902],[-97.5289,25.90648],[-97.52151,25.88625],[-97.50615,25.89031],[-97.49851,25.89903],[-97.49637,25.89641],[-97.49748,25.88008],[-97.49422,25.87981],[-97.48847,25.88564],[-97.46409,25.88174],[-97.42607,25.842],[-97.36856,25.8396],[-97.26231,25.94724],[-80.81543,24.00633],[-66.87378,44.77794],[-67.16148,45.16715],[-67.2286,45.16739],[-67.26246,45.18797],[-67.28311,45.19175],[-67.28959,45.18784],[-67.29332,45.17568],[-67.29049,45.17317],[-67.3001,45.16776],[-67.3025,45.16122],[-67.29761,45.14766],[-67.33975,45.1255],[-67.40524,45.16122],[-67.40387,45.17139],[-67.4818,45.27682],[-67.42172,45.38543],[-67.45262,45.41008],[-67.50498,45.4889],[-67.41623,45.50105],[-67.42219,45.55661],[-67.42902,45.56833],[-67.42331,45.57154],[-67.42498,45.57836],[-67.45193,45.60323],[-67.77981,45.6738],[-67.79019,47.06776],[-67.88006,47.1067],[-67.91319,47.14793],[-67.92598,47.15418],[-67.95181,47.1875],[-68.02374,47.23915],[-68.13017,47.29309],[-68.17669,47.32893],[-68.24046,47.35354],[-68.32809,47.36005],[-68.36363,47.35476],[-68.38054,47.34167],[-68.38509,47.30321],[-68.37367,47.28796],[-68.4377,47.28232],[-68.47916,47.29623],[-68.51074,47.29885],[-68.54593,47.28441],[-68.58408,47.28482],[-68.59777,47.27134],[-68.59271,47.25762],[-68.61889,47.24148],[-68.68936,47.24125],[-68.71768,47.23676],[-68.80128,47.21423],[-68.89629,47.17676],[-69.05354,47.24847],[-69.04924,47.41798],[-69.22425,47.45961],[-69.99729,46.69558],[-70.0569,46.4149],[-70.25551,46.10871],[-70.29001,46.09431],[-70.39919,45.80667],[-70.83229,45.40062],[-70.80794,45.37878],[-70.82663,45.2367],[-70.87538,45.23453],[-70.92138,45.28099],[-70.90645,45.30918],[-71.0109,45.34798],[-71.08429,45.30556],[-71.1454,45.24226],[-71.20525,45.25278],[-71.28925,45.30097],[-71.41405,45.23513],[-71.43044,45.12381],[-71.49692,45.06991],[-71.50623,45.04878],[-71.49284,45.03629],[-71.50027,45.01372],[-71.79359,45.01075],[-72.08774,45.00581],[-72.14155,45.00568],[-72.15282,45.00609],[-72.17142,45.00584],[-72.25847,45.00436],[-72.38795,45.00626],[-72.4496,45.00863],[-72.5356,45.00936],[-72.66257,45.01523],[-72.82537,45.01642],[-73.08466,45.01561],[-73.45219,45.00875],[-74.14699,44.99145],[-74.33753,44.9923],[-74.50786,44.99798],[-74.66158,44.99949],[-74.71244,44.99734],[-74.75887,44.98708],[-74.76368,45.00632],[-74.78977,45.00365],[-74.82376,45.01773],[-74.94186,44.98229],[-75.30098,44.83883],[-75.30304,44.82836],[-75.59418,44.6457],[-75.97269,44.33502],[-75.97295,44.34595],[-76.00059,44.34797],[-76.17645,44.2865],[-76.18744,44.22158],[-76.88782,43.82759],[-79.16851,43.32168],[-79.05487,43.25371],[-79.05092,43.169],[-79.04603,43.16093],[-79.04208,43.13942],[-79.07002,43.12038],[-79.06015,43.114],[-79.0568,43.10474],[-79.0774,43.07861],[-78.9996,43.05484],[-79.02311,43.02071],[-79.02552,42.99473],[-78.96235,42.9573],[-78.91188,42.9426],[-78.90398,42.89181],[-82.42767,41.47978],[-83.14316,42.03807],[-83.12805,42.23843],[-83.09715,42.29052],[-83.07252,42.31515],[-82.94575,42.34332],[-82.59676,42.5479],[-82.51368,42.61785],[-82.5108,42.66464],[-82.4675,42.76415],[-82.48055,42.80573],[-82.45497,42.9284],[-82.41334,42.97099],[-82.42596,42.99536],[-82.15851,43.39507],[-83.53729,46.098],[-83.96301,46.05036],[-84.11021,46.23851],[-84.09794,46.25656],[-84.11613,46.26878],[-84.11905,46.31516],[-84.10721,46.3218],[-84.14394,46.41076],[-84.11682,46.51576],[-84.13536,46.53218],[-84.16162,46.5284],[-84.21621,46.53891],[-84.26994,46.49189],[-84.36092,46.50997],[-84.55284,46.4407],[-84.95178,46.77185],[-89.59179,48.00307],[-89.67547,48.00371],[-90.87204,48.25943],[-91.41312,48.06753],[-92.99377,48.62474],[-93.34877,48.62604],[-93.35529,48.61124],[-93.37074,48.60584],[-93.39812,48.60369],[-93.40542,48.61089],[-93.43846,48.59478],[-93.46859,48.59205],[-93.45735,48.56667],[-93.46533,48.54593],[-93.64763,48.51751],[-93.80625,48.51888],[-93.80642,48.58047],[-93.83328,48.62582],[-93.84865,48.63064],[-93.93388,48.6326],[-94.01327,48.64471],[-94.16176,48.64697],[-94.25025,48.65463],[-94.24931,48.67827],[-94.26046,48.69816],[-94.30578,48.71073],[-94.32758,48.70433],[-94.36123,48.70478],[-94.38406,48.71135],[-94.41629,48.71067],[-94.44294,48.69266],[-94.53615,48.7024],[-94.55031,48.71419],[-94.58894,48.71928],[-94.69425,48.77938],[-94.70129,48.83376],[-94.68996,48.83953],[-94.68395,48.99914],[-111.96064,48.99841]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[180,55],[170,53],[180,49],[180,55]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[143,22],[147,22],[147,12],[143,12],[143,22]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-171.5,-10],[-171,-15],[-167,-15],[-171.5,-10]]]}}]},"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","gu","hi","hu","is","id","it","ja","kn","ko","lv","lij","lt","ml","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 (must enable Photo Overlay)","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"}},"internet_access/fee":{"label":"Internet Access Fee"},"kerb":{"label":"Curb Ramp"},"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..."},"parallel_direction":{"label":"Direction","options":{"forward":"Forward","backward":"Backward"}},"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"},"rooms":{"label":"Rooms"},"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"},"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"},"stars":{"label":"Stars"},"stop":{"label":"Stop Type","options":{"all":"All Ways","minor":"Minor Road"}},"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,grill"},"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*,med*,prescription"},"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/defibrillator":{"name":"Defibrillator","terms":""},"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/give_way":{"name":"Yield Sign","terms":"give way,yield,sign"},"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,halt,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/bridge":{"name":"Bridge","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":"Drugstore","terms":"med*,drug*,gift"},"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":{"pub":{"The Green Man":{"count":54},"Kings Arms":{"count":77},"The White Horse":{"count":223},"The White Hart":{"count":238},"Royal Oak":{"count":175},"The Red Lion":{"count":253},"The Kings Arms":{"count":60},"The Star":{"count":74},"The Cross Keys":{"count":58},"The Wheatsheaf":{"count":121},"The Kings Head":{"count":63},"The Castle":{"count":63},"The Railway":{"count":105},"The Bell":{"count":129},"The Bull":{"count":75},"The Plough":{"count":187},"The George":{"count":115},"The Royal Oak":{"count":216},"The Fox":{"count":78},"Prince of Wales":{"count":86},"The Rising Sun":{"count":78},"The Chequers":{"count":72},"The Crown":{"count":253},"The Swan":{"count":149},"The Crown Inn":{"count":76},"The Ship":{"count":90},"The Victoria":{"count":69},"New Inn":{"count":104},"The Anchor":{"count":67},"Royal Hotel":{"count":62},"Red Lion":{"count":207},"Cross Keys":{"count":63},"The Bell Inn":{"count":57},"The White Lion":{"count":73},"The Black Horse":{"count":97},"Rose and Crown":{"count":85},"Commercial Hotel":{"count":54},"The White Swan":{"count":53},"The Greyhound":{"count":98},"The New Inn":{"count":119},"Kings Head":{"count":62},"The Angel":{"count":57},"The Queens Head":{"count":55},"Black Bull":{"count":54},"Black Horse":{"count":52},"The Swan Inn":{"count":54},"Rose & Crown":{"count":58},"The Albion":{"count":54},"The Cricketers":{"count":55},"The Ship Inn":{"count":94},"The Star Inn":{"count":51},"Queens Head":{"count":56},"Irish Pub":{"count":99},"魚民":{"count":84}},"fuel":{"76":{"count":498},"Neste":{"count":177},"Shell":{"count":10789},"Agip":{"count":2524},"Migrol":{"count":78},"Avia":{"count":1014},"Texaco":{"count":1057},"Total":{"count":3182},"Statoil":{"count":668},"BP":{"count":3517},"Topaz":{"count":98},"Esso":{"count":4179},"Jet":{"count":461},"OMV":{"count":929},"Aral":{"count":1383},"Tesco":{"count":214},"JET":{"count":212},"Morrisons":{"count":122},"United":{"count":155},"Sainsbury's":{"count":72},"Canadian Tire":{"count":90},"Mobil":{"count":1150},"Q8":{"count":1325},"Caltex":{"count":1304},"Petro-Canada":{"count":736},"ABC":{"count":84},"ARAL":{"count":385},"CEPSA":{"count":972},"BFT":{"count":102},"Petron":{"count":1283},"Intermarché":{"count":591},"Total Access":{"count":173},"Auchan":{"count":71},"Super U":{"count":147},"Carrefour":{"count":291},"Station Service E. Leclerc":{"count":516},"Elan":{"count":174},"Shell Express":{"count":171},"Hess":{"count":213},"F24":{"count":56},"Gulf":{"count":330},"PTT":{"count":241},"Teboil":{"count":111},"St1":{"count":126},"HEM":{"count":241},"GALP":{"count":762},"پمپ بنزین":{"count":266},"Avanti":{"count":110},"Cepsa":{"count":208},"OK":{"count":205},"Tinq":{"count":215},"Repsol":{"count":775},"Westfalen":{"count":94},"Esso Express":{"count":150},"Slovnaft":{"count":261},"Raiffeisenbank":{"count":129},"Tamoil":{"count":913},"bft":{"count":185},"Afriquia":{"count":117},"Engen":{"count":307},"Sasol":{"count":74},"Jetti":{"count":59},"LPG":{"count":309},"Coop":{"count":67},"Orlen":{"count":1074},"Kobil":{"count":81},"Oilibya":{"count":87},"Tango":{"count":143},"Star":{"count":352},"Петрол":{"count":111},"EKO":{"count":110},"Turmöl":{"count":74},"OIL!":{"count":111},"Flying V":{"count":146},"Ultramar":{"count":246},"CARREFOUR":{"count":73},"Eni":{"count":469},"Irving":{"count":149},"Lukoil":{"count":832},"Coles Express":{"count":262},"Pioneer":{"count":93},"Speedway":{"count":381},"Uno-X":{"count":57},"7-Eleven":{"count":892},"Agrola":{"count":90},"Husky":{"count":188},"Sunoco":{"count":684},"Sheetz":{"count":248},"Turkey Hill":{"count":56},"MOL":{"count":380},"OKQ8":{"count":209},"Petronas":{"count":254},"Mol":{"count":60},"Benzina":{"count":148},"Газпромнефть":{"count":1093},"Роснефть":{"count":805},"Opet":{"count":105},"Marathon":{"count":513},"Vito":{"count":63},"Valero":{"count":656},"Neste A24":{"count":56},"REPSOL":{"count":1502},"Lotos":{"count":260},"Api":{"count":253},"Chevron":{"count":1505},"ARCO":{"count":128},"Pemex":{"count":1340},"ТНК":{"count":594},"Petrol Ofisi":{"count":115},"eni":{"count":84},"Petrom":{"count":330},"PETRONOR":{"count":196},"Rompetrol":{"count":193},"Copec":{"count":541},"Petrobras":{"count":341},"АЗС":{"count":1060},"Pilot":{"count":57},"LUKOIL":{"count":64},"SB Tank":{"count":56},"Arco":{"count":391},"Лукойл":{"count":2285},"ОМВ":{"count":58},"Liberty":{"count":79},"Bliska":{"count":162},"Moya":{"count":52},"Erg":{"count":510},"Eneos":{"count":121},"P T T":{"count":55},"Citgo":{"count":547},"IP":{"count":967},"Metano":{"count":248},"Сургутнефтегаз":{"count":75},"ENI":{"count":79},"Eko":{"count":75},"Indipend.":{"count":144},"TotalErg":{"count":188},"IES":{"count":63},"Star Oil":{"count":77},"ERG":{"count":78},"Cenex":{"count":194},"ПТК":{"count":97},"Белоруснефть":{"count":64},"HP":{"count":127},"Phillips 66":{"count":332},"Q1":{"count":53},"ป.ต.ท.":{"count":449},"Circle K":{"count":313},"Woolworths Petrol":{"count":151},"PSO":{"count":80},"Татнефть":{"count":311},"Ceypetco":{"count":63},"Seaoil":{"count":55},"Sea Oil":{"count":65},"Phoenix":{"count":182},"Cosmo":{"count":61},"Gazprom":{"count":69},"Kwik Trip":{"count":169},"КазМунайГаз":{"count":163},"Wawa":{"count":143},"Pertamina":{"count":274},"เอสโซ่":{"count":85},"Terpel":{"count":337},"Z":{"count":93},"Socar":{"count":71},"Bharat Petroleum":{"count":166},"Indian Oil":{"count":327},"АГЗС":{"count":897},"JOMO":{"count":56},"Holiday":{"count":140},"ОККО":{"count":294},"Exxon":{"count":883},"コスモ石油":{"count":325},"出光":{"count":478,"tags":{"name:en":"IDEMITSU"}},"ENEOS":{"count":1292},"WOG":{"count":323},"CAMPSA":{"count":507},"Casey's General Store":{"count":307},"Башнефть":{"count":137},"Kangaroo":{"count":113},"Puma":{"count":315},"Hindustan Petroleum":{"count":58},"Lotos Optima":{"count":64},"MEROIL":{"count":74},"TEXACO":{"count":54},"ゼネラル":{"count":58},"Helios":{"count":61},"Татнефтепродукт":{"count":69},"昭和シェル":{"count":215},"BEBECO":{"count":77},"НК Альянс":{"count":97},"คาลเท็กซ์":{"count":72},"JA-SS":{"count":52},"Sinclair":{"count":163},"Costco":{"count":73},"Conoco":{"count":327},"SPBU":{"count":205},"Макпетрол":{"count":109},"Sam's Club":{"count":51},"Ipiranga":{"count":159},"1-2-3":{"count":68},"Posto Ipiranga":{"count":168},"Drummed Fuel":{"count":156},"Rubis":{"count":107},"БРСМ-Нафта":{"count":58},"АГНКС":{"count":54},"QuikTrip":{"count":209},"เชลล์":{"count":94},"Stewart's":{"count":66},"Cumberland Farms":{"count":80},"Posto BR":{"count":191},"台灣中油":{"count":147},"บางจาก":{"count":195},"محطه وقود":{"count":59},"ANP":{"count":163},"Kum & Go":{"count":113},"Primax":{"count":147},"Mobile":{"count":56},"Укрнафта":{"count":201},"Пропан":{"count":57},"Petrolimex":{"count":143},"Tela":{"count":171},"Авіас":{"count":97},"Sokimex":{"count":82},"Safeway":{"count":53},"Delta":{"count":144},"H-E-B Gas":{"count":161},"Kroger Fuel":{"count":53},"Sinopec Fuel":{"count":112},"Posto Shell":{"count":94},"RaceTrac":{"count":71},"BR":{"count":149},"Costco Gas":{"count":58},"Murphy USA":{"count":135},"پمپ گاز":{"count":62},"Royal Farms":{"count":64},"Нефтьмагистраль":{"count":64},"PT":{"count":138},"Uno":{"count":134},"Independent Fuel Station":{"count":56},"エネオス":{"count":68},"P T":{"count":62},"Pecsa":{"count":54},"Bangchak":{"count":68},"CNG":{"count":315},"Posto":{"count":64},"NP":{"count":67},"Posto Atem":{"count":58},"GNV":{"count":79},"Minipump":{"count":116},"Petroperu":{"count":84},"Petrochina":{"count":103}},"place_of_worship":{"Saint Mary":{"count":99},"All Saints":{"count":410},"St Andrew's":{"count":60},"Catholic Church":{"count":173},"Christ Church":{"count":366},"Saint Mary's":{"count":101},"Holy Trinity":{"count":221},"St Mary's Church":{"count":183},"Saint Marks Church":{"count":196},"St Mary":{"count":138},"Trinity Baptist Church":{"count":202},"Sacred Heart Church":{"count":213},"St Peter":{"count":93},"All Saints Church":{"count":217},"Northside Baptist Church":{"count":69},"Saint John the Baptist":{"count":58},"Zion Baptist Church":{"count":71},"Trinity Methodist Church":{"count":93},"Central Baptist Church":{"count":206},"Sacred Heart Catholic Church":{"count":232},"Kingdom Hall":{"count":340},"The Church of Jesus Christ of Latter-day Saints":{"count":3095},"Salvation Army":{"count":150},"Baptist Church":{"count":166},"St John the Baptist":{"count":138},"Saint Mary the Virgin":{"count":70},"Marienkapelle":{"count":338},"Neuapostolische Kirche":{"count":771},"St Mary the Virgin":{"count":164},"Seventh Day Adventist":{"count":65},"St. Mary's Church":{"count":105},"Friedhofskapelle":{"count":810},"Faith Baptist Church":{"count":241},"Grace Lutheran Church":{"count":253},"Church of God":{"count":786},"Kingdom Hall of Jehovah's Witnesses":{"count":1029},"United Reformed Church":{"count":73},"St. Mary's":{"count":72},"St Peter's":{"count":103},"St Andrew's Church":{"count":70},"Kapelle":{"count":1666},"St. Josef":{"count":224},"St. Laurentius":{"count":259},"Church of the Good Shepherd":{"count":99},"Friedenskirche":{"count":264},"Hubertuskapelle":{"count":157},"St Mary's":{"count":218},"Kingdom Hall of Jehovahs Witnesses":{"count":601},"Saint Mary's Church":{"count":111},"Pauluskirche":{"count":110},"Martinskirche":{"count":139},"St Nicholas":{"count":107},"Erlöserkirche":{"count":137},"Saint Pauls Church":{"count":517},"Saint Josephs Church":{"count":131},"Mariä Himmelfahrt":{"count":225},"Heilig-Geist-Kirche":{"count":90},"St. Peter und Paul":{"count":314},"Königreichssaal":{"count":88},"Auferstehungskirche":{"count":159},"St. Johannes Baptist":{"count":68},"Herz Jesu":{"count":95},"Kirche":{"count":302},"Grace Chapel":{"count":54},"Katholische Kirche":{"count":172},"Sankt Michael":{"count":123},"Christuskirche":{"count":423},"St. Michael":{"count":430},"Lutherkirche":{"count":118},"Church":{"count":406},"Evangelische Kirche":{"count":671},"Johanniskirche":{"count":54},"St. Georg":{"count":319},"Sankt Laurentius":{"count":65},"St. Stephanus":{"count":98},"Chiesa di Sant'Antonio":{"count":86},"Église":{"count":1017},"Saint Joseph Church":{"count":136},"Bible Baptist Church":{"count":166},"Church of Christ":{"count":1545},"Saint Peter":{"count":63},"Kreuzkirche":{"count":172},"Seventh Day Adventist Church":{"count":560},"St. Sebastian":{"count":107},"Saint Peters Church":{"count":231},"Methodist Church":{"count":236},"Heilig-Kreuz-Kirche":{"count":66},"Herz-Jesu-Kirche":{"count":111},"Maria Hilf":{"count":55},"Shady Grove Church":{"count":323},"St. Johannes der Täufer":{"count":129},"Thomaskirche":{"count":57},"Stadtkirche":{"count":108},"Heilige Familie":{"count":56},"Jehovas Zeugen":{"count":72},"Versöhnungskirche":{"count":83},"St Mary Magdalene":{"count":57},"Sankt Marien":{"count":87},"Sankt Martin":{"count":145},"St James":{"count":102},"St Michael and All Angels":{"count":59},"Waldkapelle":{"count":72},"Sankt Nikolaus":{"count":102},"St. Joseph":{"count":111},"Friends Meeting House":{"count":60},"St. Katharina":{"count":76},"Stupa":{"count":85},"Jézus Szíve-templom":{"count":80},"Concord Church":{"count":185},"Congregational Church":{"count":58},"Chapelle":{"count":480},"Церковь":{"count":265},"Kreuzkapelle":{"count":69},"Heilig Kreuz":{"count":117},"St. Jakob":{"count":56},"St. Leonhard":{"count":108},"St John the Evangelist":{"count":82},"Anglican Church":{"count":92},"Heilig Geist":{"count":54},"St Peter's Church":{"count":95},"St. Ulrich":{"count":95},"Emmanuel Church":{"count":183},"Johanneskirche":{"count":191},"Martin-Luther-Kirche":{"count":128},"Foursquare Gospel Church":{"count":57},"St. Marien":{"count":216},"Liebfrauenkirche":{"count":73},"Seventh-Day Adventist Church":{"count":52},"Biserica":{"count":2164},"Evangelisch-Freikirchliche Gemeinde":{"count":99},"Marienkirche":{"count":192},"Iglesia de San Juan":{"count":56},"St. Antonius":{"count":121},"St Paul's":{"count":53},"St John's Church":{"count":55},"St. Jakobus":{"count":106},"St Peters":{"count":54},"Masjid":{"count":1950},"Holy Trinity Church":{"count":202},"Redeemer Lutheran Church":{"count":112},"St Andrew":{"count":68},"Sankt Georg":{"count":97},"St. Andreas":{"count":139},"St Andrews":{"count":54},"Αγία Παρασκευή":{"count":73},"San Francesco":{"count":92},"St. Martin":{"count":411},"St. Nikolaus":{"count":340},"Maria Himmelfahrt":{"count":115},"Synagogue":{"count":83},"St. Johann Baptist":{"count":58},"The Salvation Army":{"count":86},"Iglesia de San Andrés":{"count":58},"Surau":{"count":159},"Trinity Church":{"count":456},"Mariengrotte":{"count":90},"Saint Andrews Church":{"count":121},"熊野神社":{"count":99},"Chiesa di San Martino":{"count":133},"Iglesia":{"count":962},"Mosque":{"count":590},"Προφήτης Ηλίας":{"count":90},"Calvary Church":{"count":447},"St. Peter":{"count":161},"St. Bartholomäus":{"count":139},"Sankt Andreas":{"count":55},"Церковь Преображения Господня":{"count":100},"Saint Peter's Church":{"count":53},"Chapelle Sainte-Anne":{"count":92},"First Baptist Church":{"count":2346},"Igreja Matriz":{"count":213},"Saint Josephs Catholic Church":{"count":124},"Église Saint-Michel":{"count":229},"St. Johannes":{"count":160},"Sankt Josef":{"count":74},"Chiesa di San Giuseppe":{"count":137},"St. Bonifatius":{"count":108},"Chiesa di San Giorgio":{"count":134},"Chiesa di San Giovanni Battista":{"count":180},"St. Barbara":{"count":85},"New Life Church":{"count":84},"Iglesia ni Cristo":{"count":237},"Iglesia Ni Cristo":{"count":299},"Calvary Baptist Church":{"count":547},"United Methodist Church":{"count":638},"Bethany Baptist Church":{"count":105},"Saint Francis Church":{"count":54},"Holy Family Church":{"count":74},"Wesleyan Church":{"count":101},"United Pentecostal Church":{"count":158},"Trinity Lutheran Church":{"count":458},"Antoniuskapelle":{"count":94},"Emmanuel Baptist Church":{"count":149},"Chapel":{"count":230},"San Pietro":{"count":85},"St. Ägidius":{"count":57},"Iglesia de San Martín":{"count":66},"Ermita de San Roque":{"count":125},"Igreja Batista":{"count":59},"St. Elisabeth":{"count":102},"St. Anna":{"count":124},"Dorfkirche":{"count":226},"Assembleia de Deus":{"count":64},"St. Martinus":{"count":65},"Hope Church":{"count":65},"Biserică":{"count":709},"St. Pankratius":{"count":79},"Temple":{"count":504},"Biserica Ortodoxă":{"count":62},"Holy Family Catholic Church":{"count":94},"諏訪神社":{"count":86},"Chiesa di San Rocco":{"count":265},"Chiesa":{"count":203},"八坂神社":{"count":66},"Klosterkirche":{"count":76},"Église Saint-Pierre":{"count":997},"Sankt Peter und Paul":{"count":67},"St. Maria":{"count":82},"New Jerusalem Church":{"count":51},"San Martino":{"count":79},"Église Saint-Antoine":{"count":80},"St Michael's":{"count":61},"St. Vitus":{"count":106},"Santo Stefano":{"count":52},"San Giovanni Battista":{"count":78},"Markuskirche":{"count":65},"Calvary Chapel":{"count":57},"Dreifaltigkeitskirche":{"count":93},"Kapel":{"count":97},"Église Saint-Martin":{"count":1353},"Ermita de San Miguel":{"count":65},"Церковь Троицы Живоначальной":{"count":227},"Bethlehem Lutheran Church":{"count":88},"St. Johannis":{"count":60},"Maria Königin":{"count":56},"Chiesa di Sant'Andrea":{"count":57},"Mariä Heimsuchung":{"count":65},"Zion Lutheran Church":{"count":226},"Pfarrkirche":{"count":106},"Église Saint-Hilaire":{"count":109},"Holy Cross Church":{"count":86},"St. Margaretha":{"count":52},"Chiesa di Santa Lucia":{"count":70},"Ermita":{"count":109},"Saint Mary's Catholic Church":{"count":65},"Église Saint-Christophe":{"count":84},"Königreichssaal Jehovas Zeugen":{"count":59},"Trauerhalle":{"count":123},"Iglesia Parroquial":{"count":62},"Church of God of Prophecy":{"count":232},"Mount Olive Baptist Church":{"count":159},"Chiesa di San Giovanni":{"count":53},"Michaelskirche":{"count":59},"Άγιος Γεώργιος":{"count":97},"Reformierte Kirche":{"count":97},"Aussegnungshalle":{"count":106},"Wesley Church":{"count":72},"First United Methodist Church":{"count":880},"稲荷神社":{"count":85},"San Michele Arcangelo":{"count":57},"St. Mauritius":{"count":55},"svatý Václav":{"count":68},"San Lorenzo":{"count":79},"Iglesia de San Juan Bautista":{"count":90},"Evangelical Free Church":{"count":81},"Providence Church":{"count":252},"Église Saint-Vincent":{"count":105},"Chapelle Saint-Roch":{"count":106},"San Giuseppe":{"count":80},"First Presbyterian Church":{"count":1196},"Église Saint-Denis":{"count":138},"church":{"count":64},"Pentecostal Church":{"count":78},"Fellowship Baptist Church":{"count":105},"First Church of the Nazarene":{"count":306},"Iglesia Evangelica":{"count":74},"Iglesia de Nuestra Señora de la Asunción":{"count":66},"Mariä Geburt":{"count":52},"Grace Baptist Church":{"count":272},"Hope Lutheran Church":{"count":88},"San Giorgio":{"count":57},"St. Stephan":{"count":89},"Église Saint-Éloi":{"count":52},"Église Notre-Dame":{"count":548},"Chapelle Saint-Jean":{"count":53},"Église Sainte-Anne":{"count":71},"Chiesa di San Lorenzo":{"count":132},"Église Saint-Aubin":{"count":75},"Chapelle Saint-Pierre":{"count":51},"Saint Michaels Church":{"count":71},"Messiah Lutheran Church":{"count":75},"Madonna della Neve":{"count":58},"Saint Pauls Lutheran Church":{"count":141},"Church of God in Christ":{"count":141},"Immaculate Conception Catholic Church":{"count":86},"First Church of Christ Scientist":{"count":450},"Chiesa di Santa Maria Assunta":{"count":138},"New Apostolic Church":{"count":92},"Free Methodist Church":{"count":143},"Iglesia de Dios":{"count":66},"Apostolic Church":{"count":75},"Chiesa di San Francesco":{"count":108},"Ermita de San Antonio":{"count":53},"Église Saint-Joseph":{"count":114},"Church of the Nazarene":{"count":509},"Community of Christ":{"count":180},"Parroquia":{"count":92},"Santa Maria":{"count":202},"Мечеть":{"count":313},"Königreichsaal":{"count":54},"Ev. Kirche":{"count":122},"Iglesia de San Miguel":{"count":105},"Saint Paul Church":{"count":250},"Antioch Church":{"count":545},"Assembly of God Church":{"count":273},"Bethany Church":{"count":329},"Bethel Church":{"count":1055},"Bethel Lutheran Church":{"count":53},"Bethesda Church":{"count":94},"Bethlehem Church":{"count":499},"Beulah Church":{"count":179},"Christian Church":{"count":83},"Church of the Brethren":{"count":51},"Church of Jesus Christ":{"count":53},"Community Church":{"count":171},"County Line Church":{"count":91},"Ebenezer Church":{"count":349},"Evergreen Church":{"count":88},"Faith Lutheran Church":{"count":190},"Faith Church":{"count":203},"Fairview Church":{"count":388},"Fellowship Church":{"count":127},"Grace Church":{"count":312},"First Congregational Church":{"count":391},"Friendship Church":{"count":484},"Full Gospel Church":{"count":81},"Friends Church":{"count":51},"Harmony Church":{"count":162},"Immanuel Church":{"count":141},"Maple Grove Church":{"count":73},"Liberty Church":{"count":322},"Little Zion Church":{"count":71},"Midway Church":{"count":196},"Mount Bethel Church":{"count":57},"Mount Hope Church":{"count":98},"Mount Zion Church":{"count":1084},"Mount Pleasant Church":{"count":508},"Mount Olive Church":{"count":594},"Nazarene Church":{"count":60},"New Hope Church":{"count":701},"Oak Grove Church":{"count":592},"Pleasant Hill Church":{"count":482},"Pilgrim Church":{"count":73},"Pine Grove Church":{"count":292},"Pleasant View Church":{"count":137},"Pleasant Valley Church":{"count":203},"Pleasant Ridge Church":{"count":127},"Riverside Church":{"count":84},"Saint John Church":{"count":148},"Saint James Church":{"count":318},"Saint Lukes Church":{"count":193},"Saint Johns Church":{"count":664},"Saint Luke Church":{"count":87},"Saint Mark Church":{"count":51},"Saint Marys Church":{"count":311},"Saint Mary Church":{"count":64},"Tabernacle Church":{"count":97},"Saint Thomas Church":{"count":76},"Saint Stephens Church":{"count":74},"Salem Church":{"count":475},"Smith Chapel":{"count":80},"Springfield Church":{"count":74},"Union Church":{"count":519},"Union Chapel":{"count":182},"United Church":{"count":90},"Victory Church":{"count":90},"Zion Church":{"count":493},"Macedonia Church":{"count":423},"Saint Joseph Catholic Church":{"count":116},"Saint Pauls Episcopal Church":{"count":140},"Unity Church":{"count":86},"Central Church":{"count":111},"Shiloh Church":{"count":420},"All Saints Episcopal Church":{"count":98},"Saint Johns Catholic Church":{"count":71},"Locust Grove Church":{"count":67},"Greenwood Church":{"count":51},"Jehovah's Witnesses":{"count":69},"Bethel Baptist Church":{"count":255},"Calvary Lutheran Church":{"count":64},"Grace United Methodist Church":{"count":165},"Wesley United Methodist Church":{"count":111},"First Assembly of God Church":{"count":389},"Saint John's Episcopal Church":{"count":61},"Bethany Lutheran Church":{"count":86},"Saint Paul's Lutheran Church":{"count":62},"First Lutheran Church":{"count":195},"Saint John's Lutheran Church":{"count":69},"Bethel African Methodist Episcopal Church":{"count":122},"Church of the Living God":{"count":96},"Chapel Hill Church":{"count":77},"Mount Calvary Church":{"count":158},"First Church of God":{"count":235},"Saint Paul Lutheran Church":{"count":151},"Holy Trinity Lutheran Church":{"count":70},"Immaculate Conception Church":{"count":120},"Immanuel Baptist Church":{"count":127},"Prince of Peace Lutheran Church":{"count":70},"Shiloh Baptist Church":{"count":170},"Christ United Methodist Church":{"count":86},"Community Bible Church":{"count":58},"Trinity United Methodist Church":{"count":230},"Holy Trinity Episcopal Church":{"count":51},"Grace Bible Church":{"count":65},"Grace Episcopal Church":{"count":128},"Good Shepherd Lutheran Church":{"count":131},"First Church of Christ":{"count":81},"Presbyterian Church":{"count":160},"Saint James Episcopal Church":{"count":115},"Community Baptist Church":{"count":110},"Central United Methodist Church":{"count":56},"Trinity Episcopal Church":{"count":221},"First Missionary Baptist Church":{"count":59},"First Reformed Church":{"count":63},"Holy Cross Lutheran Church":{"count":63},"Peace Lutheran Church":{"count":108},"Saint John Lutheran Church":{"count":79},"First Free Will Baptist Church":{"count":51},"Immanuel Lutheran Church":{"count":190},"Faith United Methodist Church":{"count":70},"Berean Baptist Church":{"count":51},"First Methodist Church":{"count":191},"Bethlehem Baptist Church":{"count":97},"Second Baptist Church":{"count":211},"First Christian Church":{"count":663},"Saint Johns Lutheran Church":{"count":150},"Friendship Baptist Church":{"count":176},"Our Savior Lutheran Church":{"count":74},"Saint Marks Episcopal Church":{"count":76},"First United Presbyterian Church":{"count":84},"Christ Episcopal Church":{"count":160},"Saint Johns Episcopal Church":{"count":151},"Grace Community Church":{"count":64},"Liberty Baptist Church":{"count":124},"Pentecostal Church of God":{"count":98},"Saint Matthews Church":{"count":139},"Christ Lutheran Church":{"count":155},"Saint Paul United Methodist Church":{"count":70},"Victory Baptist Church":{"count":97},"Saint James Catholic Church":{"count":54},"Christ Community Church":{"count":56},"Saint Andrews Episcopal Church":{"count":91},"New Hope Baptist Church":{"count":230},"Westminster Presbyterian Church":{"count":150},"Trinity Evangelical Lutheran Church":{"count":57},"New Bethel Baptist Church":{"count":52},"Hopewell Baptist Church":{"count":60},"Pleasant Grove Church":{"count":487},"Mount Zion Baptist Church":{"count":221},"Mount Calvary Baptist Church":{"count":91},"Saint Paul Baptist Church":{"count":77},"Cornerstone Baptist Church":{"count":51},"First Pentecostal Church":{"count":76},"Cornerstone Church":{"count":53},"United Church of Christ":{"count":95},"Crossroads Church":{"count":110},"Covenant Presbyterian Church":{"count":63},"Asbury Church":{"count":64},"Asbury United Methodist Church":{"count":80},"Beech Grove Church":{"count":76},"Berea Church":{"count":59},"Cedar Grove Church":{"count":213},"Center Church":{"count":79},"Corinth Church":{"count":176},"Galilee Church":{"count":91},"Hebron Church":{"count":114},"Hickory Grove Church":{"count":115},"Hopewell Church":{"count":288},"Jerusalem Church":{"count":139},"Lebanon Church":{"count":111},"Macedonia Baptist Church":{"count":138},"New Bethel Church":{"count":216},"New Liberty Church":{"count":57},"New Prospect Church":{"count":88},"New Home Church":{"count":96},"New Salem Church":{"count":132},"New Zion Church":{"count":164},"Mount Zion United Methodist Church":{"count":51},"Mount Gilead Church":{"count":81},"Mount Hebron Church":{"count":56},"Mount Pisgah Church":{"count":139},"Mount Moriah Church":{"count":196},"Mount Nebo Church":{"count":108},"Oak Grove Baptist Church":{"count":90},"Oak Hill Church":{"count":95},"Oakland Church":{"count":60},"Mount Carmel Church":{"count":284},"Mount Olivet Church":{"count":101},"Mount Vernon Church":{"count":152},"Mount Tabor Church":{"count":106},"Rose Hill Church":{"count":75},"Palestine Church":{"count":63},"Rock Springs Church":{"count":51},"Pisgah Church":{"count":72},"Sharon Church":{"count":90},"Union Grove Church":{"count":173},"Saint Patricks Church":{"count":91},"Walnut Grove Church":{"count":102},"Wesley Chapel":{"count":174},"White Oak Church":{"count":82},"Memorial Baptist Church":{"count":57},"Bethel United Methodist Church":{"count":78},"Central Christian Church":{"count":84},"Saint Thomas Episcopal Church":{"count":54},"Saint Marys Catholic Church":{"count":180},"Zion Hill Church":{"count":146},"Saint Patricks Catholic Church":{"count":79},"Mount Pleasant Baptist Church":{"count":76},"Philadelphia Church":{"count":82},"Good Hope Church":{"count":125},"Mount Carmel Baptist Church":{"count":72},"Central Church of Christ":{"count":68},"First Southern Baptist Church":{"count":95},"Union Baptist Church":{"count":131},"Holy Cross Catholic Church":{"count":54},"Central Presbyterian Church":{"count":51},"Trinity Presbyterian Church":{"count":70},"Saint Marys Roman Catholic Church":{"count":54},"Temple Baptist Church":{"count":119},"Saint Peters Episcopal Church":{"count":54},"Antioch Baptist Church":{"count":173},"Sacred Heart Roman Catholic Church":{"count":59},"First United Pentecostal Church":{"count":85},"Holy Trinity Catholic Church":{"count":54},"Calvary United Methodist Church":{"count":65},"Providence Baptist Church":{"count":77},"Mount Sinai Church":{"count":107},"Morning Star Church":{"count":126},"Saint James Baptist Church":{"count":59},"Pleasant Grove Baptist Church":{"count":89},"Pleasant Hill Baptist Church":{"count":68},"Tabernacle Baptist Church":{"count":108},"Morning Star Baptist Church":{"count":72},"Saint Paul African Methodist Episcopal Church":{"count":58},"Saint Peter Church":{"count":62},"Salem Baptist Church":{"count":79},"Highland Church":{"count":61},"Smyrna Church":{"count":69},"Saint Pauls United Methodist Church":{"count":52},"Saint Mary's Roman Catholic Church":{"count":55},"Fairview Baptist Church":{"count":54},"Saint Lukes Episcopal Church":{"count":69},"Église Saint-Jean-Baptiste":{"count":435},"Церковь Рождества Христова":{"count":117},"Pilgrim Rest Church":{"count":80},"Chestnut Grove Church":{"count":64},"Ebenezer Baptist Church":{"count":100},"Enon Church":{"count":68},"Faith Tabernacle":{"count":55},"Holiness Church":{"count":94},"Mill Creek Church":{"count":61},"Mountain View Church":{"count":106},"Oak Ridge Church":{"count":65},"Sardis Church":{"count":86},"Sweet Home Church":{"count":59},"Union Hill Church":{"count":119},"Mount Zion Church (historical)":{"count":68},"Pentecostal Holiness Church":{"count":69},"Liberty Hill Church":{"count":77},"New Hope Church (historical)":{"count":55},"Bethel Church (historical)":{"count":84},"Saint John Baptist Church":{"count":56},"Saint Marys Episcopal Church":{"count":51},"Southside Baptist Church":{"count":94},"Holly Grove Church":{"count":57},"Jones Chapel":{"count":73},"Mount Pilgrim Church":{"count":52},"Pilgrims Rest Church":{"count":68},"Pine Hill Church":{"count":57},"Piney Grove Church":{"count":170},"Poplar Springs Church":{"count":57},"Prospect Church":{"count":98},"Red Hill Church":{"count":72},"Rock Hill Church":{"count":74},"Spring Hill Church":{"count":178},"Beulah Baptist Church":{"count":51},"Magnolia Church":{"count":57},"Westside Baptist Church":{"count":55},"First Assembly of God":{"count":61},"First Church of Christ, Scientist":{"count":64},"Église Saint-André":{"count":176},"Kaplica":{"count":269},"Ganesh Temple":{"count":56},"Ganesha Temple":{"count":53},"Iglesia de San Francisco":{"count":55},"Igreja Universal do Reino de Deus":{"count":114},"Calvaire":{"count":53},"Kaplička":{"count":56},"Santa Lucia":{"count":69},"temple":{"count":68},"Hanuman Temple":{"count":172},"Église Saint-Étienne":{"count":281},"Часовня":{"count":319},"Szent Mihály-templom":{"count":60},"Chiesa di Sant'Anna":{"count":81},"Chiesa di San Sebastiano":{"count":69},"八幡神社":{"count":139},"Église Saint-Léger":{"count":155},"Chiesa di Santa Maria":{"count":65},"Церковь Михаила Архангела":{"count":142},"Церковь Покрова Пресвятой Богородицы":{"count":288},"San Michele":{"count":83},"Église Saint-Didier":{"count":51},"biserica":{"count":192},"San Rocco":{"count":157},"Jakobuskirche":{"count":53},"Церковь Рождества Пресвятой Богородицы":{"count":173},"مسجد":{"count":485},"Capilla":{"count":173},"Église Sainte-Marie-Madeleine":{"count":72},"Άγιος Νικόλαος":{"count":99},"Église Saint-Nicolas":{"count":283},"Assembléia de Deus":{"count":177},"Salão do Reino das Testemunhas de Jeová":{"count":64},"Chiesa di San Michele":{"count":85},"Salón del Reino de los Testigos de Jehová":{"count":90},"Église Saint-Julien":{"count":104},"Chiesa di San Pietro":{"count":114},"Церковь Николая Чудотворца":{"count":366},"Református templom":{"count":357},"Saint-Pierre":{"count":55},"Церковь Петра и Павла":{"count":71},"San Bartolomeo":{"count":57},"Chapelle Notre-Dame":{"count":62},"Hanuman Mandir":{"count":54},"Mosquée":{"count":587},"Церковь Иконы Божией Матери Казанская":{"count":88},"Iglesia de Santa María":{"count":106},"Saint-Martin":{"count":70},"Nanebevzetí Panny Marie":{"count":128},"Église Saint-Sébastien":{"count":67},"Церковь Вознесения Господня":{"count":95},"Shiva Temple":{"count":76},"Wat":{"count":124},"Chapelle Saint-Joseph":{"count":54},"Sant'Anna":{"count":56},"Церковь Успения Пресвятой Богородицы":{"count":176},"Chiesa di San Bartolomeo":{"count":99},"Iglesia de San Pedro":{"count":114},"Kaplica cmentarna":{"count":558},"kaplica cmentarna":{"count":95},"mosque":{"count":63},"The Church of Jesus Christ of Latter Day Saints":{"count":64},"Chiesa della Santissima Trinità":{"count":59},"masjid":{"count":224},"神明社":{"count":67},"Igreja Católica":{"count":87},"Церковь Богоявления Господня":{"count":72},"Chiesa di San Giacomo":{"count":61},"Église Saint-Jacques":{"count":115},"Evangélikus templom":{"count":113},"Église Saint-Maurice":{"count":197},"Église Saint-Georges":{"count":171},"Église Saint-Barthélémy":{"count":54},"mosquée":{"count":67},"Chiesa del Carmine":{"count":52},"Chiesa dei Santi Pietro e Paolo":{"count":73},"Ermita de San Pedro":{"count":54},"Igreja":{"count":89},"Masjid Al Ikhlas":{"count":54},"Masjid Nurul Iman":{"count":80},"Masjid Nurul Huda":{"count":79},"Kościół pw. Najświętszego Serca Pana Jezusa":{"count":98},"Gereja":{"count":100},"Chiesa di Sant'Antonio Abate":{"count":71},"Sant'Antonio":{"count":81},"Santa Maria Assunta":{"count":109},"Église Saint-Vaast":{"count":83},"Camii":{"count":95},"Église Saint-Jean":{"count":79},"Sala Królestwa Świadków Jehowy":{"count":82},"Église Saint-Laurent":{"count":236},"Iglesia Catolica":{"count":80},"土地公廟":{"count":56},"Церква":{"count":63},"Capela":{"count":81},"Церковь Георгия Победоносца":{"count":83},"Церковь Воскресения Христова":{"count":78},"Eglise":{"count":79},"Kościół pw. Wniebowzięcia Najświętszej Maryi Panny":{"count":68},"Церковь Илии Пророка":{"count":59},"مسجد جامع":{"count":65},"Церковь Спаса Преображения":{"count":51},"Iglesia de Jesucristo de los Santos de los Últimos Días":{"count":60},"წმინდა გიორგი (St. Giorgi)":{"count":62},"Iglesia Cristiana Evangélica":{"count":53},"Église de l'Assomption":{"count":114},"Église Notre-Dame de l'Assomption":{"count":114},"Église Saint-Louis":{"count":64},"Église Saint-Sauveur":{"count":55},"Chiesa di San Michele Arcangelo":{"count":90},"Mesjid":{"count":330},"Mushalla":{"count":215},"Iglesia Católica":{"count":56},"Igreja Assembléia de Deus":{"count":57},"Άγιος Δημήτριος":{"count":57},"Iglesia de San Esteban":{"count":56},"Narození Panny Marie":{"count":54},"Église Saint-Sulpice":{"count":89},"La Iglesia de Jesucristo de los Santos de los Últimos Días":{"count":53},"Chiesa di San Biagio":{"count":56},"Église Saint-Saturnin":{"count":56},"monastery":{"count":254},"Церковь Казанской иконы Божией Матери":{"count":83},"pilone votivo":{"count":64},"mesjid":{"count":173},"Église Sainte-Madeleine":{"count":91},"Église Saint-Pierre et Saint-Paul":{"count":120},"Chiesa di Santo Stefano":{"count":80},"Sant Miquel":{"count":71},"福德祠":{"count":62},"Musholla":{"count":164},"Église Notre-Dame-de-l'Assomption":{"count":187},"Църква":{"count":52},"福德宮":{"count":63},"Chiesa di San Domenico":{"count":54},"Magyarok Nagyasszonya-templom":{"count":64},"Mushola":{"count":172},"Nagyboldogasszony-templom":{"count":98},"Église Saint-Germain":{"count":138},"Église Saint-Roch":{"count":53},"Nejsvětější Trojice":{"count":85},"MASJID":{"count":66},"Église Saint-Médard":{"count":114},"Mushollah":{"count":51},"Sant Pere":{"count":54},"pagoda":{"count":306},"Église Saint-Symphorien":{"count":64},"Kościół pw. Miłosierdzia Bożego":{"count":56},"Unknown Mosque":{"count":55},"Église Saint-Pierre-ès-Liens":{"count":65},"Kovil":{"count":161},"Église Saint-Blaise":{"count":71},"Église Saint-Barthélemy":{"count":77},"Kościół pw. Podwyższenia Krzyża Świętego":{"count":63},"Szent István király templom":{"count":66},"Ref. templom":{"count":191},"Église Saint-Rémy":{"count":77},"IGLESIA CATOLICA":{"count":56},"Szent István király-templom":{"count":111},"Église Saint-Paul":{"count":92},"Église Saint-Rémi":{"count":67},"Kisboldogasszony-templom":{"count":70},"Szent Anna-templom":{"count":64},"Église Saint-Quentin":{"count":66},"svätého Michala":{"count":68},"Église du Sacré-Cœur":{"count":55},"Szentháromság-templom":{"count":104},"Nagyboldogasszony templom":{"count":52},"Szent Kereszt felmagasztalása-templom":{"count":61}},"fast_food":{"Quick":{"count":489},"McDonald's":{"count":16493,"tags":{"cuisine":"burger"}},"Subway":{"count":9310,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":5558,"tags":{"cuisine":"burger"}},"Pizza Hut":{"count":1320,"tags":{"cuisine":"pizza"}},"Ali Baba":{"count":90},"Hungry Jacks":{"count":239,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":179},"KFC":{"count":4912,"tags":{"cuisine":"chicken"}},"Chowking":{"count":213},"Jollibee":{"count":637},"Five Guys":{"count":310},"肯德基":{"count":174},"Domino's Pizza":{"count":1811,"tags":{"cuisine":"pizza"}},"Hesburger":{"count":156},"Wendy's":{"count":2624,"tags":{"cuisine":"burger"}},"Steers":{"count":168},"Arby's":{"count":1231},"A&W":{"count":497},"Tim Hortons":{"count":406},"Dairy Queen":{"count":1243},"Fish & Chips":{"count":159},"麦当劳":{"count":97},"Harvey's":{"count":138},"麥當勞":{"count":168},"Pizza Pizza":{"count":361},"Istanbul Kebab":{"count":53},"Kotipizza":{"count":77},"Jack in the Box":{"count":815,"tags":{"cuisine":"burger"}},"KFC/Taco Bell":{"count":59},"Istanbul":{"count":86},"Kochlöffel":{"count":76},"Pita Pit":{"count":110},"Telepizza":{"count":267},"Hallo Pizza":{"count":93},"Qdoba":{"count":71},"Carl's Jr.":{"count":482,"tags":{"cuisine":"burger"}},"Quiznos":{"count":336,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":156},"Sonic":{"count":1004,"tags":{"cuisine":"burger"}},"Rally's":{"count":61},"Taco Bell":{"count":2528,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":83},"Papa John's":{"count":626,"tags":{"cuisine":"pizza"}},"Max":{"count":52},"Nordsee":{"count":188},"Mr. Sub":{"count":140},"Panera Bread":{"count":103},"Krispy Kreme":{"count":60},"Chipotle":{"count":542,"tags":{"cuisine":"mexican"}},"Макдоналдс":{"count":394,"tags":{"name:en":"McDonald's"}},"マクドナルド":{"count":1078,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"ケンタッキーフライドチキン":{"count":302,"tags":{"name:en":"KFC","cuisine":"chicken"}},"Food Court":{"count":58},"Dunkin' Donuts":{"count":645,"tags":{"cuisine":"donut"}},"Asia Wok":{"count":52},"In-N-Out Burger":{"count":115},"松屋":{"count":542,"tags":{"name:en":"Matsuya"}},"Jimmy John's":{"count":379,"tags":{"cuisine":"sandwich"}},"Hardee's":{"count":511,"tags":{"cuisine":"burger"}},"Waffle House":{"count":67},"Bistro":{"count":64},"Quiznos Sub":{"count":56},"Jamba Juice":{"count":120},"Робин Сдобин":{"count":115},"Baskin-Robbins":{"count":200,"tags":{"amenity":"ice_cream"}},"くら寿司":{"count":97},"吉野家":{"count":403},"Taco Time":{"count":135},"モスバーガー":{"count":449,"tags":{"name:en":"MOS BURGER"}},"すき家":{"count":493,"tags":{"name:en":"SUKIYA"}},"CoCo壱番屋":{"count":203},"スシロー":{"count":94},"Sibylla":{"count":70},"The Pizza Company":{"count":58},"Firehouse Subs":{"count":115},"Little Caesar's":{"count":62},"Papa Murphy's":{"count":70},"Little Caesars":{"count":393,"tags":{"cuisine":"pizza"}},"El Pollo Loco":{"count":112},"Del Taco":{"count":214},"White Castle":{"count":126},"Boston Market":{"count":100},"Chick-fil-A":{"count":720,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":477,"tags":{"cuisine":"chinese"}},"Checkers":{"count":83},"Wienerschnitzel":{"count":53},"ミスタードーナツ":{"count":172},"Whataburger":{"count":760},"Cold Stone Creamery":{"count":71},"Greenwich":{"count":69},"Habib's":{"count":72},"Culver's":{"count":497},"Bob's":{"count":69},"ロッテリア":{"count":83},"Church's Chicken":{"count":207},"Popeye's":{"count":374,"tags":{"cuisine":"chicken"}},"Five Guys Burgers and Fries":{"count":54},"Jersey Mike's Subs":{"count":64},"Long John Silver's":{"count":176},"Kiosk":{"count":56},"Booster Juice":{"count":58},"Burger Machine":{"count":52},"Lotteria":{"count":56},"Schlotzsky's Deli":{"count":274},"Pizza Hut Delivery":{"count":59},"Pollo Campero":{"count":115},"Шаурма":{"count":151},"Taco John's":{"count":119},"SubWay":{"count":52},"Bojangles":{"count":70},"Taco Cabana":{"count":53},"New York Pizza":{"count":53},"Русский Аппетит":{"count":120},"Теремок":{"count":117},"かっぱ寿司":{"count":76},"Zaxby's":{"count":139},"Kebabai":{"count":56},"オリジン弁当":{"count":89},"なか卯":{"count":156},"DQ":{"count":52},"Бургер Кинг":{"count":85},"Бистро":{"count":51},"サブウェイ":{"count":73},"Шаверма":{"count":66},"ほっともっと":{"count":133},"幸楽苑":{"count":67},"摩斯漢堡":{"count":61},"СтарДогс":{"count":72},"Pollo Granjero":{"count":62},"Pinulito":{"count":57}},"restaurant":{"Pizza Hut":{"count":2161,"tags":{"cuisine":"pizza"}},"Pizzeria Italia":{"count":53},"Hirsch":{"count":84},"Shakey's":{"count":53},"Little Chef":{"count":66},"Zur Krone":{"count":93},"Deutsches Haus":{"count":89},"Krone":{"count":178},"Akropolis":{"count":197},"Schützenhaus":{"count":147},"Wagamama":{"count":94},"Kreuz":{"count":85},"Waldschänke":{"count":53},"Pizza Express":{"count":380},"Jägerhof":{"count":55},"La Piazza":{"count":111},"Adler":{"count":158},"La Boucherie":{"count":72},"Lamm":{"count":68},"Zur Sonne":{"count":72},"Zur Linde":{"count":219},"La Pergola":{"count":72},"Panorama":{"count":89},"Shanghai":{"count":89},"Red Lobster":{"count":371},"Zum Löwen":{"count":82},"Swiss Chalet":{"count":148},"Frankie & Benny's":{"count":137},"Hooters":{"count":168},"Sternen":{"count":86},"Hirschen":{"count":88},"Ali Baba":{"count":51},"Denny's":{"count":737},"Sonne":{"count":116},"Ratskeller":{"count":162},"La Cantina":{"count":90},"El Greco":{"count":98},"Gasthof zur Post":{"count":96},"Löwen":{"count":134},"Lotus":{"count":77},"La Pataterie":{"count":96},"Syrtaki":{"count":61},"Bella Napoli":{"count":75},"Oasis":{"count":62},"Mandarin":{"count":82},"Nando's":{"count":338},"Zizzi":{"count":87},"Rhodos":{"count":88},"Bären":{"count":72},"Gasthaus Adler":{"count":66},"Pomodoro":{"count":52},"Lindenhof":{"count":90},"Milano":{"count":66},"Dolce Vita":{"count":108},"Kirchenwirt":{"count":92},"Kreta":{"count":67},"Kantine":{"count":99},"Hong Kong":{"count":109},"Ochsen":{"count":94},"Spur":{"count":64},"Applebee's":{"count":845},"Europa":{"count":61},"Flunch":{"count":175},"Zur Post":{"count":125},"China Town":{"count":99},"La Dolce Vita":{"count":118},"Waffle House":{"count":421},"Gasthaus Krone":{"count":89},"Linde":{"count":115},"Outback Steakhouse":{"count":348},"Dionysos":{"count":80},"Frohsinn":{"count":51},"Kelsey's":{"count":65},"Boston Pizza":{"count":239},"Mykonos":{"count":79},"Bella Italia":{"count":183},"Olympia":{"count":83},"Zum Hirschen":{"count":51},"China House":{"count":61},"Sizzler":{"count":80},"Grüner Baum":{"count":118},"Taj Mahal":{"count":154},"Delphi":{"count":102},"Casablanca":{"count":54},"Rössli":{"count":85},"Wimpy":{"count":57},"Traube":{"count":68},"La Taverna":{"count":52},"Adria":{"count":59},"Pancake House":{"count":67},"Red Robin":{"count":273},"Golden Dragon":{"count":54},"L'Osteria":{"count":64},"Bahnhof":{"count":53},"Harvester":{"count":67},"Belvedere":{"count":52},"Roma":{"count":75},"Hellas":{"count":63},"Mamma Mia":{"count":104},"La Perla":{"count":82},"Vips":{"count":87},"Tim Hortons":{"count":60},"Santorini":{"count":52},"Poseidon":{"count":131},"Gusto":{"count":59},"Тануки":{"count":55},"Da Vinci":{"count":79},"Toscana":{"count":64},"Hippopotamus":{"count":107},"La Terrazza":{"count":54},"Courtepaille":{"count":145},"Dorfkrug":{"count":54},"Hard Rock Cafe":{"count":94},"Marco Polo":{"count":58},"La Piazzetta":{"count":62},"Zorbas":{"count":53},"Athen":{"count":70},"La Fontana":{"count":53},"Pinocchio":{"count":63},"San Marco":{"count":82},"Portofino":{"count":61},"デニーズ":{"count":178},"La Gondola":{"count":56},"Sphinx":{"count":53},"Sportheim":{"count":103},"ジョリーパスタ":{"count":64},"TGI Friday's":{"count":320},"Gasthaus zur Linde":{"count":54},"餃子の王将":{"count":176},"Engel":{"count":52},"Alte Post":{"count":64},"Carpe Diem":{"count":57},"Pizzeria Roma":{"count":77},"La Casa":{"count":56},"China Garden":{"count":106},"Vapiano":{"count":123},"Bistro":{"count":59},"La Scala":{"count":59},"Telepizza":{"count":66},"Trattoria":{"count":64},"Casa Mia":{"count":63},"Taverna":{"count":52},"IHOP":{"count":643},"Chili's":{"count":556},"Asia":{"count":66},"Athos":{"count":58},"Ruby Tuesday":{"count":270},"Olive Garden":{"count":460},"Ocean Basket":{"count":57},"Mellow Mushroom":{"count":63},"Cracker Barrel":{"count":294},"La Strada":{"count":65},"Panera Bread":{"count":466},"Friendly's":{"count":108},"Buffalo Grill":{"count":255},"Taste of India":{"count":57},"Texas Roadhouse":{"count":199},"ガスト":{"count":431,"tags":{"name:en":"Gusto"}},"牛角":{"count":75},"Schwarzer Adler":{"count":59},"丸亀製麺":{"count":63},"ココス":{"count":119},"ロイヤルホスト":{"count":95},"Sushi Bar":{"count":58},"La Tasca":{"count":51},"Venezia":{"count":57},"ジョナサン":{"count":122},"Léon de Bruxelles":{"count":57},"夢庵":{"count":53},"Mensa":{"count":146},"大戸屋":{"count":51},"El Paso":{"count":54},"Five Guys":{"count":70},"びっくりドンキー":{"count":91},"サイゼリヤ":{"count":233},"MK Restaurants":{"count":51},"Village Inn":{"count":135},"Peking":{"count":69},"Sakura":{"count":109},"Tony Roma's":{"count":55},"restaurant":{"count":60},"El Rancho":{"count":71},"Prezzo":{"count":117},"La Bodega":{"count":51},"バーミヤン":{"count":115},"Mimi's Cafe":{"count":53},"Little Italy":{"count":71},"Boston Market":{"count":80},"La Trattoria":{"count":72},"Bonefish Grill":{"count":66},"Wasabi":{"count":62},"China Wok":{"count":76},"Joe's Crab Shack":{"count":66},"Round Table Pizza":{"count":78},"Buffalo Wild Wings":{"count":365},"Perkins":{"count":140},"La Terrasse":{"count":69},"L'Escale":{"count":54},"Shari's":{"count":70},"Bob Evans":{"count":237},"바다횟집 (Bada Fish Restaurant)":{"count":56},"Mang Inasal":{"count":115},"California Pizza Kitchen":{"count":98},"Da Grasso":{"count":53},"Якитория":{"count":80},"Pizza Ranch":{"count":64},"Golden Corral":{"count":191},"Old Chicago":{"count":51},"Евразия":{"count":95},"Carrabba's Italian Grill":{"count":52},"Noodles & Company":{"count":80},"Canteen":{"count":60},"Kudu":{"count":172},"Cold Stone Creamery":{"count":59},"O'Charley's":{"count":59},"Qdoba":{"count":70},"Campanile":{"count":52},"Jason's Deli":{"count":63},"安楽亭":{"count":53},"LongHorn Steakhouse":{"count":151},"Logan's Roadhouse":{"count":81},"Moe's Southwest Grill":{"count":62},"Sushi":{"count":53},"Sonic":{"count":64,"tags":{"cuisine":"burger"}},"Famous Dave's":{"count":56},"Jimmy John's":{"count":119,"tags":{"cuisine":"sandwich"}},"Hardee's":{"count":51,"tags":{"cuisine":"burger"}},"天下一品":{"count":61},"Little Caesars":{"count":66,"tags":{"cuisine":"pizza"}},"Firehouse Subs":{"count":65},"Steak 'n Shake":{"count":68,"tags":{"cuisine":"burger"}},"ジョイフル":{"count":53},"Dunkin' Donuts":{"count":76,"tags":{"cuisine":"donut"}},"やよい軒":{"count":52},"Ristorante Del Arte":{"count":143},"八方雲集":{"count":94}},"parking":{"Coop":{"count":55},"Wanderparkplatz":{"count":796},"Tesco":{"count":86},"Carpool":{"count":55},"Friedhof":{"count":485},"Park and Ride":{"count":88},"Rathaus":{"count":111},"P1":{"count":357},"P2":{"count":351},"Markt":{"count":77},"Public Parking":{"count":173},"Rewe":{"count":201},"ALDI":{"count":324},"P7":{"count":96},"P8":{"count":62},"Parkplatz":{"count":278},"Car Park":{"count":232},"P9":{"count":56},"Lot D":{"count":91},"Park & Ride":{"count":139},"Marktplatz":{"count":166},"P3":{"count":282},"Parkhaus":{"count":139},"Visitor Parking":{"count":246},"Parking de la Mairie":{"count":105},"Edeka":{"count":142},"Besucherparkplatz":{"count":182},"Friedhofsparkplatz":{"count":165},"Estacionamiento":{"count":214},"Festplatz":{"count":109},"Behindertenparkplatz":{"count":176},"Place de l'Église":{"count":311},"Volksbank":{"count":59},"Sportplatz":{"count":143},"P&R":{"count":59},"Kaufland":{"count":117},"Waldparkplatz":{"count":143},"Bahnhof":{"count":98},"P+R":{"count":150},"Parcare":{"count":300},"Sparkasse":{"count":129},"NETTO":{"count":268},"Tesco Car Park":{"count":59},"Bus":{"count":61},"Norma":{"count":68},"Kirche":{"count":52},"P6":{"count":132},"Kundenparkplatz":{"count":280},"School":{"count":241},"Parkplatz Friedhof":{"count":124},"Place de la Mairie":{"count":229},"Krankenhaus":{"count":55},"parking":{"count":252},"Schulparkplatz":{"count":76},"Place de la République":{"count":137},"Lehrerparkplatz":{"count":189},"Lidl":{"count":544},"P10":{"count":55},"P4":{"count":214},"停車場":{"count":92},"Dorfplatz":{"count":53},"P":{"count":73},"Staff Car Park":{"count":71},"Парковка":{"count":164},"Busparkplatz":{"count":122},"PKW":{"count":59},"LKW":{"count":81},"Parking de la Gare":{"count":125},"Carrefour":{"count":77},"Spar":{"count":69},"Mitarbeiterparkplatz":{"count":110},"Parkplatz Lidl":{"count":60},"Penny":{"count":123},"McDonald's":{"count":166,"tags":{"cuisine":"burger"}},"Disabled Parking":{"count":58},"Anwohnerparkplatz":{"count":68},"Bus Parking":{"count":117},"Pendlerparkplatz":{"count":68},"Gravel Parking Lot, Mille Lacs WMA":{"count":82},"Privatparkplatz":{"count":71},"Parking de la Poste":{"count":52},"Lidl Parkplatz":{"count":78},"Place du 19 Mars 1962":{"count":88},"Place de la Liberté":{"count":55},"P5":{"count":193},"Parcheggio":{"count":198},"counter":{"count":110},"Park":{"count":325},"Car Parking":{"count":159},"Parking du Cimetière":{"count":62},"タイムズ":{"count":870},"Visitor":{"count":60},"Private":{"count":188},"Netto Parkplatz":{"count":51},"Place du Champ de Foire":{"count":63},"Kunden":{"count":65},"Parqueadero":{"count":103},"TIR":{"count":93},"Student Parking":{"count":98},"Faculty Parking":{"count":62},"Parking":{"count":1047},"Esselunga":{"count":66},"Автостоянка":{"count":171},"Place du 8 Mai 1945":{"count":86},"Place du Général de Gaulle":{"count":83},"Billa":{"count":51},"Parking Garage":{"count":81},"Freibad":{"count":57},"Staff Parking":{"count":215},"Паркинг":{"count":88},"Place du Marché":{"count":105},"Parqueo":{"count":122},"PMR":{"count":130},"Besucher":{"count":101},"Estacionamento":{"count":122},"<different> Boat Ramp":{"count":113},"Lot B":{"count":107},"Parking leśny":{"count":63},"Стоянка":{"count":147},"Lot C":{"count":95},"Truck Parking":{"count":144},"Parking Lot":{"count":127},"三井のリパーク":{"count":595},"Lot 6":{"count":72},"Lot 2":{"count":81},"Lot 1":{"count":96},"Lot 5":{"count":84},"Lot 3":{"count":90},"Lot 4":{"count":76},"Place de la Libération":{"count":55},"LKW-Parkplatz":{"count":55},"Aldi Süd":{"count":82},"Aparcamiento":{"count":110},"Parkplatz Netto":{"count":56},"Mieterparkplatz":{"count":180},"P11":{"count":51},"Гаражи":{"count":96},"Church":{"count":90},"Private Parking":{"count":55},"H":{"count":51},"Parking strzeżony":{"count":52},"Parkoló":{"count":82},"Privat":{"count":69},"Mitarbeiter":{"count":51},"Handicapped":{"count":203},"Park Cimitero":{"count":126},"駐車場":{"count":178},"Place de la Gare":{"count":72},"car parking":{"count":96},"Parcheggio disabili":{"count":266},"Employee Parking":{"count":104},"Motorcycle Parking":{"count":62},"miejsce postoju":{"count":66},"Overflow Parking":{"count":69},"پارکینگ":{"count":74},"Disabled":{"count":210},"名鉄協商パーキング":{"count":182},"月極駐車場":{"count":67},"Lot A":{"count":110},"Parcheggio Disabili":{"count":238},"Garaje":{"count":83},"Life Time Fitness Parking":{"count":55},"truck parking":{"count":84},"Lot F":{"count":57},"Lot 7":{"count":61},"truck":{"count":271},"car":{"count":460},"Lot H":{"count":54},"Lot E":{"count":67},"Lot G":{"count":55},"Förhyrda":{"count":58}},"bank":{"Chase":{"count":1355},"Commonwealth Bank":{"count":338},"Citibank":{"count":421},"Barclays":{"count":1203},"Westpac":{"count":297},"NAB":{"count":186},"HSBC":{"count":1571},"ANZ":{"count":331},"Bank of China":{"count":65},"TSB":{"count":198},"BBBank":{"count":55},"Sparkasse":{"count":4663},"UCPB":{"count":108},"PNB":{"count":293},"Metrobank":{"count":309},"BDO":{"count":473},"Volksbank":{"count":2690},"China Bank":{"count":132},"BPI":{"count":529},"Postbank":{"count":575},"NatWest":{"count":773},"بانک ملت":{"count":151},"Raiffeisenbank":{"count":2581},"Nordea":{"count":336},"Landbank":{"count":76},"Yorkshire Bank":{"count":89},"ABSA":{"count":96},"Standard Bank":{"count":137},"FNB":{"count":117},"بانک کشاورزی":{"count":100},"بانک مسکن":{"count":96},"Deutsche Bank":{"count":995},"Santander":{"count":2861},"Commerzbank":{"count":867},"Targobank":{"count":201},"ABN AMRO":{"count":144},"SEB":{"count":131},"Handelsbanken":{"count":239},"Kreissparkasse":{"count":615},"UniCredit Bank":{"count":501},"Monte dei Paschi di Siena":{"count":225},"Banca Popolare di Milano":{"count":76},"Caja Rural":{"count":186},"Sparda-Bank":{"count":284},"VÚB":{"count":103},"Slovenská sporiteľňa":{"count":157},"Банка ДСК":{"count":59},"Bank of Montreal":{"count":137},"Comerica Bank":{"count":55},"KBC":{"count":265},"US Bank":{"count":492},"HypoVereinsbank":{"count":449},"Bank Austria":{"count":151},"Lloyds Bank":{"count":620},"ING":{"count":596},"Erste Bank":{"count":206},"CIBC":{"count":422},"BCA":{"count":65},"Scotiabank":{"count":918},"Caisse d'Épargne":{"count":1426},"Clydesdale Bank":{"count":54},"Bank of Scotland":{"count":115},"Royal Bank of Scotland":{"count":126},"TD Canada Trust":{"count":613},"BMO":{"count":288},"Danske Bank":{"count":152},"Crédit Mutuel de Bretagne":{"count":330},"中国农业银行":{"count":148},"Crédit Agricole":{"count":2267},"LCL":{"count":846},"VR-Bank":{"count":500},"ČSOB":{"count":203},"Česká spořitelna":{"count":233},"BNP Paribas":{"count":1062},"National Bank":{"count":100},"RBC Royal Bank":{"count":104},"Nationwide":{"count":286},"中国建设银行":{"count":105},"Halifax":{"count":316},"BAWAG PSK":{"count":98},"CIBC Banking Centre":{"count":55},"Royal Bank":{"count":83},"Nedbank":{"count":92},"First National Bank":{"count":150},"Rabobank":{"count":557},"Sparkasse KölnBonn":{"count":78},"Swedbank":{"count":241},"Tatra banka":{"count":74},"IberCaja":{"count":201},"Volksbank Raiffeisenbank":{"count":60},"İş Bankası":{"count":89},"Berliner Sparkasse":{"count":67},"Berliner Volksbank":{"count":78},"Wells Fargo":{"count":1643},"Société Générale":{"count":1022},"RBC":{"count":363},"Osuuspankki":{"count":85},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":159},"Cassa di Risparmio del Veneto":{"count":99},"Ziraat Bankası":{"count":106},"Akbank":{"count":85},"Banque Populaire":{"count":715},"BNP Paribas Fortis":{"count":282},"Banco Popular":{"count":543},"Bankia":{"count":529},"La Caixa":{"count":1120},"Credit Suisse":{"count":87},"BRD":{"count":251},"BCR":{"count":193},"BW-Bank":{"count":101},"Komerční banka":{"count":171},"Banco Pastor":{"count":77},"OTP":{"count":345},"Sberbank":{"count":130},"CIC":{"count":645},"Banca Transilvania":{"count":139},"Bancpost":{"count":70},"Maybank":{"count":155},"BBVA":{"count":1146},"Postbank Finanzcenter":{"count":55},"中国银行":{"count":209},"Unicredit Banca":{"count":462},"Crédit Mutuel":{"count":1034},"mBank":{"count":58},"Intesa San Paolo":{"count":135},"ATB Financial":{"count":58},"Belfius":{"count":280},"Piraeus Bank":{"count":59},"Bank of America":{"count":1595},"Standard Chartered":{"count":64},"Intesa Sanpaolo":{"count":66},"Сбербанк":{"count":6219},"Россельхозбанк":{"count":304},"Banc Sabadell":{"count":163},"MetroBank":{"count":82},"Crédit du Nord":{"count":138},"Popular":{"count":68},"Banco Sabadell":{"count":176},"Santander Consumer Bank":{"count":101},"BancoEstado":{"count":106},"Millennium Bank":{"count":397},"State Bank of India":{"count":317},"Banca Popolare di Novara":{"count":66},"Беларусбанк":{"count":510},"ING Bank Śląski":{"count":117},"Targo Bank":{"count":60},"Белинвестбанк":{"count":71},"Kreissparkasse Köln":{"count":68},"Banco BCI":{"count":63},"Banco de Chile":{"count":133},"Приорбанк":{"count":70},"ВТБ24":{"count":454},"UBS":{"count":169},"BPH":{"count":61},"PKO BP":{"count":453},"Казкоммерцбанк":{"count":66},"PSBank":{"count":99},"UnionBank":{"count":100},"RCBC Savings Bank":{"count":79},"EastWest Bank":{"count":66},"Commercial Bank of Ceylon PLC":{"count":97},"Caja España":{"count":63},"Unicaja Banco":{"count":180},"BBK":{"count":100},"RBS":{"count":178},"Bank of Ireland":{"count":142},"BNL":{"count":129},"Stadtsparkasse":{"count":72},"Banco Itaú":{"count":288},"AIB":{"count":86},"Райффайзенбанк":{"count":63},"BZ WBK":{"count":146},"Banco do Brasil":{"count":1063},"Caixa Econômica Federal":{"count":451},"Banca Românească":{"count":52},"Fifth Third Bank":{"count":202},"Banca Popolare di Vicenza":{"count":112},"Wachovia":{"count":53},"K&H Bank":{"count":70},"CIB Bank":{"count":61},"Budapest Bank":{"count":53},"みずほ銀行":{"count":244},"BES":{"count":54},"ICICI Bank":{"count":160},"Credem":{"count":55},"HDFC Bank":{"count":163},"La Banque Postale":{"count":109},"Bangkok Bank":{"count":69},"Bank BPH":{"count":58},"工商银行":{"count":140},"Oberbank":{"count":99},"Oldenburgische Landesbank":{"count":68},"Banco di Sardegna":{"count":72},"BRED":{"count":60},"First Bank":{"count":74},"Banca Popolare di Verona":{"count":56},"OLB":{"count":53},"Banca Popolare di Sondrio":{"count":82},"Cariparma":{"count":60},"Bendigo Bank":{"count":127},"BBVA Bancomer":{"count":97},"Argenta":{"count":114},"AXA":{"count":96},"Axis Bank":{"count":125},"Getin Bank":{"count":97},"CaixaBank":{"count":111},"Pekao SA":{"count":120},"Banca March":{"count":60},"БПС-Сбербанк":{"count":51},"Альфа-Банк":{"count":262},"GE Money Bank":{"count":86},"Белагропромбанк":{"count":157},"Banco Galicia":{"count":78},"Eurobank":{"count":215},"Bankinter":{"count":108},"横浜銀行":{"count":79},"Τράπεζα Πειραιώς":{"count":108},"Bank of New Zealand":{"count":58},"Banca Intesa":{"count":93},"Canara Bank":{"count":168},"Cajamar":{"count":149},"Banamex":{"count":288},"CEC Bank":{"count":95},"Davivienda":{"count":152},"Alpha Bank":{"count":287},"招商银行":{"count":63},"Banque Nationale":{"count":88},"第一銀行":{"count":53},"SBI":{"count":70},"Veneto Banca":{"count":65},"Bank of the West":{"count":140},"Liberbank":{"count":101},"Εθνική Τράπεζα":{"count":88},"Key Bank":{"count":226},"Western Union":{"count":327},"Citizens Bank":{"count":190},"УкрСиббанк":{"count":218},"ProCredit Bank":{"count":51},"ПриватБанк":{"count":919},"RCBC":{"count":125},"Security Bank":{"count":136},"Millenium":{"count":59},"HNB":{"count":60},"三菱東京UFJ銀行":{"count":217},"CajaSur":{"count":72},"ВТБ":{"count":70},"Indian Bank":{"count":60},"Andhra Bank":{"count":55},"Banque populaire":{"count":52},"Ulster Bank":{"count":96},"Bancolombia":{"count":122},"TD Bank":{"count":341},"Caixa":{"count":163},"SunTrust Bank":{"count":129},"Авангард":{"count":58},"Union Bank":{"count":173},"Philippine National Bank":{"count":64},"Bradesco":{"count":553},"Itaú":{"count":525},"Punjab National Bank":{"count":73},"PBZ":{"count":64},"Alior Bank":{"count":154},"中国工商银行":{"count":130},"Novo Banco":{"count":69},"Santander Río":{"count":157},"Райффайзен Банк Аваль":{"count":111},"Yorkshire Building Society":{"count":59},"De Venezuela":{"count":89},"ธนาคารไทยพาณิชย์":{"count":57},"Bancomer":{"count":192},"Banorte":{"count":186},"BOC":{"count":94},"Republic Bank":{"count":65},"Raiffeisen Polbank":{"count":60},"Банк Москвы":{"count":172},"Banco Nacional":{"count":120},"Banco Pichincha":{"count":69},"Syndicate Bank":{"count":69},"Bank Zachodni WBK":{"count":80},"Caja Duero":{"count":80},"Regions Bank":{"count":158},"KeyBank":{"count":80},"Bank of Commerce":{"count":64},"Росбанк":{"count":207},"Открытие":{"count":64},"Бинбанк":{"count":77},"People's Bank":{"count":68},"ПУМБ":{"count":63},"Banco di Napoli":{"count":67},"Bank Spółdzielczy":{"count":333},"Public Bank":{"count":56},"SunTrust":{"count":150},"PNC Bank":{"count":493},"신한은행":{"count":221,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":168,"tags":{"name:en":"Gungmin Bank"}},"Газпромбанк":{"count":129},"M&T Bank":{"count":138},"Indian Overseas Bank":{"count":62},"Santander Totta":{"count":186},"Banco de Bogotá":{"count":51},"Ощадбанк":{"count":684},"State Bank of Mysore":{"count":58},"Banco Estado":{"count":110},"Ecobank":{"count":138},"Takarékszövetkezet":{"count":120},"East West Bank":{"count":53},"بانک صادرات":{"count":178},"Banco Azteca":{"count":79},"TCF Bank":{"count":68},"Banco Nación":{"count":123},"Galicia":{"count":58},"Macro":{"count":57},"Πειραιώς":{"count":109},"Промсвязьбанк":{"count":120},"بانک سپه":{"count":119},"بانک ملی":{"count":186},"Уралсиб":{"count":118},"صادرات":{"count":61},"三井住友銀行":{"count":179},"りそな銀行":{"count":122},"Commercial Bank":{"count":53},"BCI":{"count":91},"Banco de Costa Rica":{"count":113},"Hrvatska poštanska banka":{"count":55},"Caixa Geral de Depósitos":{"count":210},"Banco Provincia":{"count":98},"Bank of India":{"count":59},"Umpqua Bank":{"count":70},"BCP":{"count":86},"BB&T":{"count":344},"Capital One":{"count":114},"ABANCA":{"count":79},"Возрождение":{"count":67},"Banco Macro":{"count":79},"МДМ Банк":{"count":59},"Capital One Bank":{"count":68},"BBVA Compass":{"count":60},"Caja Círculo":{"count":69},"CatalunyaCaixa":{"count":110},"One Network Bank":{"count":89},"Occidental de Descuento":{"count":67},"Народный банк":{"count":56},"Bank Mandiri":{"count":130},"Banco de la Nación Argentina":{"count":73},"Mercantil":{"count":116},"PKO Bank Polski":{"count":75},"Русский стандарт":{"count":52},"First Citizens Bank":{"count":69},"Corporation Bank":{"count":61},"Bank of Baroda":{"count":68},"Banco G&T Continental":{"count":86},"U.S. Bank":{"count":69},"Banco de Occidente":{"count":55},"Kasa Stefczyka":{"count":57},"Мособлбанк":{"count":52},"CGD":{"count":56},"Huntington Bank":{"count":74},"Sampath Bank":{"count":72},"Peoples Bank":{"count":151},"建设银行":{"count":78},"Interbank":{"count":68},"Banrisul":{"count":76},"بانک تجارت":{"count":122},"Cбербанк":{"count":79},"BBVA Francés":{"count":75},"ธนาคารกรุงเทพ":{"count":77},"ธนาคารกสิกรไทย":{"count":66},"Millenium BCP":{"count":54},"Montepio":{"count":105},"Provincial":{"count":78},"Bicentenario":{"count":127},"Banesco":{"count":172},"ธนาคารกรุงไทย":{"count":60},"Garanti Bankası":{"count":59},"Совкомбанк":{"count":102},"BNA":{"count":67},"BAC":{"count":74},"Banrural":{"count":58},"BRI":{"count":62},"Banco Industrial":{"count":74},"京都銀行":{"count":67},"Cajero Automatico Bancared":{"count":122},"Banco de Desarrollo Banrural":{"count":84},"Banco Patagonia":{"count":51},"ICBC":{"count":107},"Banco de Venezuela":{"count":53},"Banco de la Nación":{"count":117},"Banco Bradesco":{"count":190},"Bank of Africa":{"count":52},"Sicredi":{"count":64},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"ملی":{"count":52},"Crédito Agrícola":{"count":79},"Caja Rural de Jaén":{"count":54},"ASB Bank":{"count":51},"농협":{"count":51},"РНКБ":{"count":78},"农业银行":{"count":64},"Bank BRI":{"count":140},"Генбанк":{"count":67},"하나은행":{"count":81},"CityCommerce Bank":{"count":54}},"bureau_de_change":{"Western Union":{"count":119},"Kantor":{"count":106}},"car_rental":{"Europcar":{"count":468},"Budget":{"count":161},"Sixt":{"count":250},"Hertz":{"count":511},"Avis":{"count":481},"Enterprise":{"count":443},"Thrifty":{"count":58},"U-Haul":{"count":74},"ニッポンレンタカー":{"count":87}},"pharmacy":{"Lloyds Pharmacy":{"count":436},"Rowlands Pharmacy":{"count":102},"Boots":{"count":1096},"Marien-Apotheke":{"count":371},"Park-Apotheke":{"count":55},"Mercury Drug":{"count":507},"Löwen-Apotheke":{"count":398},"Superdrug":{"count":157},"Sonnen-Apotheke":{"count":374},"Engel-Apotheke":{"count":143},"Hirsch-Apotheke":{"count":114},"Apoteket":{"count":53},"Stadt-Apotheke":{"count":387},"Markt-Apotheke":{"count":203},"Аптека":{"count":2787},"Brunnen-Apotheke":{"count":77},"Hubertus Apotheke":{"count":124},"Bahnhof Apotheke":{"count":51},"Shoppers Drug Mart":{"count":642},"Pharmacie Centrale":{"count":178},"Alte Apotheke":{"count":99},"Neue Apotheke":{"count":122},"Rathaus-Apotheke":{"count":178},"Gintarinė vaistinė":{"count":116},"Rats-Apotheke":{"count":127},"Rosen-Apotheke":{"count":203},"Eczane":{"count":55},"Pharmasave":{"count":116},"Adler Apotheke":{"count":378},"Bären-Apotheke":{"count":109},"Hirsch Apotheke":{"count":69},"Walgreens":{"count":2896},"Rite Aid":{"count":1293},"Linden-Apotheke":{"count":220},"Bahnhof-Apotheke":{"count":88},"Burg-Apotheke":{"count":77},"Apotheke am Markt":{"count":82},"Schloss-Apotheke":{"count":62},"Jean Coutu":{"count":93},"Pharmaprix":{"count":79},"Pharmacie du Centre":{"count":123},"Farmacias Ahumada":{"count":130},"Stern-Apotheke":{"count":83},"Guardian":{"count":59},"Farmacia Comunale":{"count":167},"Dr.Max":{"count":102},"Cruz Verde":{"count":132},"ГорЗдрав":{"count":65},"Watsons":{"count":67},"Farmacias Cruz Verde":{"count":132},"CVS":{"count":2678},"Sensiblu":{"count":96},"Норма":{"count":89},"Farmacias SalcoBrand":{"count":142},"Фармация":{"count":163},"Pharmacie du Marché":{"count":89},"Pharmacie de la Mairie":{"count":57},"Farmacia Guadalajara":{"count":101},"Apteka":{"count":579},"Pharmacie Principale":{"count":54},"Clicks":{"count":116},"セイジョー":{"count":62},"Аптека №1":{"count":80},"マツモトキヨシ":{"count":171},"Dr. Max":{"count":169},"Вита":{"count":119},"Catena":{"count":88},"Здоровье":{"count":64},"Аптека 36,6":{"count":238},"Радуга":{"count":109},"サンドラッグ":{"count":98},"Pharmacie de la Gare":{"count":100},"Apollo Pharmacy":{"count":58},"Chemist Warehouse":{"count":74},"Rexall":{"count":88},"Первая помощь":{"count":114},"Gyógyszertár":{"count":51},"Ригла":{"count":187},"Droga Raia":{"count":104},"Аптека низьких цін":{"count":60},"Rose Pharmacy":{"count":83},"Dbam o Zdrowie":{"count":51},"Kinney Drugs":{"count":71},"Классика":{"count":91},"Ljekarna":{"count":66},"Фармакор":{"count":103},"Farmàcia":{"count":56},"スギ薬局":{"count":101},"Pague Menos":{"count":55},"Горздрав":{"count":139},"Mēness aptieka":{"count":60},"South Star Drug":{"count":57},"Панацея":{"count":58},"Арніка":{"count":123},"Drogaria São Paulo":{"count":52},"Аптечный пункт":{"count":254},"Невис":{"count":106},"Panvel":{"count":52},"Айболит":{"count":71},"SalcoBrand":{"count":90},"Живика":{"count":58},"Walmart Pharmacy":{"count":84},"Walgreens Pharmacy":{"count":59},"Farmacity":{"count":135},"аптека":{"count":126},"The Generics Pharmacy":{"count":171},"Farmacias del Ahorro":{"count":110},"Farmacias Similares":{"count":84},"Farmatodo":{"count":137},"ツルハドラッグ":{"count":116},"Duane Reade":{"count":78},"Фармленд":{"count":93},"H-E-B Pharmacy":{"count":219},"Планета здоровья":{"count":101},"pharmacie":{"count":62},"А5":{"count":94},"Имплозия":{"count":97},"Eurovaistinė":{"count":83},"Бережная аптека":{"count":58},"36.6":{"count":51},"Benavides":{"count":65},"ドラッグてらしま (Drug Terashima)":{"count":77},"Farmacias Guadalajara":{"count":66},"Drogasil":{"count":93},"Фармакопейка":{"count":53},"Аптека от склада":{"count":52},"クリエイト":{"count":51},"ავერსი (Aversi)":{"count":58},"Inkafarma":{"count":56},"Farmahorro":{"count":51},"ウエルシア":{"count":61},"داروخانه":{"count":58},"丁丁藥局":{"count":72}},"library":{"Stadtbibliothek":{"count":233},"Central Library":{"count":56},"Stadtbücherei":{"count":282},"Médiathèque":{"count":251},"Public Library":{"count":74},"Biblioteca Municipal":{"count":388},"Gemeindebücherei":{"count":138},"Bibliothek":{"count":81},"Biblioteca Comunale":{"count":193},"Library":{"count":536},"Bibliothèque municipale":{"count":203},"Bibliothèque Municipale":{"count":266},"Bibliothèque":{"count":252},"Bücherei":{"count":108},"Biblioteca Pública Municipal":{"count":55},"Bibliotheek":{"count":71},"Библиотека":{"count":369},"Детская библиотека":{"count":208},"Центральная городская библиотека":{"count":52},"Biblioteca":{"count":188},"Miejska Biblioteka Publiczna":{"count":59},"Městská knihovna":{"count":54},"Biblioteka":{"count":71},"Biblioteca comunale":{"count":175},"Biblioteka Publiczna":{"count":68},"Gminna Biblioteka Publiczna":{"count":52},"Центральная библиотека":{"count":69},"Biblioteca Pública":{"count":52},"Бібліотека":{"count":71},"Городская библиотека":{"count":54},"图书馆":{"count":51}},"kindergarten":{"Kindergarten":{"count":980},"Arche Noah":{"count":65},"Waldkindergarten":{"count":101},"Städtischer Kindergarten":{"count":98},"Детский сад №19":{"count":56},"Детский сад №11":{"count":71},"Детский сад №8":{"count":73},"Детский сад №2":{"count":139},"Детский сад №7":{"count":89},"Детский сад №15":{"count":65},"Evangelischer Kindergarten":{"count":214},"Katholischer Kindergarten":{"count":99},"Villa Kunterbunt":{"count":86},"Waldorfkindergarten":{"count":69},"Ev. Kindergarten":{"count":106},"Kindergarten St. Josef":{"count":52},"Kindertagesstätte":{"count":130},"Pusteblume":{"count":55},"École maternelle":{"count":335},"Materská škola":{"count":133},"MŠ":{"count":100},"Детский сад №1":{"count":134},"Детский сад №18":{"count":72},"École Maternelle":{"count":66},"Crèche":{"count":99},"Kindergarten Regenbogen":{"count":61},"Przedszkole":{"count":140},"Mateřská škola":{"count":164},"Детский сад":{"count":812},"детский сад":{"count":77},"Óvoda":{"count":63},"Jardin Infantil":{"count":77},"Детский сад №4":{"count":77},"Nursery School":{"count":110},"Детский сад №3":{"count":112},"Детский сад №14":{"count":69},"Детский сад №5":{"count":95},"Детский сад №10":{"count":69},"Детский сад №29":{"count":51},"Детский сад №22":{"count":55},"Scuola Materna":{"count":60},"Детский сад №25":{"count":53},"Детский сад №6":{"count":85},"Солнышко":{"count":87},"საბავშვო ბაღი (Kindergarten)":{"count":149},"Детский сад №9":{"count":71},"PAUD":{"count":83},"Детский сад №12":{"count":51},"中央保育所":{"count":56},"Детский сад \"Солнышко\"":{"count":54},"Maria Montessori":{"count":91},"Rosaura Zapata":{"count":70},"Federico Froebel":{"count":88},"Детский сад №17":{"count":59},"Gabriela Mistral":{"count":129},"Anganwadi":{"count":85},"Jean Piaget":{"count":82},"Amado Nervo":{"count":64},"Benito Juarez":{"count":89},"Cuauhtemoc":{"count":55},"Educacion Inicial de CONAFE No Escolarizado":{"count":184},"Emiliano Zapata":{"count":60},"Estefania Casta�eda":{"count":53},"Jose Vasconcelos":{"count":71},"Juan Escutia":{"count":83},"Miguel Hidalgo Y Costilla":{"count":57},"Preescolar Comunitario":{"count":409},"CONAFE Preescolar":{"count":91},"Ni�os Heroes":{"count":68},"Sor Juana Ines De La Cruz":{"count":76},"Cursos Comunitarios":{"count":74}},"cafe":{"Starbucks":{"count":6881,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":436},"Costa":{"count":1104},"Caffè Nero":{"count":247},"Кафе":{"count":364},"Café Central":{"count":100},"Tim Hortons":{"count":1662},"Second Cup":{"count":227},"Country Style":{"count":60},"Bar Centrale":{"count":67},"Dunkin' Donuts":{"count":1300,"tags":{"cuisine":"donut"}},"Espresso House":{"count":95},"Coffee Time":{"count":134},"Eiscafe Dolomiti":{"count":52},"Cafe Coffee Day":{"count":197},"Eiscafe Venezia":{"count":226},"スターバックス":{"count":503,"tags":{"name:en":"Starbucks"}},"タリーズコーヒー":{"count":230},"Coffee House":{"count":62},"Segafredo":{"count":101},"Dolce Vita":{"count":55},"Pret A Manger":{"count":155},"Barista":{"count":53},"Venezia":{"count":52},"Шоколадница":{"count":223},"Peet's Coffee & Tea":{"count":64},"Jamba Juice":{"count":68},"Сказка":{"count":56},"ドトールコーヒーショップ":{"count":488},"Café des Sports":{"count":63},"Internet Cafe":{"count":53},"Coffee Shop":{"count":53},"Bistro":{"count":56},"Столовая":{"count":903},"Tchibo":{"count":172},"Бистро":{"count":65},"The Coffee Club":{"count":62},"Теремок":{"count":56},"Кофе Хауз":{"count":121},"Wayne's Coffee":{"count":51},"星巴克":{"count":93},"The Coffee Bean & Tea Leaf":{"count":69},"Закусочная":{"count":74},"Caribou Coffee":{"count":147},"Уют":{"count":81},"ארומה":{"count":53},"カフェ・ベローチェ":{"count":106},"Кофейня":{"count":56},"Шашлычная":{"count":109},"Кафетерий":{"count":55},"Встреча":{"count":70},"Coffee Fellows":{"count":68},"Havanna":{"count":52},"コメダ珈琲店":{"count":147},"คาเฟ่ อเมซอน":{"count":61},"Traveler's Coffee":{"count":64},"85度C":{"count":80},"Café Amazon":{"count":231},"エクセルシオール カフェ":{"count":56},"カフェ・ド・クリエ":{"count":76,"tags":{"name:en":"Cafe de CRIE"}},"サンマルクカフェ":{"count":150},"Bar Kafe":{"count":154}},"school":{"Школа №17":{"count":105},"Kindergarten":{"count":120},"Základná škola":{"count":96},"Astrid-Lindgren-Schule":{"count":78},"École primaire":{"count":2788},"Albert-Schweitzer-Schule":{"count":84},"Grundschule":{"count":653},"Realschule":{"count":75},"school":{"count":51},"Volkshochschule":{"count":100},"Sunnyside School":{"count":61},"Primary School":{"count":1171},"École primaire Jules Ferry":{"count":78},"Základní škola":{"count":159},"Musikschule":{"count":156},"École Primaire":{"count":184},"École Saint-Joseph":{"count":85},"École maternelle":{"count":880},"Erich-Kästner-Schule":{"count":53},"Hauptschule":{"count":156},"Escola Primária":{"count":66},"ZŠ":{"count":111},"Holy Trinity School":{"count":60},"Школа №21":{"count":69},"Школа №27":{"count":55},"Школа №7":{"count":198},"Школа №11":{"count":132},"Школа №16":{"count":89},"Школа №24":{"count":71},"Школа №22":{"count":76},"Saint Joseph School":{"count":149},"Scuola Materna":{"count":63},"Center School":{"count":118},"Jackson School":{"count":60},"Lincoln School":{"count":288},"Roosevelt School":{"count":118},"Pleasant Valley School":{"count":86},"Garfield Elementary School":{"count":66},"Riverside School":{"count":76},"Gimnazjum nr 1":{"count":57},"École Communale":{"count":98},"Volksschule":{"count":372},"Pestalozzischule":{"count":84},"Scuola Elementare":{"count":201},"Scuole Elementari":{"count":61},"Colegio San José":{"count":64},"Schule":{"count":56},"Средняя школа":{"count":161},"Школа №1":{"count":534},"Trinity School":{"count":86},"Secondary School":{"count":222},"Szkoła":{"count":59},"Saint Johns School":{"count":176},"Schillerschule":{"count":59},"Музыкальная школа":{"count":257},"École élémentaire":{"count":845},"École maternelle publique":{"count":78},"École primaire publique":{"count":144},"Гимназия №1":{"count":93},"École primaire privée Saint-Joseph":{"count":130},"Collège Jean Moulin":{"count":65},"Primarschule":{"count":68},"Emiliano Zapata":{"count":286},"Школа №18":{"count":108},"Школа №2":{"count":481},"Школа №3":{"count":380},"Школа №5":{"count":266},"Гимназия":{"count":80},"Школа №26":{"count":60},"École communale":{"count":56},"Scuola Media":{"count":95},"Escuela":{"count":313},"Miguel Hidalgo":{"count":85},"Turnhalle":{"count":60},"École":{"count":452},"Longfellow Elementary School":{"count":54},"Oak Grove School":{"count":149},"Scuola elementare":{"count":94},"Central High School":{"count":140},"Central Elementary School":{"count":176},"Central School":{"count":222},"Fairview School":{"count":166},"Fairview Elementary School":{"count":61},"Garfield School":{"count":61},"Franklin School":{"count":134},"Franklin Elementary School":{"count":100},"Highland School":{"count":72},"Holy Cross School":{"count":70},"Holy Family School":{"count":75},"Hillcrest Elementary School":{"count":60},"Jefferson Elementary School":{"count":177},"Jackson Elementary School":{"count":52},"Jefferson School":{"count":116},"Immaculate Conception School":{"count":86},"Lincoln Elementary School":{"count":264},"Liberty School":{"count":87},"Longfellow School":{"count":60},"McKinley School":{"count":65},"Maple Grove School":{"count":51},"Pine Grove School":{"count":63},"Pleasant View School":{"count":62},"Pleasant Hill School":{"count":110},"Roosevelt Elementary School":{"count":116},"Saint Josephs School":{"count":166},"Saint James School":{"count":89},"Sacred Heart School":{"count":210},"Saint Pauls School":{"count":76},"Saint Peters School":{"count":83},"Saint Marys School":{"count":272},"Saint Patricks School":{"count":87},"Saint Paul School":{"count":53},"Smith School":{"count":61},"Union School":{"count":131},"Trinity Lutheran School":{"count":51},"Wilson School":{"count":85},"Wilson Elementary School":{"count":63},"Washington School":{"count":225},"Washington Elementary School":{"count":191},"White School":{"count":52},"West Elementary School":{"count":58},"Saint Francis School":{"count":58},"Saint Mary School":{"count":53},"North Elementary School":{"count":56},"McKinley Elementary School":{"count":61},"Brown School":{"count":57},"Miller School":{"count":67},"Mount Pleasant School":{"count":63},"School Number 1":{"count":250},"School Number 4":{"count":137},"School Number 3":{"count":192},"School Number 2":{"count":217},"Highland Elementary School":{"count":52},"Liberty Elementary School":{"count":52},"Bethel School (historical)":{"count":97},"Cedar Grove School (historical)":{"count":71},"Center School (historical)":{"count":87},"Central School (historical)":{"count":70},"Fairview School (historical)":{"count":219},"Friendship School (historical)":{"count":63},"Hickory Grove School (historical)":{"count":59},"Hopewell School (historical)":{"count":63},"Liberty School (historical)":{"count":111},"Mount Olive School (historical)":{"count":55},"Mount Pleasant School (historical)":{"count":77},"Mount Zion School (historical)":{"count":96},"New Hope School (historical)":{"count":95},"Oak Hill School (historical)":{"count":53},"Pine Grove School (historical)":{"count":103},"Pleasant Grove School (historical)":{"count":92},"Pleasant Hill School (historical)":{"count":134},"Salem School (historical)":{"count":72},"Union School (historical)":{"count":157},"Walnut Grove School (historical)":{"count":70},"Oak Grove School (historical)":{"count":261},"Maple Grove School (historical)":{"count":54},"Pleasant Valley School (historical)":{"count":95},"Shady Grove School (historical)":{"count":80},"Shiloh School (historical)":{"count":75},"School Number 3 (historical)":{"count":276},"School Number 4 (historical)":{"count":304},"Antioch School (historical)":{"count":70},"School Number 1 (historical)":{"count":276},"School Number 12 (historical)":{"count":128},"School Number 5 (historical)":{"count":272},"School Number 7 (historical)":{"count":255},"School Number 8 (historical)":{"count":209},"School Number 10 (historical)":{"count":160},"Mount Zion School":{"count":53},"Midway School (historical)":{"count":55},"School Number 15 (historical)":{"count":56},"School Number 2 (historical)":{"count":290},"School Number 9 (historical)":{"count":185},"High School":{"count":62},"School Number 6 (historical)":{"count":263},"School Number 11 (historical)":{"count":130},"School Number 14 (historical)":{"count":71},"School Number 13 (historical)":{"count":91},"Gimnazjum":{"count":96},"Zespół Szkół":{"count":83},"Szkoła Podstawowa":{"count":291},"Школа искусств":{"count":75},"School":{"count":1293},"École Élémentaire":{"count":65},"École Maternelle":{"count":62},"Школа №10":{"count":164},"Школа №19":{"count":92},"Escuela Primaria":{"count":79},"École élémentaire publique":{"count":104},"Ecole":{"count":248},"Школа":{"count":665},"Школа №12":{"count":130},"Elementary School":{"count":64},"Scuola Primaria":{"count":71},"Escola Municipal":{"count":161},"Детская школа искусств":{"count":101},"Groupe Scolaire":{"count":55},"CEM":{"count":183},"Школа №8":{"count":187},"École primaire Jean Jaurès":{"count":61},"Szkoła Podstawowa nr 3":{"count":57},"Школа №14":{"count":116},"Школа №13":{"count":128},"Школа № 1":{"count":126},"Szkoła Podstawowa nr 2":{"count":73},"Colegio":{"count":66},"Szkoła Podstawowa nr 1":{"count":75},"Школа №25":{"count":59},"Школа № 2":{"count":105},"Școala":{"count":51},"École primaire privée Notre-Dame":{"count":63},"Школа №4":{"count":275},"Школа №6":{"count":205},"Школа №15":{"count":113},"EPP":{"count":68},"Школа № 4":{"count":78},"Lycée":{"count":120},"Школа № 6":{"count":52},"Ecole primaire":{"count":107},"Szkoła podstawowa":{"count":77},"Школа № 3":{"count":78},"Sekolah":{"count":184},"Escola Estadual":{"count":57},"Collège privé Saint-Joseph":{"count":56},"Школа №9":{"count":178},"Начальная школа":{"count":77},"Школа №23":{"count":76},"Primaire":{"count":59},"Школа-интернат":{"count":109},"Школа №20":{"count":94},"Школа №31":{"count":51},"市立南小学校":{"count":58},"Ecole Primaire":{"count":57},"Средняя школа №2":{"count":70},"UNIDAD EDUCATIVA":{"count":105},"Средняя школа №1":{"count":59},"Nursery School":{"count":66},"Benito Juarez":{"count":295},"SDN":{"count":287},"市立南中学校":{"count":57},"市立東中学校":{"count":62},"市立西中学校":{"count":52},"SD":{"count":54},"Leona Vicario":{"count":64},"École primaire privée Sainte-Marie":{"count":57},"школа":{"count":72},"ESCUELA":{"count":224},"Escola":{"count":70},"École primaire privée":{"count":64},"Általános iskola":{"count":106},"Adolfo Lopez Mateos":{"count":139},"Amado Nervo":{"count":85},"Cuauhtemoc":{"count":152},"Gregorio Torres Quintero":{"count":54},"Guadalupe Victoria":{"count":58},"Ignacio Zaragoza":{"count":98},"Justo Sierra":{"count":118},"Nicolas Bravo":{"count":58},"Vicente Guerrero":{"count":159},"مدرسه":{"count":97},"ESCOLA":{"count":75},"sekolah":{"count":75},"École Primaire Publique":{"count":53},"Juan Escutia":{"count":122},"Josefa Ortiz De Dominguez":{"count":78},"Curso Comunitario":{"count":58},"Francisco I. Madero":{"count":52},"Francisco Villa":{"count":116},"Ignacio Allende":{"count":51},"Jose Clemente Orozco":{"count":59},"Lazaro Cardenas":{"count":69},"Miguel Hidalgo Y Costilla":{"count":213},"Ni�os Heroes":{"count":158},"Ricardo Flores Magon":{"count":91},"Valentin Gomez Farias":{"count":72},"Venustiano Carranza":{"count":64},"Jose Vasconcelos":{"count":75},"Lazaro Cardenas Del Rio":{"count":153},"Agustin Ya�ez":{"count":57},"Manuel Lopez Cotilla":{"count":111},"Nombre En Tramite":{"count":130},"Sor Juana Ines De La Cruz":{"count":56},"Francisco I Madero":{"count":87},"Jose Ma Morelos Y Pavon":{"count":120},"Pedro Moreno":{"count":69},"Ramon Corona":{"count":54},"Cursos Comunitarios":{"count":116},"Primaria Comunitaria":{"count":58},"Saint Kizito Primary School":{"count":61}},"cinema":{"Odeon":{"count":77},"Cineworld":{"count":58},"Cinemark":{"count":68}},"doctors":{"Ärztehaus":{"count":243},"Centro de Salud":{"count":89},"Поликлиника":{"count":152},"Cabinet Médical":{"count":77},"Orvosi rendelő":{"count":64},"Cabinet médical":{"count":84},"Инвитро":{"count":74},"Женская консультация":{"count":66},"Детская поликлиника":{"count":89},"Амбулатория":{"count":53},"Фельдшерско-акушерский пункт":{"count":58},"Ośrodek Zdrowia":{"count":58},"Медпункт":{"count":65},"ФАП":{"count":87},"Consultorio Médico":{"count":102},"Centre de Santé":{"count":54}},"hospital":{"Детская поликлиника":{"count":181},"Centro de Saúde":{"count":86},"Женская консультация":{"count":55},"Centro de Salud":{"count":280},"Cruz Roja":{"count":79},"IMSS":{"count":62},"Government Hospital":{"count":77},"ЦРБ":{"count":114},"Больница":{"count":276},"Поликлиника":{"count":325},"Центральная районная больница":{"count":133},"Роддом":{"count":106},"Городская больница":{"count":60},"Hospital":{"count":157},"Кожно-венерологический диспансер":{"count":51},"Hospital Municipal":{"count":102},"Hôpital":{"count":51},"경희한의원 (Gyeonghui Oriental Medicine Clinic)":{"count":52},"Амбулатория":{"count":81},"Скорая помощь":{"count":66},"Районная больница":{"count":74},"Медпункт":{"count":54},"Детская больница":{"count":87},"ФАП":{"count":86},"Родильный дом":{"count":66},"Инфекционное отделение":{"count":71},"Consultorio Médico":{"count":64},"โรงพยาบาลส่งเสริมสุขภาพตำบล":{"count":59}},"biergarten":{"Biergarten":{"count":86}},"theatre":{"Teatro Municipal":{"count":75},"Freilichtbühne":{"count":67},"Дом культуры":{"count":51},"Amphitheatre":{"count":77},"Anfiteatro":{"count":74},"Amphitheater":{"count":85},"Amfiteatr":{"count":79}},"bar":{"Bar Sport":{"count":81},"Bar Centrale":{"count":115},"Бар":{"count":53},"Beach Bar":{"count":55}},"car_wash":{"Spălătorie Auto":{"count":61},"Автомойка":{"count":1105},"Shell":{"count":150},"Statoil":{"count":58},"Мойка":{"count":106},"Car Wash":{"count":400},"Waschanlage":{"count":66},"Aral":{"count":86},"BP":{"count":61},"автомойка":{"count":60},"Intermarché":{"count":57},"Car wash":{"count":60},"Myjnia samochodowa":{"count":52},"Myjnia bezdotykowa":{"count":62},"H-E-B Car Wash":{"count":65}},"clinic":{"Детская поликлиника":{"count":130},"Centro de Salud":{"count":127},"Polyclinique":{"count":90},"Поликлиника":{"count":147},"Posto de Saúde":{"count":64},"Clinic":{"count":83},"Health Sub Center":{"count":71},"ФАП":{"count":138},"Aid Post":{"count":69}},"bicycle_rental":{"metropolradruhr":{"count":82},"Bicing":{"count":99}},"veterinary":{"Clinique vétérinaire":{"count":75},"Ветлечебница":{"count":55},"Vétérinaire":{"count":68},"Clinique Vétérinaire":{"count":106},"Ветеринарная клиника":{"count":60}},"dentist":{"Стоматология":{"count":437},"Zahnarzt":{"count":68},"Dentiste":{"count":61},"Clínica Dental":{"count":56},"Dental Clinic":{"count":55},"Dentist":{"count":85},"Aspen Dental":{"count":51},"Стоматологическая поликлиника":{"count":87},"Clinica Dental":{"count":212}},"casino":{"Casino":{"count":59}},"arts_centre":{"Дом культуры":{"count":159},"Клуб":{"count":55},"Świetlica wiejska":{"count":52}},"ice_cream":{"Grido":{"count":98}},"sauna":{"Баня":{"count":102}},"driving_school":{"Автошкола":{"count":92}},"social_facility":{"Социальный участковый":{"count":208},"Safe Haven":{"count":92}}},"shop":{"mobile_phone":{"Vodafone":{"count":827},"T-Punkt":{"count":53},"Билайн":{"count":318},"ソフトバンクショップ":{"count":463},"O2":{"count":433},"Carphone Warehouse":{"count":260},"Orange":{"count":565},"3 Store":{"count":64},"T-Mobile":{"count":477},"Sprint":{"count":269},"AT&T":{"count":380},"Verizon Wireless":{"count":453},"Verizon":{"count":94},"mobilcom debitel":{"count":56},"Евросеть":{"count":821},"Movistar":{"count":257},"Telekom Shop":{"count":80},"EE":{"count":143},"Telekom":{"count":111},"ドコモショップ":{"count":370},"Telus":{"count":51},"Claro":{"count":326},"Cricket":{"count":77},"The Phone House":{"count":120},"vodafone":{"count":59},"Bell":{"count":123},"SFR":{"count":133},"МТС":{"count":731},"Metro PCS":{"count":55},"Digicel":{"count":127},"Связной":{"count":695},"Мегафон":{"count":457},"Теле2":{"count":58},"Wind":{"count":115},"Boost Mobile":{"count":91},"Алло":{"count":52},"МегаФон":{"count":76},"MetroPCS":{"count":65},"Play":{"count":103},"Telenor":{"count":79},"au":{"count":107},"Plus":{"count":73},"Tele2":{"count":90},"auショップ":{"count":318},"Bitė":{"count":68}},"supermarket":{"Eurospar":{"count":329},"Merkur":{"count":117},"Sainsbury's":{"count":580},"Lidl":{"count":8439},"Edeka":{"count":2195},"Coles":{"count":525},"Iceland":{"count":468},"Woolworths":{"count":754},"denn's Biomarkt":{"count":119},"Billa":{"count":1507},"ALDI":{"count":5976},"The Co-operative Food":{"count":1146},"Checkers":{"count":134},"Penny":{"count":1836},"Norma":{"count":1128},"NETTO":{"count":4439},"Morrisons":{"count":447},"Kaufland":{"count":1119},"Tesco Metro":{"count":148},"Intermarché Contact":{"count":97},"Nahkauf":{"count":198},"Rewe":{"count":2766},"Aldi Süd":{"count":720},"Real":{"count":195},"Tesco Express":{"count":504},"King Soopers":{"count":96},"Kiwi":{"count":219},"Asda":{"count":234},"Pick n Pay":{"count":258},"Coop":{"count":2227},"Tengelmann":{"count":189},"Carrefour":{"count":2267},"Waitrose":{"count":288},"Spar":{"count":2996},"Hofer":{"count":461},"Tesco":{"count":1371},"M-Preis":{"count":80},"tegut":{"count":213},"Sainsbury's Local":{"count":200},"E-Center":{"count":66},"Aldi Nord":{"count":260},"Meijer":{"count":112},"Safeway":{"count":553},"No Frills":{"count":163},"Budgens":{"count":81},"Albert":{"count":245},"Jumbo":{"count":421},"Interspar":{"count":112},"Shoprite":{"count":294},"MPreis":{"count":70},"Penny Market":{"count":543},"Tesco Extra":{"count":186},"Albert Heijn":{"count":697},"IGA":{"count":488},"Super U":{"count":577},"Metro":{"count":333},"Migros":{"count":586},"Marktkauf":{"count":118},"Combi":{"count":121},"ICA":{"count":176},"PLUS":{"count":100},"Hoogvliet":{"count":60},"EMTÉ":{"count":66},"coop":{"count":80},"Dirk van den Broek":{"count":52},"Food Basics":{"count":108},"Casino":{"count":332},"Penny Markt":{"count":482},"Giant":{"count":243},"COOP Jednota":{"count":136},"ADEG":{"count":77},"ALDI SÜD":{"count":130},"Simply Market":{"count":483},"Konzum":{"count":321},"Carrefour Express":{"count":634},"Foodland":{"count":170},"Famila":{"count":152},"Hemköp":{"count":83},"Markant":{"count":91},"Volg":{"count":200},"Netto Marken-Discount":{"count":525},"Leader Price":{"count":417},"Treff 3000":{"count":120},"Kaiser's":{"count":231},"K+K":{"count":108},"Unimarkt":{"count":97},"Meny":{"count":79},"U Express":{"count":88},"Carrefour City":{"count":268},"Bim":{"count":468},"real,-":{"count":78},"Countdown":{"count":130},"Sobeys":{"count":158},"Cargills Food City":{"count":71},"S-Market":{"count":110},"Denner":{"count":376},"Zielpunkt":{"count":76},"NP":{"count":178},"Franprix":{"count":371},"Monoprix":{"count":271},"Landi":{"count":51},"Diska":{"count":66},"PENNY":{"count":86},"Giant Eagle":{"count":118},"NORMA":{"count":133},"NP-Markt":{"count":77},"Coop Extra":{"count":72},"AD Delhaize":{"count":75},"Grocery Outlet":{"count":79},"Auchan":{"count":215},"Mercadona":{"count":1050},"Consum":{"count":169},"Pam":{"count":73},"sky":{"count":118},"Şok":{"count":114},"Despar":{"count":194},"Eroski":{"count":269},"Costcutter":{"count":88},"SuperValu":{"count":73},"Dia":{"count":1198},"Rema 1000":{"count":437},"Bunnpris":{"count":69},"Konsum":{"count":140},"Profi":{"count":176},"Colruyt":{"count":203},"The Co-operative":{"count":67},"Rimi":{"count":112},"Delhaize":{"count":224},"CBA":{"count":241},"Real Canadian Superstore":{"count":59},"Mercator":{"count":155},"Smith's":{"count":53},"Walmart":{"count":999},"Kroger":{"count":550},"Intermarché":{"count":1559},"Albertsons":{"count":304},"Trader Joe's":{"count":324},"Feneberg":{"count":64},"Kvickly":{"count":54},"Makro":{"count":188},"Лента":{"count":104},"Nah & Frisch":{"count":100},"ALDI Nord":{"count":83},"Maxi":{"count":154},"Ашан":{"count":65},"CRAI":{"count":56},"ICA Supermarket":{"count":83},"Fakta":{"count":274},"Ekom":{"count":61},"Магнит":{"count":3327},"nahkauf":{"count":103},"Caprabo":{"count":129},"Famiglia Cooperativa":{"count":84},"Народная 7Я семьЯ":{"count":194},"Carrefour Contact":{"count":185},"Авоська":{"count":55},"Maxima XX":{"count":60},"Petit Casino":{"count":142},"Wasgau":{"count":55},"Match":{"count":133},"Pingo Doce":{"count":281},"Lider":{"count":69},"Unimarc":{"count":217},"Santa Isabel":{"count":155},"Седьмой континент":{"count":87},"Carrefour express":{"count":70},"HIT":{"count":60},"Пятёрочка":{"count":2392},"Conad":{"count":449},"Фуршет":{"count":82},"Willys":{"count":89},"Farmfoods":{"count":114},"Фора":{"count":126},"Dunnes Stores":{"count":74},"Сільпо":{"count":196},"マルエツ":{"count":87},"Alnatura":{"count":77},"Piggly Wiggly":{"count":87},"Prix":{"count":51},"Crai":{"count":99},"Biocoop":{"count":105},"REWE City":{"count":60},"El Árbol":{"count":103},"E. Leclerc":{"count":55},"Super Brugsen":{"count":69},"Дикси":{"count":1323},"Whole Foods Market":{"count":328,"tags":{"shop":"supermarket"}},"G20":{"count":65},"Eroski Center":{"count":63},"Publix":{"count":568},"Conad City":{"count":70},"Перекресток":{"count":409},"Føtex":{"count":67},"イトーヨーカドー":{"count":64},"Sprouts Farmers Market":{"count":55},"Ralphs":{"count":75},"Mila":{"count":73},"Biedronka":{"count":2155},"Proxi":{"count":55},"Eurospin":{"count":273},"Семья":{"count":76},"Виват":{"count":60},"Froiz":{"count":72},"Gadis":{"count":107},"Евроопт":{"count":168},"Квартал":{"count":74},"Esselunga":{"count":105},"SuperBrugsen":{"count":72},"Reliance Fresh":{"count":80},"Winn-Dixie":{"count":67},"Coop Jednota":{"count":97},"Irma":{"count":70},"Londis":{"count":54},"Mega Image":{"count":88},"Centre Commercial E. Leclerc":{"count":486},"西友":{"count":124},"A101":{"count":193},"Game":{"count":55},"Coop Konsum":{"count":97},"Alimerka":{"count":94},"Piotr i Paweł":{"count":98},"A&O":{"count":61},"Coviran":{"count":87},"Żabka":{"count":61},"Maxima":{"count":106},"ΑΒ Βασιλόπουλος":{"count":62},"Stokrotka":{"count":197},"Карусель":{"count":66},"Utile":{"count":52},"CONAD":{"count":64},"Winn Dixie":{"count":76},"Vival":{"count":67},"Μασούτης":{"count":64},"Mini Market":{"count":115},"イオン":{"count":84},"Chedraui":{"count":60},"Family Dollar":{"count":59},"Walmart Neighborhood Market":{"count":112},"Delikatesy Centrum":{"count":158},"Géant Casino":{"count":63},"Ekono":{"count":59},"ASDA":{"count":220},"Intermarche":{"count":117},"Stop & Shop":{"count":118},"Maxima X":{"count":147},"Tottus":{"count":58},"Food Lion":{"count":388},"Społem":{"count":99},"Harris Teeter":{"count":140},"Sam's Club":{"count":259},"Continente":{"count":96},"Foodworks":{"count":78},"Centra":{"count":60},"Maxi Dia":{"count":52},"H-E-B":{"count":271},"Price Chopper":{"count":123},"Soriana":{"count":148},"Атак":{"count":80},"Puregold":{"count":58},"Полушка":{"count":198},"Jewel-Osco":{"count":53},"Condis":{"count":103},"Big C":{"count":53},"Sigma":{"count":95},"Верный":{"count":160},"いなげや":{"count":57},"АТБ":{"count":499},"Bodega Aurrera":{"count":167},"Fresh Market":{"count":75},"Виктория":{"count":64},"Азбука Вкуса":{"count":52},"QFC":{"count":55},"Vea":{"count":62},"Minipreço":{"count":204},"Lewiatan":{"count":208},"Sisa":{"count":51},"Монетка":{"count":291},"Магнолия":{"count":111},"POLOmarket":{"count":186},"Palí":{"count":64},"Costco":{"count":264},"Tesco Lotus":{"count":90},"Lupa":{"count":69},"Hy-Vee":{"count":110},"Копейка":{"count":54},"Disco":{"count":68},"Pão de Açúcar":{"count":59},"Save-A-Lot":{"count":79,"tags":{"shop":"supermarket"}},"Walmart Supercenter":{"count":511},"Comercial Mexicana":{"count":53},"Plodine":{"count":61},"Grand Frais":{"count":52},"Hannaford":{"count":87},"Fred Meyer":{"count":76},"Мария-Ра":{"count":118},"マックスバリュ":{"count":109},"Wegmans":{"count":88},"Dia %":{"count":136},"Dollar General":{"count":92},"Cub Foods":{"count":51},"Универсам":{"count":84},"Extra":{"count":126},"Магазин":{"count":130},"ライフ":{"count":117},"Picard":{"count":93},"Гроздь":{"count":52},"Супермаркет":{"count":52},"Atacadão":{"count":63},"New World":{"count":86},"Ahorramás":{"count":56},"Eko":{"count":64},"Oxxo":{"count":112},"業務スーパー":{"count":129},"Despensa Familiar":{"count":68},"Красный Яр":{"count":60},"全聯福利中心":{"count":157},"Plaza Vea":{"count":53},"Командор":{"count":66},"Таврія-В":{"count":56},"まいばすけっと":{"count":108},"Norfa XL":{"count":63},"Indomaret":{"count":123},"Радеж":{"count":58},"Mercado de Abastos":{"count":52},"Dino":{"count":182},"Покупочка":{"count":51},"Хүнсний дэлгүүр":{"count":64},"E. Leclerc Drive":{"count":55},"Leclerc Drive":{"count":127},"Alfamart":{"count":76},"全聯":{"count":62}},"sports":{"Sports Direct":{"count":160},"Decathlon":{"count":388},"Intersport":{"count":614},"Спорттовары":{"count":60},"Nike":{"count":57},"Sports Authority":{"count":147},"Спортмастер":{"count":165},"Hervis":{"count":55},"Sport 2000":{"count":147},"Big 5 Sporting Goods":{"count":68},"Adidas":{"count":90},"Dick's Sporting Goods":{"count":170}},"department_store":{"Big W":{"count":114},"Canadian Tire":{"count":150},"Karstadt":{"count":64},"Walmart":{"count":802},"Kmart":{"count":359},"El Corte Inglés":{"count":60},"Target":{"count":947},"Big Lots":{"count":86},"The Warehouse":{"count":56},"Galeria Kaufhof":{"count":61},"Dollar General":{"count":53},"Debenhams":{"count":110},"Marks & Spencer":{"count":115},"Argos":{"count":71},"Woolworth":{"count":112},"Универмаг":{"count":139},"Sears":{"count":384},"Walmart Supercenter":{"count":195},"BHS":{"count":81},"Kohl's":{"count":302},"Bed Bath & Beyond":{"count":58},"Dillard's":{"count":60},"Macy's":{"count":243},"JCPenney":{"count":214},"Dollar Tree":{"count":59},"HEMA":{"count":157},"Family Dollar":{"count":65},"Sam's Club":{"count":95},"Магнит":{"count":87}},"convenience":{"Shell":{"count":412},"Statoil":{"count":78},"Spar":{"count":1340},"Tesco Express":{"count":605},"Sainsbury's Local":{"count":192},"Aral":{"count":84},"One Stop":{"count":233},"The Co-operative Food":{"count":318},"Londis":{"count":453},"7-Eleven":{"count":8603},"Coop":{"count":1110},"Sale":{"count":86},"Sheetz":{"count":111},"Costcutter":{"count":379},"Konzum":{"count":219},"Siwa":{"count":222},"Esso":{"count":120},"COOP Jednota":{"count":324},"Mac's":{"count":254},"Alepa":{"count":68},"Hasty Market":{"count":84},"K-Market":{"count":64},"On the Run":{"count":106},"CBA":{"count":291},"Valintatalo":{"count":55},"Alimentación":{"count":88},"Casino":{"count":89},"Franprix":{"count":85},"Circle K":{"count":603},"セブンイレブン":{"count":6433,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":3335,"tags":{"name:en":"LAWSON"}},"BP":{"count":238},"Tesco":{"count":59},"OK":{"count":72},"Migrolino":{"count":66},"Petit Casino":{"count":285},"Volg":{"count":147},"Total":{"count":51},"Coop Pronto":{"count":53},"Mace":{"count":142},"Mini Market":{"count":874},"Kiosk":{"count":455},"Nisa Local":{"count":114},"Продукты":{"count":7552},"Mini Stop":{"count":362},"LAWSON":{"count":265},"デイリーヤマザキ":{"count":314},"Biedronka":{"count":96},"Надежда":{"count":94},"Mobil":{"count":78},"Nisa":{"count":67},"Premier":{"count":224},"ABC":{"count":655},"КазМунайГаз":{"count":120},"McColl's":{"count":159},"Carrefour Express":{"count":178},"ミニストップ":{"count":631,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":893,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":221},"8 à Huit":{"count":76},"Żabka":{"count":1199},"Almacen":{"count":375},"Mercator":{"count":109},"Proxi":{"count":199},"萊爾富":{"count":298},"全家便利商店":{"count":524},"Vival":{"count":309},"FamilyMart":{"count":742},"ファミリーマート":{"count":3307,"tags":{"name:en":"FamilyMart"}},"Магазин":{"count":1407},"Carrefour City":{"count":88},"Casey's General Store":{"count":174},"セブンイレブン(Seven-Eleven)":{"count":246},"Jednota":{"count":67},"Гастроном":{"count":321},"Centra":{"count":130},"Евроопт":{"count":104},"Couche-Tard":{"count":71},"サークルK":{"count":976,"tags":{"name:en":"Circle K"}},"7 eleven":{"count":71},"Mlin i pekare":{"count":59},"Sunkus":{"count":54},"Wawa":{"count":228},"Lifestyle Express":{"count":75},"Chevron":{"count":89},"ampm":{"count":132},"General Store":{"count":75},"Tchibo":{"count":65},"Lewiatan":{"count":397},"Coles Express":{"count":170},"Večerka":{"count":110},"Potraviny":{"count":393},"Rite Aid":{"count":59},"Select":{"count":111},"Ассорти":{"count":85},"Копейка":{"count":55},"Универсам":{"count":118},"Берёзка":{"count":170},"Русь":{"count":52},"Społem":{"count":161},"Shop":{"count":473},"Cumberland Farms":{"count":96},"Fresh Market":{"count":75},"Мария-Ра":{"count":189},"Пятёрочка":{"count":671},"Woolworths Petrol":{"count":73},"Aibė":{"count":71},"Coop Jednota":{"count":120},"Магнит":{"count":1196},"Tesco Lotus Express":{"count":89},"Смак":{"count":104},"Виктория":{"count":122},"Plaid Pantry":{"count":60},"M&S Simply Food":{"count":56},"Sklep spożywczy":{"count":223},"Groszek":{"count":187},"Продукти":{"count":595},"Martin's":{"count":51},"Bodega":{"count":55},"Татьяна":{"count":55},"24 часа":{"count":76},"Минимаркет":{"count":279},"Oxxo":{"count":1566},"abc":{"count":237},"Family Dollar":{"count":60},"Stewart's":{"count":253},"Любимый":{"count":67},"Shop & Go":{"count":72},"Hruška":{"count":76},"ローソンストア100":{"count":252},"CU":{"count":57},"Гурман":{"count":57},"Sunoco":{"count":54},"Радуга":{"count":131},"Delikatesy Centrum":{"count":142},"Фортуна":{"count":80},"Магнолия":{"count":79},"Magazin Mixt":{"count":98},"Kum & Go":{"count":102},"Magazin Alimentar":{"count":60},"Маяк":{"count":58},"Prehrana":{"count":87},"Picard":{"count":95},"Epicerie":{"count":398},"Эконом":{"count":60},"Four Square":{"count":84},"Визит":{"count":84},"Nasz Sklep":{"count":64},"Апельсин":{"count":53},"Авоська":{"count":92},"Dollar General":{"count":397},"Studenac":{"count":107},"Magazin":{"count":61},"Central Convenience Store":{"count":66},"セイコーマート":{"count":258},"Mini Mart":{"count":62},"Mini ABC":{"count":66},"Holiday":{"count":65},"Тройка":{"count":56},"продукты":{"count":139},"Теремок":{"count":91},"Alimentari":{"count":72},"Кулинария":{"count":105},"Агрокомплекс":{"count":51},"Speedway":{"count":85},"全家":{"count":414},"Citgo":{"count":73},"GS25":{"count":89},"Extra":{"count":63},"Kwik Trip":{"count":116},"Мечта":{"count":81},"Кировский":{"count":78},"United Dairy Farmers":{"count":52},"Freshmarket":{"count":87},"Mini market":{"count":66},"Перекресток":{"count":118},"Delikatesy":{"count":127},"Odido":{"count":74},"Corner Store":{"count":74},"Монетка":{"count":118},"Ромашка":{"count":63},"Alimentation Générale":{"count":73},"ポプラ":{"count":95},"BP Shop":{"count":57},"Верный":{"count":52},"Дикси":{"count":246},"Продуктовый":{"count":227},"Almacén":{"count":73},"Весна":{"count":78},"Центральный":{"count":58},"Мираж":{"count":51},"Mini Market Non-Stop":{"count":124},"ココストア":{"count":53},"Светлана":{"count":72},"Копеечка":{"count":83},"QuikTrip":{"count":133},"Меркурий":{"count":63},"Food Mart":{"count":384},"Royal Farms":{"count":75},"Vegyesbolt":{"count":326},"Елена":{"count":51},"Продукты 24":{"count":61},"Kisbolt":{"count":98},"Parduotuvė":{"count":67},"Reál":{"count":52},"AMPM":{"count":114},"ヤマザキショップ":{"count":86},"Продуктовый магазин":{"count":611},"Alfamart":{"count":228},"odido":{"count":53},"Indomaret":{"count":275},"магазин":{"count":258},"Alfa Mart":{"count":55},"Fresh":{"count":57},"Boutique":{"count":163},"მარკეტი (Market)":{"count":140},"Magazin alimentar":{"count":52},"OK便利商店":{"count":65},"Magazin Non-Stop":{"count":64},"Sari-sari Store":{"count":65},"Stores":{"count":66},"Małpka Express":{"count":56},"Baqala":{"count":61}},"chemist":{"Budnikowsky":{"count":102},"dm":{"count":1667},"Müller":{"count":321},"Schlecker":{"count":97},"Etos":{"count":480},"Bipa":{"count":398},"Rossmann":{"count":2241},"Teta":{"count":82},"Kruidvat":{"count":338},"Walgreens":{"count":57},"Рубль Бум":{"count":64},"Watsons":{"count":87},"Улыбка радуги":{"count":96},"7 Дней":{"count":52},"Бытовая химия":{"count":67},"丁丁藥局":{"count":72},"Магнит Косметик":{"count":53},"屈臣氏":{"count":56}},"car_repair":{"Peugeot":{"count":133},"Kwik Fit":{"count":215},"A.T.U":{"count":460},"Midas":{"count":387},"Feu Vert":{"count":152},"Norauto":{"count":220},"Шиномонтаж":{"count":2787},"Speedy":{"count":175},"Автозапчасти":{"count":209},"Renault":{"count":257},"Pit Stop":{"count":72},"Garage Renault":{"count":57},"Jiffy Lube":{"count":363},"СТО":{"count":820},"Автосервис":{"count":668},"O'Reilly Auto Parts":{"count":194},"Pep Boys":{"count":68},"Carglass":{"count":181},"ÖAMTC":{"count":53},"шиномонтаж":{"count":136},"Citroen":{"count":93},"Euromaster":{"count":133},"NAPA Auto Parts":{"count":83},"Mr. Lube":{"count":52},"Valvoline Instant Oil Change":{"count":74},"Wulkanizacja":{"count":64},"Firestone":{"count":176},"Goodyear":{"count":75},"Roady":{"count":75},"Mekonomen":{"count":51},"Автомастерская":{"count":74},"Advance Auto Parts":{"count":238},"Замена масла":{"count":74},"Gomeria":{"count":110},"Firestone Complete Auto Care":{"count":52},"Автосервис+шиномонтаж":{"count":55},"Gomería":{"count":65}},"bakery":{"Red Ribbon":{"count":53},"Kamps":{"count":263},"Lila Bäcker":{"count":83},"Bäckerei Schmidt":{"count":95},"Anker":{"count":83},"Schäfer":{"count":59},"Schäfer's":{"count":80},"Sternenbäck":{"count":74},"Hofpfisterei":{"count":125},"Bakers Delight":{"count":62},"Greggs":{"count":481},"Panaderia":{"count":367},"Ströck":{"count":62},"Oebel":{"count":65},"Sehne":{"count":76},"Boulangerie":{"count":454},"Stadtbäckerei":{"count":60},"Banette":{"count":89},"Steinecke":{"count":234},"Goldilocks":{"count":106},"Backwerk":{"count":130},"Dat Backhus":{"count":77},"K&U":{"count":98},"Ihle":{"count":100},"Der Beck":{"count":102},"Ditsch":{"count":60},"Bäckerei Fuchs":{"count":59},"Bakker Bart":{"count":80},"Bäckerei Müller":{"count":66},"Backshop":{"count":51},"Wiener Feinbäcker":{"count":58},"Bäckerei Grimminger":{"count":52},"Goeken backen":{"count":53},"von Allwörden":{"count":52},"Paul":{"count":150},"Musmanni":{"count":73},"Хлеб":{"count":136},"Fornetti":{"count":110},"Le Fournil":{"count":51},"Булочная":{"count":56},"boulangerie":{"count":65},"Пекарня":{"count":187},"Mlinar":{"count":71},"Свежий хлеб":{"count":59},"Marie Blachère":{"count":53},"Cooplands":{"count":57},"Кулиничи":{"count":101}},"furniture":{"IKEA":{"count":215},"Dänisches Bettenlager":{"count":424},"Jysk":{"count":209},"Roller":{"count":89},"Conforama":{"count":141},"The Brick":{"count":51},"Мебель":{"count":327},"Casa":{"count":57},"JYSK":{"count":97},"But":{"count":115},"Pier 1 Imports":{"count":81},"ニトリ":{"count":68},"Black Red White":{"count":61}},"doityourself":{"Hornbach":{"count":129},"B&Q":{"count":235},"Hubo":{"count":93},"Mr Bricolage":{"count":133},"Gamma":{"count":122},"OBI":{"count":498},"Lowe's":{"count":1228},"Wickes":{"count":148},"Hagebau":{"count":53},"Castorama":{"count":161},"Rona":{"count":69},"Toom Baumarkt":{"count":95},"Homebase":{"count":209},"Lagerhaus":{"count":108},"Bauhaus":{"count":210},"Leroy Merlin":{"count":255},"Hellweg":{"count":65},"Brico":{"count":125},"Bricomarché":{"count":381},"Hagebaumarkt":{"count":128},"Biltema":{"count":52},"BayWa":{"count":53},"Home Depot":{"count":1250},"Menards":{"count":103},"Weldom":{"count":101},"Toom":{"count":63},"Praktiker":{"count":62},"Canadian Tire":{"count":125},"Easy":{"count":51},"Sonderpreis Baumarkt":{"count":52},"Ace Hardware":{"count":246},"Screwfix":{"count":62},"Karwei":{"count":59},"Mr. Bricolage":{"count":56},"Строитель":{"count":57},"Home Hardware":{"count":144},"Мастер":{"count":53},"Хозтовары":{"count":128},"コメリ":{"count":108},"Bunnings Warehouse":{"count":167},"Bricorama":{"count":103},"Стройматериалы":{"count":401},"コーナン":{"count":67},"Point P":{"count":102}},"stationery":{"Staples":{"count":580},"McPaper":{"count":136},"Ryman":{"count":74},"Pagro":{"count":64},"Officeworks":{"count":59},"Office Depot":{"count":295},"Libro":{"count":64},"Office Max":{"count":138},"Канцтовары":{"count":110}},"car":{"Skoda":{"count":122},"BMW":{"count":189},"KIA":{"count":74},"Citroen":{"count":395},"Renault":{"count":560},"Mercedes-Benz":{"count":335},"Volvo":{"count":146},"Ford":{"count":382},"Kia Motors":{"count":97},"Mazda":{"count":164},"Mitsubishi":{"count":120},"Fiat":{"count":130},"Автозапчасти":{"count":243},"Opel":{"count":206},"Volkswagen":{"count":318},"Toyota":{"count":467},"Nissan":{"count":330},"Seat":{"count":84},"Porsche":{"count":72},"Suzuki":{"count":127},"Honda":{"count":271},"Peugeot":{"count":461},"Lexus":{"count":66},"KIA Motors":{"count":54},"Hyundai":{"count":330},"Kia":{"count":108},"Audi":{"count":158},"Автомагазин":{"count":92},"Subaru":{"count":92},"Chevrolet":{"count":178}},"kiosk":{"Shell":{"count":119},"R-kioski":{"count":147},"Aral":{"count":73},"Kiosk":{"count":774},"R-Kioski":{"count":198},"Narvesen":{"count":177},"Tabak Trafik":{"count":65},"Trafik":{"count":235},"Pressbyrån":{"count":104},"Trinkhalle":{"count":96},"Relay":{"count":54},"Kiosco":{"count":164},"Edicola":{"count":68},"Пресса":{"count":130},"Trafika":{"count":64},"Роспечать":{"count":218},"Ruch":{"count":146},"Продукты":{"count":216},"Tisak":{"count":218},"Киоск":{"count":137},"Kolporter":{"count":70},"Мороженое":{"count":55},"Kiosko":{"count":63},"Kios":{"count":191},"Lietuvos spauda":{"count":59},"Союзпечать":{"count":87},"Pulpería":{"count":59},"მარკეტი (Market)":{"count":138},"KIOS":{"count":84},"Warung":{"count":68}},"electronics":{"Maplin":{"count":102},"Media Markt":{"count":378},"Saturn":{"count":147},"Currys":{"count":116},"Euronics":{"count":203},"Rogers":{"count":59},"Radio Shack":{"count":461},"Apple Store":{"count":56},"Best Buy":{"count":609},"Elgiganten":{"count":52},"Expert":{"count":173},"Эльдорадо":{"count":265},"DNS":{"count":74},"Hartlauer":{"count":52},"Darty":{"count":130},"Interdiscount":{"count":52},"М.Видео":{"count":106},"Bell":{"count":76},"Samsung":{"count":99},"Фокстрот":{"count":70},"ケーズデンキ":{"count":111},"The Source":{"count":67},"Boulanger":{"count":65},"Batteries Plus Bulbs":{"count":67},"Эксперт":{"count":61},"Media Expert":{"count":119},"ヤマダ電機":{"count":129},"エディオン":{"count":62},"Neonet":{"count":73},"La Curacao":{"count":63}},"variety_store":{"Tedi":{"count":575},"Action":{"count":85},"Dollar Tree":{"count":487},"Dollarama":{"count":295},"ダイソー":{"count":152},"Fix Price":{"count":92},"Family Dollar":{"count":405},"GiFi":{"count":92},"Dollar General":{"count":233},"Big Lots":{"count":54},"Poundland":{"count":132},"Variety Store":{"count":143}},"alcohol":{"Alko":{"count":169},"Beer Store":{"count":54},"LCBO":{"count":371},"The Beer Store":{"count":213},"Systembolaget":{"count":259},"Норман":{"count":120},"Ароматный мир":{"count":130},"Bargain Booze":{"count":108},"BWS":{"count":124},"Vinmonopolet":{"count":60},"Botilleria":{"count":87},"SAQ":{"count":118},"Gall & Gall":{"count":505},"Liquorland":{"count":86},"Mitra":{"count":60},"Nicolas":{"count":180},"Живое пиво":{"count":158},"Dan Murphy's":{"count":54},"Красное и белое":{"count":88},"BC Liquor Store":{"count":52},"Красное & Белое":{"count":168},"Liquor Store":{"count":54},"Бристоль":{"count":152},"Красное и Белое":{"count":57},"Разливное пиво":{"count":96},"Красное&Белое":{"count":78},"Пиво":{"count":53}},"bicycle":{"Halfords":{"count":156},"サイクルベースあさひ":{"count":64},"Веломарка":{"count":52}},"clothes":{"KiK":{"count":1684},"H&M":{"count":1182},"C&A":{"count":764},"Second Hand":{"count":129},"Urban Outfitters":{"count":107},"Vögele":{"count":182},"Zeeman":{"count":272},"Takko":{"count":761},"Adler":{"count":75},"Charles Vögele":{"count":114},"Nike":{"count":80},"Peek & Cloppenburg":{"count":68},"OVS":{"count":59},"Zara":{"count":413},"Vero Moda":{"count":187},"Ernsting's family":{"count":631},"Winners":{"count":93},"NKD":{"count":690},"GAP":{"count":63},"River Island":{"count":105},"Next":{"count":262},"Monsoon":{"count":72},"Gap":{"count":147},"Adidas":{"count":176},"Gerry Weber":{"count":183},"Woolworths":{"count":119},"Ulla Popken":{"count":105},"Mr Price":{"count":93},"Jet":{"count":65},"Pep":{"count":134},"Edgars":{"count":113},"Ackermans":{"count":93},"House":{"count":53},"Truworths":{"count":66},"Reserved":{"count":99},"Ross":{"count":215},"Hugo Boss":{"count":82},"Burton":{"count":78},"Dorothy Perkins":{"count":77},"s.Oliver":{"count":89},"Cecil":{"count":103},"Lane Bryant":{"count":66},"Dress Barn":{"count":97},"Old Navy":{"count":303},"Dressmann":{"count":56},"Jack & Jones":{"count":142},"Hunkemöller":{"count":129},"Pimkie":{"count":141},"Esprit":{"count":359},"Primark":{"count":150},"Bonita":{"count":277},"Mexx":{"count":76},"Matalan":{"count":127},"Tally Weijl":{"count":118},"Mango":{"count":277},"TK Maxx":{"count":157},"Springfield":{"count":69},"Sisley":{"count":68},"Benetton":{"count":164},"AWG":{"count":83},"Tommy Hilfiger":{"count":149},"Wibra":{"count":59},"Fat Face":{"count":72},"Reitmans":{"count":51},"Lindex":{"count":99},"New Look":{"count":230},"KappAhl":{"count":64},"New Yorker":{"count":301},"Etam":{"count":91},"Kiabi":{"count":234},"Palmers":{"count":68},"Jack Wolfskin":{"count":69},"American Apparel":{"count":86},"Desigual":{"count":135},"Cubus":{"count":54},"Gant":{"count":65},"Tom Tailor":{"count":101},"Topshop":{"count":58},"Bershka":{"count":117},"Burlington Coat Factory":{"count":76},"Jules":{"count":95},"Guess":{"count":100},"Jennyfer":{"count":60},"Pepco":{"count":88},"Orsay":{"count":139},"Tezenis":{"count":64},"Forever 21":{"count":84},"Mark's":{"count":55},"Triumph":{"count":100},"Timberland":{"count":66},"Superdry":{"count":62},"AOKI":{"count":89},"Calzedonia":{"count":186},"Intimissimi":{"count":119},"Orchestra":{"count":79},"United Colors of Benetton":{"count":159},"Goodwill":{"count":56},"洋服の青山":{"count":199},"Levi's":{"count":152},"Petit Bateau":{"count":55},"Diesel":{"count":66},"American Eagle Outfitters":{"count":62},"Celio":{"count":140},"Camaïeu":{"count":98},"Puma":{"count":55},"Lacoste":{"count":156},"WE":{"count":62},"Men's Wearhouse":{"count":112},"Sergent Major":{"count":63},"Jeans Fritz":{"count":92},"Ross Dress for Less":{"count":52},"Promod":{"count":159},"Street One":{"count":129},"TJ Maxx":{"count":171},"ユニクロ":{"count":168},"Engbers":{"count":62},"Only":{"count":62},"Marc O'Polo":{"count":61},"Banana Republic":{"count":102},"Одежда":{"count":124},"Marshalls":{"count":161},"La Halle":{"count":113},"Peacocks":{"count":144},"Camaieu":{"count":68},"Justice":{"count":59},"Massimo Dutti":{"count":76},"Cache Cache":{"count":53},"Женская одежда":{"count":51},"Victoria's Secret":{"count":90},"Спецодежда":{"count":65},"西松屋":{"count":84},"Calvin Klein":{"count":56},"Stradivarius":{"count":66},"Stefanel":{"count":51},"Gina Laura":{"count":57},"Chico's":{"count":74},"Caroll":{"count":61},"しまむら":{"count":161},"Gémo":{"count":71},"ワークマン":{"count":51},"Mim":{"count":52},"Cropp":{"count":52},"Toko Pakaian":{"count":72}},"bookmaker":{"Paddy Power":{"count":102},"Ladbrokes":{"count":497},"Betfred":{"count":229},"William Hill":{"count":518},"Coral":{"count":352}},"video_games":{"GameStop":{"count":372},"Game Stop":{"count":80},"EB Games":{"count":86},"Micromania":{"count":67},"Gamestop":{"count":56},"Game":{"count":53}},"beverages":{"REWE Getränkemarkt":{"count":131},"Getränke Hoffmann":{"count":164},"Orterer Getränkemarkt":{"count":65},"Rewe Getränkemarkt":{"count":110},"Getränkemarkt":{"count":104},"Fristo":{"count":85},"Trinkgut":{"count":58},"Dursty":{"count":71},"Getränkeland":{"count":71},"Edeka Getränkemarkt":{"count":62},"trinkgut":{"count":52}},"pet":{"Fressnapf":{"count":555},"PetSmart":{"count":398},"Pet Valu":{"count":84},"Das Futterhaus":{"count":128},"Pets at Home":{"count":133},"Petco":{"count":281},"Зоотовары":{"count":63}},"butcher":{"Boucherie":{"count":103},"Carniceria":{"count":69},"Мясо":{"count":131},"Великолукский мясокомбинат":{"count":160},"Мясная лавка":{"count":165},"Macelleria":{"count":68},"Ариант":{"count":69},"Свежее мясо":{"count":82}},"tobacco":{"Estanco":{"count":76},"Tabacchi":{"count":53},"Табак":{"count":58},"Nemzeti Dohánybolt":{"count":386},"Nemzeti dohánybolt":{"count":134}},"laundry":{"Laundrette":{"count":52},"Coin Laundry":{"count":71},"Laverie":{"count":96},"Laundromat":{"count":122},"Lavanderia":{"count":76},"Laundry":{"count":84}},"optician":{"Boots Opticians":{"count":68},"Fielmann":{"count":375},"Pearle":{"count":120},"Apollo":{"count":377},"Hakim Optical":{"count":62},"メガネスーパー":{"count":51},"Vision Express":{"count":146},"Générale d'Optique":{"count":66},"Optic 2000":{"count":225},"Alain Afflelou":{"count":153},"Specsavers":{"count":287},"Krys":{"count":144},"Optical Center":{"count":90},"Atol":{"count":101},"Les Opticiens Mutualistes":{"count":76},"Hans Anders":{"count":63},"Optika":{"count":51},"眼鏡市場":{"count":176},"แว่นท็อปเจริญ":{"count":57}},"hardware":{"Lowe's":{"count":81},"1000 мелочей":{"count":87},"Стройматериалы":{"count":87},"Home Hardware":{"count":67},"Хозтовары":{"count":336},"Ferretería":{"count":78},"Hardware":{"count":63},"Quincaillerie":{"count":66},"Сантехника":{"count":52},"Ferreteria":{"count":95}},"shoes":{"Deichmann":{"count":1083},"Reno":{"count":231},"Payless ShoeSource":{"count":91},"Ecco":{"count":131},"Clarks":{"count":221},"La Halle aux Chaussures":{"count":120},"東京靴流通センター":{"count":56},"Brantano":{"count":130},"Tamaris":{"count":73},"Geox":{"count":113},"Salamander":{"count":88},"Payless Shoe Source":{"count":255},"Famous Footwear":{"count":132},"Quick Schuh":{"count":109},"Foot Locker":{"count":192},"Shoe Zone":{"count":135},"Siemes Schuhcenter":{"count":62},"Юничел":{"count":54},"Ремонт обуви":{"count":51},"Rieker":{"count":55},"Aldo":{"count":59},"Bata":{"count":200},"Eram":{"count":73},"CCC":{"count":169},"Scapino":{"count":55},"ЦентрОбувь":{"count":73},"Chaussea":{"count":74}},"toys":{"La Grande Récré":{"count":82},"Toys R Us":{"count":358,"tags":{"shop":"toys"}},"Intertoys":{"count":108},"Bart Smit":{"count":80},"Детский мир":{"count":151},"Maxi Toys":{"count":51},"Игрушки":{"count":76},"King Jouet":{"count":78},"Dráčik":{"count":51}},"travel_agency":{"Flight Centre":{"count":130},"Thomson":{"count":106},"Thomas Cook":{"count":236},"First Reisebüro":{"count":52},"TUI":{"count":187},"The Co-operative Travel":{"count":51}},"jewelry":{"585":{"count":77},"Bijou Brigitte":{"count":143},"Christ":{"count":98},"Claire's":{"count":69},"Swarovski":{"count":174},"Pandora":{"count":177},"Адамас":{"count":54},"Kay Jewelers":{"count":59}},"carpet":{"Carpet Right":{"count":90}},"energy":{"Punto Enel":{"count":1007}},"beauty":{"Yves Rocher":{"count":708},"Nails":{"count":60},"Sally Beauty Supply":{"count":101}},"car_parts":{"Halfords":{"count":84},"Автозапчасти":{"count":576},"Автомагазин":{"count":75},"AutoZone":{"count":481},"O'Reilly Auto Parts":{"count":222},"NAPA Auto Parts":{"count":185},"Advance Auto Parts":{"count":210},"タイヤ館":{"count":76},"Repco":{"count":58},"オートバックス":{"count":65},"イエローハット":{"count":57},"Tokić":{"count":61},"repuestos automotrices":{"count":61}},"video":{"World of Video":{"count":59},"Family Video":{"count":72},"Blockbuster":{"count":90},"TSUTAYA":{"count":94},"ゲオ":{"count":58}},"charity":{"British Heart Foundation":{"count":137},"Cancer Research UK":{"count":101},"Oxfam":{"count":169},"Scope":{"count":55},"Age UK":{"count":91},"Goodwill":{"count":77},"Sue Ryder":{"count":57}},"florist":{"Monceau Fleurs":{"count":62},"Blumenladen":{"count":69},"Interflora":{"count":66},"Blume 2000":{"count":86},"Цветы":{"count":1002},"Blumen":{"count":74},"Blumen Risse":{"count":64},"상록식물원 (Sangnok Florist)":{"count":57},"Florărie":{"count":53},"Kwiaciarnia":{"count":177},"Virágbolt":{"count":56}},"tailor":{"Änderungsschneiderei":{"count":171},"Ателье":{"count":70}},"hearing_aids":{"Kind Hörgeräte":{"count":61},"Amplifon":{"count":84}},"hairdresser":{"Klier":{"count":218},"Klipp":{"count":64},"Toni & Guy":{"count":63},"Haarscharf":{"count":56},"Supercuts":{"count":276},"Hairkiller":{"count":73},"Great Clips":{"count":426},"Парикмахерская":{"count":719},"Haarmonie":{"count":74},"Top Hair":{"count":64},"Figaro":{"count":60},"Перукарня":{"count":81},"Tchip":{"count":53},"Jean Louis David":{"count":76},"The Barber Shop":{"count":81},"Стиль":{"count":74},"Sport Clips":{"count":88},"Franck Provost":{"count":120},"Hair Cuttery":{"count":92},"Cost Cutters":{"count":56},"Coiffure":{"count":61},"Шарм":{"count":60},"Салон красоты":{"count":80},"Peluquería":{"count":58}},"bed":{"Matratzen Concord":{"count":324},"Dänisches Bettenlager":{"count":129},"Mattress Firm":{"count":102}},"copyshop":{"FedEx Office Print and Ship Center":{"count":166}},"erotic":{"Orion":{"count":76}},"money_lender":{"Money Mart":{"count":79}},"cosmetics":{"The Body Shop":{"count":59},"Yves Rocher":{"count":73},"Sephora":{"count":59},"Л'Этуаль":{"count":66}},"confectionery":{"Thorntons":{"count":57},"Hussel":{"count":71},"Leonidas":{"count":75},"Fagyizó":{"count":59}},"music":{"HMV":{"count":79}},"motorcycle":{"Harley Davidson":{"count":58},"Yamaha":{"count":171},"Honda":{"count":174},"Suzuki":{"count":80}},"newsagent":{"Relay":{"count":212},"WH Smith":{"count":53},"Maison de la Presse":{"count":103},"WHSmith":{"count":72},"Печать":{"count":92},"Tabac Presse":{"count":58},"Роспечать":{"count":306},"Союзпечать":{"count":120},"Edicola":{"count":94},"Витебскоблсоюзпечать":{"count":52},"Пресса":{"count":73}},"lottery":{"Lotto":{"count":113},"ONCE":{"count":68},"Lottózó":{"count":60}},"coffee":{"Tchibo":{"count":155},"Starbucks":{"count":164,"tags":{"cuisine":"coffee_shop"}},"Café Amazon":{"count":219}},"garden_centre":{"Dehner":{"count":56},"Jardiland":{"count":114},"Gamm Vert":{"count":164},"Point Vert":{"count":55}},"tyres":{"Шиномонтаж":{"count":349},"Discount Tire":{"count":67},"Euromaster":{"count":60},"Borracharia":{"count":55},"Вулканизация":{"count":67}},"baby_goods":{"Babies R Us":{"count":67}},"craft":{"Michael's":{"count":74},"Michaels":{"count":79},"Hobby Lobby":{"count":57}},"outdoor":{"REI":{"count":66},"Mountain Warehouse":{"count":59},"Рыболов":{"count":62}},"houseware":{"Blokker":{"count":212},"Xenos":{"count":67},"Marskramer":{"count":58}},"computer":{"PC World":{"count":63},"ДНС":{"count":51},"DNS":{"count":201},"Apple Store":{"count":70}},"interior_decoration":{"Depot":{"count":66}},"second_hand":{"Goodwill":{"count":171}},"pawnbroker":{"Cash Converters":{"count":64},"Ломбард":{"count":177}},"hifi":{},"farm":{"Hofladen":{"count":51}},"dry_cleaning":{"Химчистка":{"count":55},"Диана":{"count":68},"Cleaners":{"count":80},"ホワイト急便":{"count":81}},"paint":{"Sherwin-Williams":{"count":107},"Comex":{"count":51},"Sherwin Williams":{"count":111}},"funeral_directors":{"Ритуальные услуги":{"count":114},"The Co-operative Funeralcare":{"count":53}},"gift":{"Hallmark":{"count":119},"Сувениры":{"count":60},"Card Factory":{"count":70},"Gift Shop":{"count":53}},"ticket":{"Проездные билеты":{"count":54},"Boutique Grandes Lignes":{"count":63},"Guichet Transilien":{"count":245}},"greengrocer":{"Овощи-фрукты":{"count":98},"Zöldséges":{"count":52}},"ice_cream":{"Мороженое":{"count":67}},"fabric":{"Ткани":{"count":105}},"frame":{"rumah penduduk":{"count":175}},"kitchen":{"Home Utensils":{"count":65}}},"tourism":{"hotel":{"Holiday Inn":{"count":429},"Hotel Ibis":{"count":67},"Novotel":{"count":172},"Crowne Plaza":{"count":86},"Premier Inn":{"count":362},"Holiday Inn Express":{"count":472},"Park Hotel":{"count":75},"Krone":{"count":66},"Ibis":{"count":203},"Ibis Budget":{"count":178},"Travelodge":{"count":280},"City Hotel":{"count":69},"Hotel Royal":{"count":54},"Campanile":{"count":137},"Comfort Inn":{"count":273},"Quality Inn & Suites":{"count":57},"Courtyard by Marriott":{"count":74},"Mercure":{"count":112},"Hotel Europa":{"count":74},"Formule 1":{"count":70},"Grand Hotel":{"count":83},"Hilton":{"count":56},"Courtyard Marriott":{"count":69},"Hotel Krone":{"count":56},"Ramada":{"count":103},"Best Western":{"count":231},"Quality Inn":{"count":166},"Days Inn":{"count":246},"Royal Hotel":{"count":81},"Marriott":{"count":66},"Première Classe":{"count":53},"Hotel Central":{"count":78},"Hotel Victoria":{"count":52},"東横イン":{"count":53},"Kyriad":{"count":60},"Parkhotel":{"count":65},"Extended Stay America":{"count":90},"Hotel zur Post":{"count":57},"Fairfield Inn":{"count":60},"Hotel Post":{"count":56},"Staybridge Suites":{"count":51},"Hotel Panorama":{"count":51},"Hampton Inn & Suites":{"count":84},"Hotel":{"count":270},"Sheraton":{"count":65},"Hôtel Ibis":{"count":63},"Sleep Inn":{"count":59},"Country Inn & Suites":{"count":82},"Comfort Suites":{"count":143},"Super 8":{"count":223},"Гостиница":{"count":120},"Residence Inn":{"count":83},"Homewood Suites":{"count":57},"Hilton Garden Inn":{"count":178},"Hôtel de France":{"count":53},"Hampton Inn":{"count":380},"Embassy Suites":{"count":65},"Econo Lodge":{"count":66},"Comfort Inn & Suites":{"count":59},"Motel 6":{"count":85},"La Quinta":{"count":57},"Holiday Inn Express & Suites":{"count":56},"Fairfield Inn & Suites":{"count":63}},"motel":{"Travelodge":{"count":68},"Motel":{"count":72},"Econo Lodge":{"count":81},"Super 8":{"count":162},"Days Inn":{"count":100},"Motel 6":{"count":190},"Rodeway Inn":{"count":77},"Comfort Inn":{"count":128},"Best Western":{"count":57},"Quality Inn":{"count":91},"Holiday Inn Express":{"count":52},"Budget Inn":{"count":60}},"museum":{"Heimatmuseum":{"count":336},"Stadtmuseum":{"count":84},"Краеведческий музей":{"count":210},"Музей":{"count":83},"Tájház":{"count":89}},"attraction":{"Kursächsische Postmeilensäule":{"count":54},"Lavoir":{"count":78},"Sommerrodelbahn":{"count":60},"Часовня":{"count":66},"Колесо обозрения":{"count":54},"дольмен":{"count":112},"Усадьба":{"count":52},"Кладбище еврейское":{"count":91},"Приусадебный парк":{"count":69},"Хозяйственный двор":{"count":72},"OWŚ":{"count":106}},"camp_site":{"Camping":{"count":173},"Camping Municipal":{"count":184},"Camping municipal":{"count":77},"Campsite":{"count":59}},"chalet":{"Ferienwohnung":{"count":55}},"hostel":{"Общежитие":{"count":58}},"alpine_hut":{"КОШ":{"count":77}},"caravan_site":{"мк с Освещением ":{"count":97},"жб с Освещением ":{"count":65}}},"leisure":{"playground":{"Playground":{"count":569},"Abenteuerspielplatz":{"count":149},"Kinderspielplatz":{"count":268},"Waldspielplatz":{"count":170},"Spielplatz":{"count":264},"Bolzplatz":{"count":127},"Wasserspielplatz":{"count":148},"Játszótér":{"count":114},"Parque Infantil":{"count":385},"Parque":{"count":59},"Parque infantil":{"count":79},"Aire de Jeux":{"count":78},"Plac zabaw":{"count":255},"Dětské hřiště":{"count":77},"Детская площадка":{"count":200},"playground":{"count":118},"Tot Lot":{"count":121},"Jeux Enfants":{"count":51},"Parco giochi":{"count":58},"Zona Infantil":{"count":57}},"sports_centre":{"Kieser Training":{"count":95},"Turnhalle":{"count":612},"Virgin Active":{"count":69},"Planet Fitness":{"count":104},"Sporthalle":{"count":1056},"Schulsporthalle":{"count":85},"Palestra":{"count":74},"Sportplatz":{"count":100},"Schützenhaus":{"count":77},"Fitness First":{"count":67},"LA Fitness":{"count":106},"Reithalle":{"count":104},"McFit":{"count":59},"Спортзал":{"count":94},"Mrs. Sporty":{"count":75},"Tennishalle":{"count":164},"YMCA":{"count":166},"Hallenbad":{"count":79},"Palazzetto dello Sport":{"count":114},"24 Hour Fitness":{"count":52},"Mehrzweckhalle":{"count":152},"Gold's Gym":{"count":79},"Polideportivo":{"count":200},"Curves":{"count":88},"Tennis":{"count":109},"ДЮСШ":{"count":72},"Gymnase":{"count":360},"Salle Polyvalente":{"count":59},"ФОК":{"count":81},"Anytime Fitness":{"count":126},"Piscina Municipal":{"count":54},"Gym":{"count":80},"Hala sportowa":{"count":61},"Orlik":{"count":70},"Спорткомплекс":{"count":52},"Palestra Comunale":{"count":65},"Gymnasium":{"count":55},"Gimnasio":{"count":144},"Gimnasio Municipal":{"count":75},"Polideportivo Municipal":{"count":185},"Life Time Fitness":{"count":78},"Pabellón de Deportes":{"count":82},"Pabellón Municipal de Deportes":{"count":85}},"swimming_pool":{"Piscina municipal":{"count":77},"Piscine municipale":{"count":102},"Pool":{"count":170},"Piscine":{"count":97},"Piscina Comunale":{"count":59},"Swimming Pool":{"count":88},"Бассейн":{"count":62},"Piscina Municipal":{"count":178},"Piscina":{"count":145},"Nichtschwimmerbecken":{"count":57},"Sportbecken":{"count":54},"Kinderbecken":{"count":94},"piscina":{"count":62}}},"man_made":{"windmill":{"De Hoop":{"count":57}}}},"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"},"driveLeft":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[113.573,22.186],[114,22.5],[114.05534,22.503],[114.0595,22.51422],[114.07345,22.51934],[114.0786,22.53012],[114.08512,22.53228],[114.09034,22.53717],[114.09611,22.53486],[114.10602,22.53472],[114.1115,22.52929],[114.11522,22.5308],[114.11649,22.53418],[114.11979,22.53511],[114.1244,22.5393],[114.12729,22.53947],[114.13018,22.54132],[114.13807,22.5432],[114.14429,22.54176],[114.14441,22.54114],[114.14846,22.54179],[114.14855,22.54397],[114.15042,22.54593],[114.14964,22.54954],[114.15166,22.55168],[114.15192,22.55438],[114.15642,22.55495],[114.15841,22.55909],[114.16032,22.56153],[114.16162,22.56187],[114.1634,22.55931],[114.16553,22.5593],[114.16733,22.56119],[114.16934,22.56093],[114.17061,22.5597],[114.17702,22.56],[114.17798,22.55546],[114.18401,22.5552],[114.18673,22.55468],[114.1878,22.55545],[114.18979,22.55446],[114.1955,22.55564],[114.19674,22.55691],[114.20005,22.55717],[114.20376,22.55623],[114.20884,22.55672],[114.22438,22.55055],[114.2264,22.5476],[114.22545,22.54528],[114.22661,22.54339],[114.32,22.576],[114.45,22.43],[114.45,22.14],[113.9,22.14],[113.75,22.22],[113.553,22.095],[113.54678,22.1207],[113.5456,22.1207],[113.54076,22.12373],[113.54016,22.13618],[113.54701,22.13651],[113.5475,22.14583],[113.52456,22.17978],[113.53623,22.20191],[113.53299,22.21232],[113.53497,22.21351],[113.53735,22.21377],[113.54156,22.21315],[113.54385,22.21709],[113.55849,22.21565],[113.573,22.186]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-75,18],[-80,20.5],[-83,19],[-77,16],[-75,18]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-64.33594,32.86113],[-79.39819,27.21067],[-80.63965,23.71998],[-73.39966,20.40642],[-65.1709,19.20743],[-65.09399,16.23577],[-59.13391,16.80454],[-62.64404,15.16228],[-59.43054,14.85985],[-62.83081,13.64599],[-61.80359,10.73752],[-62.08649,10.04729],[-61.06201,9.85522],[-59.81369,8.31274],[-59.8027,8.27469],[-59.83498,8.22712],[-59.94141,8.21149],[-59.99771,8.15576],[-59.97986,8.13265],[-59.99771,8.12041],[-60.00183,8.07147],[-60.05127,8.02524],[-60.09933,8.03747],[-60.12268,8.02388],[-60.14053,7.98988],[-60.36163,7.83345],[-60.53467,7.81713],[-60.5896,7.6375],[-60.72144,7.54493],[-60.5896,7.31888],[-60.63904,7.24532],[-60.54703,7.12542],[-60.46875,7.20309],[-60.37262,7.18401],[-60.29984,7.1445],[-60.2916,7.06819],[-60.39871,6.95097],[-60.66513,6.83235],[-60.71869,6.75053],[-60.91232,6.81735],[-60.94254,6.72053],[-61.14441,6.72326],[-61.23093,6.5773],[-61.1554,6.45314],[-61.14441,6.20199],[-61.39709,5.95619],[-60.71045,5.20036],[-60.21606,5.23319],[-59.99634,5.06906],[-60.13916,4.51071],[-59.69971,4.40118],[-59.5459,3.93002],[-59.87549,3.56825],[-59.7876,3.37086],[-60.01831,2.83332],[-59.90845,2.38335],[-59.69971,2.2626],[-59.77661,1.87833],[-59.65302,1.85087],[-59.69147,1.75754],[-59.61456,1.71361],[-59.55139,1.73283],[-59.36188,1.49123],[-59.26575,1.39238],[-58.92242,1.30726],[-58.83728,1.17271],[-58.71918,1.23037],[-58.71094,1.29902],[-58.49121,1.26058],[-58.461,1.37591],[-58.50494,1.38689],[-58.51044,1.46102],[-58.38135,1.4775],[-58.32642,1.57359],[-58.00507,1.49946],[-57.99133,1.65321],[-57.79907,1.69165],[-57.70844,1.71087],[-57.54364,1.68341],[-57.41455,1.94421],[-57.10693,1.97715],[-56.8103,1.85636],[-56.48071,1.92225],[-55.90942,1.81244],[-55.90942,2.04302],[-56.14014,2.26534],[-55.94788,2.53701],[-55.70892,2.39981],[-55.37933,2.43274],[-55.19257,2.53976],[-54.98108,2.57268],[-54.88495,2.43548],[-54.71191,2.46293],[-54.69543,2.34767],[-54.58832,2.32846],[-54.43451,2.43548],[-54.20654,2.76748],[-54.17358,3.12955],[-53.96484,3.57921],[-54.33838,4.00674],[-54.44412,4.52577],[-54.46884,4.91036],[-54.36653,5.13061],[-54.27727,5.26191],[-54.19968,5.3084],[-54.01222,5.54457],[-54.0239,5.64605],[-53.86322,5.94936],[-64.33594,32.86113]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-14.5,-6],[11.79657,-17.27197],[12.03415,-17.14866],[12.38159,-17.22213],[12.58484,-17.22476],[13.01193,-16.97405],[13.24951,-17.00098],[13.38135,-16.98522],[13.93341,-17.38734],[18.42476,-17.38996],[18.47763,-17.46857],[18.62663,-17.64599],[18.79211,-17.76177],[18.92944,-17.82061],[19.33044,-17.84806],[19.66278,-17.8644],[19.74792,-17.90557],[19.85161,-17.87486],[20.10361,-17.90296],[20.18394,-17.88401],[20.34737,-17.88466],[20.44693,-17.91733],[20.51697,-17.96698],[20.83008,-18.03032],[20.95711,-17.97285],[21.16997,-17.93497],[21.42677,-18.0264],[23.45032,-17.63879],[22.00012,-16.38866],[21.99944,-13.00523],[24.03809,-12.99118],[24.03809,-10.91962],[24.43359,-11.09217],[24.45557,-11.48002],[25.37842,-11.19996],[25.42236,-11.60919],[26.96045,-11.97484],[27.18018,-11.60919],[28.125,-12.42048],[29.11377,-13.36824],[29.1687,-13.43771],[29.55872,-13.19716],[29.68506,-13.2239],[29.62463,-13.41099],[29.80591,-13.44305],[29.81415,-12.14809],[29.31152,-12.55456],[28.41064,-11.78133],[28.63037,-10.70379],[28.65234,-9.73071],[28.37219,-9.24309],[28.89748,-8.47916],[30.78644,-8.26857],[29.39941,-6.05316],[29.4873,-4.45595],[29.75922,-4.46759],[29.81415,-4.36421],[29.88007,-4.36832],[30.04074,-4.26699],[30.07919,-4.1629],[30.18356,-4.08311],[30.1918,-4.05126],[30.21566,-4.04595],[30.22923,-4.01136],[30.21326,-3.99612],[30.25978,-3.88755],[30.29274,-3.86288],[30.34424,-3.77245],[30.39848,-3.79095],[30.40878,-3.76765],[30.39548,-3.7304],[30.39054,-3.72821],[30.3896,-3.71918],[30.39093,-3.7101],[30.39514,-3.70444],[30.42028,-3.64963],[30.46886,-3.53501],[30.67108,-3.41335],[30.63297,-3.34892],[30.84206,-3.25535],[30.84549,-3.16108],[30.83485,-3.09698],[30.7933,-3.06235],[30.82111,-3.02258],[30.84515,-2.9739],[30.74764,-2.99618],[30.7037,-2.97013],[30.66422,-2.98967],[30.57632,-2.90738],[30.49393,-2.94441],[30.41016,-2.87172],[30.52002,-2.39432],[30.77545,-2.38883],[30.8606,-2.31199],[30.84961,-2.19398],[30.89081,-2.07322],[30.81116,-1.96068],[30.83862,-1.6587],[30.73425,-1.4418],[30.56259,-1.33884],[30.4541,-1.05737],[30.35797,-1.06287],[30.34149,-1.13152],[30.16571,-1.34296],[29.91852,-1.48024],[29.83887,-1.31824],[29.58344,-1.39238],[29.729,0.05493],[29.96796,0.5136],[29.9707,0.8569],[30.22339,0.92281],[30.24536,1.15349],[30.47745,1.20772],[31.30966,2.15693],[31.20255,2.22211],[31.20255,2.29278],[31.16409,2.27906],[31.13937,2.28318],[31.13113,2.26534],[31.07826,2.30033],[31.0714,2.34767],[31.00479,2.4005],[30.97183,2.40461],[30.94711,2.38746],[30.94849,2.36276],[30.9375,2.33532],[30.88531,2.34012],[30.83038,2.42176],[30.74112,2.43274],[30.76035,2.5864],[30.90179,2.88132],[30.76447,3.04178],[30.93613,3.40239],[30.94059,3.50588],[30.85236,3.48601],[30.90866,3.5936],[30.95055,3.63918],[30.94677,3.65391],[30.9866,3.70187],[31.00582,3.70701],[31.02058,3.69708],[31.16547,3.7954],[31.28838,3.79643],[31.52699,3.66282],[31.7038,3.72449],[31.82671,3.82794],[31.96198,3.65596],[31.95854,3.57099],[32.04987,3.59155],[32.07733,3.57099],[32.0842,3.53672],[32.20093,3.50657],[32.21672,3.56448],[32.19578,3.59977],[32.41516,3.74504],[32.72055,3.76782],[32.89307,3.81219],[33.02782,3.89371],[33.18146,3.7793],[33.51173,3.75258],[33.98758,4.23309],[34.05762,4.28342],[34.38721,4.61065],[35.94452,4.62023],[35.95688,4.53467],[36.04134,4.44568],[36.89621,4.4491],[38.14728,3.62992],[38.55927,3.62033],[38.92181,3.51068],[39.56039,3.43392],[39.87076,3.87522],[40.76752,4.28753],[41.16371,3.94372],[41.89774,3.97797],[41.31271,3.14463],[40.98896,2.82869],[40.99548,-0.84042],[41.7,-1.8],[41.7,-49],[-27,-60],[-65.7,-52.5],[-14.5,-6]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[31.33301,35.47856],[33.0249,33.75175],[35.44189,36.02245],[31.33301,35.47856]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[14,36.5],[15,36],[14,35],[14,36.5]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[0,62],[2.5,51.3],[-2,50],[-1.9,49],[-20,53],[0,62]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[180,-35],[161,-19],[167,-12],[180,-22],[180,4],[170,3.7],[127,5.7],[118,2.7],[119.5,5.4],[117.2,7.5],[102.6,8],[102.9158,11.74099],[102.76268,12.07357],[102.70226,12.17158],[102.74139,12.46474],[102.53128,12.68857],[102.49557,12.9256],[102.49763,13.0064],[102.4597,13.08199],[102.43412,13.09026],[102.39155,13.16407],[102.35481,13.29341],[102.35893,13.30945],[102.34503,13.34837],[102.35928,13.39797],[102.3567,13.48095],[102.36168,13.50582],[102.33559,13.53787],[102.33971,13.56023],[102.35498,13.5649],[102.36511,13.5785],[102.40047,13.5679],[102.42537,13.56891],[102.44614,13.56123],[102.48047,13.57091],[102.53849,13.56757],[102.5699,13.58526],[102.57806,13.60486],[102.62501,13.60845],[102.62132,13.61295],[102.60767,13.61562],[102.57231,13.63331],[102.56922,13.64082],[102.54879,13.658],[102.56252,13.68552],[102.5972,13.70803],[102.67084,13.74472],[102.68818,13.75172],[102.7014,13.7684],[102.73161,13.77082],[102.76543,13.85541],[102.78397,13.93207],[102.80388,13.94406],[102.81607,13.96639],[102.90705,14.02119],[102.89726,14.0535],[102.90095,14.0838],[102.92421,14.10744],[102.92378,14.12838],[102.94147,14.15035],[102.92953,14.17952],[103.17535,14.33774],[103.19939,14.32992],[103.68553,14.44],[103.94508,14.34157],[104.05756,14.34589],[104.06636,14.3419],[104.26025,14.37749],[104.50058,14.36984],[104.57817,14.36019],[104.6422,14.42387],[104.66632,14.40234],[104.68357,14.39877],[104.71138,14.43169],[104.72305,14.42188],[104.71687,14.40043],[104.75344,14.40459],[104.80408,14.43867],[104.83429,14.41573],[104.99239,14.3838],[105.05402,14.19783],[105.47905,14.49186],[105.60883,15.0005],[105.46703,15.13005],[105.48866,15.20237],[105.59269,15.2716],[105.58617,15.32823],[105.50308,15.31912],[105.46703,15.33948],[105.49175,15.37921],[105.59372,15.42869],[105.59372,15.50927],[105.60986,15.54871],[105.62616,15.56492],[105.62702,15.59129],[105.63518,15.62742],[105.63612,15.66056],[105.5975,15.72088],[105.49965,15.76681],[105.46291,15.74517],[105.43819,15.75459],[105.40489,15.79424],[105.34241,15.92039],[105.37811,15.98344],[105.39167,15.99136],[105.41931,15.98608],[105.42652,15.99764],[105.41468,16.01661],[105.21263,16.05076],[105.04955,16.10552],[105.01316,16.24401],[104.88235,16.37812],[104.8391,16.45782],[104.77936,16.49041],[104.73919,16.53287],[104.74228,16.62205],[104.76391,16.70953],[104.73953,16.80323],[104.76425,16.85088],[104.73782,16.90968],[104.744,17.0128],[104.81266,17.21853],[104.79841,17.39274],[104.70348,17.52833],[104.46384,17.65515],[104.34368,17.83564],[104.27776,17.8559],[104.22661,17.98069],[104.1116,18.10735],[104.06525,18.21174],[103.97392,18.33823],[103.9286,18.33237],[103.88809,18.29456],[103.85582,18.28673],[103.83659,18.32715],[103.79128,18.3467],[103.70201,18.34214],[103.60931,18.40405],[103.57292,18.40437],[103.51593,18.42978],[103.45963,18.42587],[103.41568,18.44802],[103.30479,18.43206],[103.24265,18.37082],[103.24333,18.34133],[103.29123,18.32357],[103.28899,18.29521],[103.23595,18.28299],[103.16608,18.25511],[103.02429,17.98135],[102.6535,17.83237],[102.40631,17.99963],[102.10968,18.22413],[101.548,17.81538],[101.30493,17.64991],[101.14563,17.46595],[100.95886,17.61654],[101.01757,17.88858],[101.18752,18.05121],[101.16863,18.10409],[101.18134,18.33595],[101.08727,18.38287],[101.05499,18.43988],[101.23215,18.73015],[101.35265,19.04524],[101.25927,19.12733],[101.2373,19.32637],[101.25824,19.58438],[101.11954,19.56836],[101.08898,19.58777],[101.08624,19.59715],[101.03165,19.6185],[100.89844,19.62125],[100.77827,19.49249],[100.63751,19.56432],[100.58258,19.49313],[100.47478,19.5944],[100.42929,19.67152],[100.43341,19.7024],[100.4147,19.7255],[100.40525,19.7646],[100.43907,19.80345],[100.45555,19.84843],[100.50636,19.87264],[100.51709,19.93027],[100.58653,20.1599],[100.56576,20.1757],[100.54945,20.17473],[100.52731,20.14379],[100.51065,20.14895],[100.48697,20.17956],[100.46774,20.196],[100.45246,20.20147],[100.45521,20.22129],[100.44783,20.23546],[100.41607,20.25286],[100.40594,20.28184],[100.38397,20.31082],[100.37556,20.35187],[100.36165,20.35638],[100.35736,20.37408],[100.33195,20.39902],[100.27805,20.40224],[100.25917,20.39677],[100.2475,20.37263],[100.22535,20.35509],[100.22346,20.31839],[100.16579,20.29988],[100.17162,20.24545],[100.10845,20.25221],[100.09266,20.2696],[100.09798,20.31485],[100.07961,20.3678],[99.9567,20.46417],[99.91636,20.44925],[99.90765,20.44977],[99.89121,20.44511],[99.87276,20.44406],[99.86212,20.44326],[99.80186,20.33948],[99.46472,20.3884],[99.56085,20.20035],[99.43691,20.08882],[99.27727,20.11623],[99.06921,20.1101],[98.97789,19.74538],[98.24387,19.68656],[97.85934,19.57014],[97.76733,18.57336],[97.39655,18.47179],[97.62451,18.30238],[97.73849,17.97743],[97.66502,17.87943],[97.90947,17.56745],[98.52951,16.82557],[98.51303,16.69276],[98.69293,16.26873],[98.87421,16.43609],[98.93394,16.3353],[98.84743,16.13356],[98.74512,16.12037],[98.58307,16.07287],[98.5762,15.79754],[98.54736,15.37557],[98.17383,15.15167],[98.3606,14.63674],[99.08295,13.89208],[99.16534,13.72204],[99.18182,13.00723],[99.39331,12.56797],[99.64153,11.78973],[99.32156,11.30266],[98.77859,10.67849],[98.80597,10.47642],[98.76657,10.40459],[98.74924,10.34194],[96.85547,6.40265],[92.42523,20.54794],[92.2728,20.96272],[92.187,21.16],[92.26,21.36],[92.27,21.4328],[92.62,21.43],[92.6,22],[93.22,22.25],[93.4,23.7],[93.34,23.95],[94.155,23.847],[94.17,23.92],[94.25171,24.07405],[94.28,24.23],[94.30175,24.2371],[94.3256,24.2731],[94.6,24.7],[94.78,25.47],[95.2,26],[95.1,26.6],[97.3,27.9],[96.14,29.38],[95.4,29.1],[94.8,29.2],[92.5,27.8],[91.64,27.76],[91.4,28],[89.58,28.18],[88.9,27.32],[88.74,27.47],[88.9,27.86],[88.7,28.1],[88.1,27.87],[85.93,27.942],[81.6,30.5],[81.2,30],[78.73,31.5],[78.77,31.99],[78.4,32.5],[79.3,32.5],[79,34.3],[78.3,34.6],[78,35.5],[76.1,35.8],[76,36.55],[75.15,37],[72.5,36.9],[71.1,36.1],[71.65,35.44],[71.45,35],[70.985,34.54],[71.18,34.36],[71.092,34.118],[70.88,33.97],[70.5,33.94],[69.908,34.04],[69.869,33.96],[70,33.75],[70.13,33.73],[70.34,33.34],[70.013,33.14],[69.57,33.09],[69.24,32.45],[69.3,31.9],[68.1,31.6],[66.393,30.934],[66.2,29.8],[62.5,29.4],[60.87,29.86],[61.53,29.0165],[61.66,28.77],[61.96,28.54],[62.43,28.42],[62.6,28.25],[62.795,28.28],[62.86,27.25],[63.24,27.25],[63.327,27.13],[63.25,27.1],[63.25,26.84],[63.18,26.83],[63.18,26.65],[62.3,26.5],[62.2,26.28],[61.85,26.22],[61.84,25.75],[61.68,25.67],[56.5,-21],[180,-65],[180,-35]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[138.7,46.7],[145.5,44.5],[145.2,43.7],[146,43.3],[142,18],[122,24.3],[138.7,46.7]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-180,7],[-148,7],[-151,-12],[-171.5,-10.75],[-171,-16],[-152,-19],[-180,-55],[-180,7]]]}}]}};