]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Add traffico icons to vendor/assets/iD and asset pipeline precompile list
[rails.git] / vendor / assets / iD / iD.js
1 (function(exports) {
2
3   var bootstrap = (typeof exports.bootstrap === "object") ?
4     exports.bootstrap :
5     (exports.bootstrap = {});
6
7   bootstrap.tooltip = function() {
8
9     var tooltip = function(selection) {
10         selection.each(setup);
11       },
12       animation = d3.functor(false),
13       html = d3.functor(false),
14       title = function() {
15         var title = this.getAttribute("data-original-title");
16         if (title) {
17           return title;
18         } else {
19           title = this.getAttribute("title");
20           this.removeAttribute("title");
21           this.setAttribute("data-original-title", title);
22         }
23         return title;
24       },
25       over = "mouseenter.tooltip",
26       out = "mouseleave.tooltip",
27       placements = "top left bottom right".split(" "),
28       placement = d3.functor("top");
29
30     tooltip.title = function(_) {
31       if (arguments.length) {
32         title = d3.functor(_);
33         return tooltip;
34       } else {
35         return title;
36       }
37     };
38
39     tooltip.html = function(_) {
40       if (arguments.length) {
41         html = d3.functor(_);
42         return tooltip;
43       } else {
44         return html;
45       }
46     };
47
48     tooltip.placement = function(_) {
49       if (arguments.length) {
50         placement = d3.functor(_);
51         return tooltip;
52       } else {
53         return placement;
54       }
55     };
56
57     tooltip.show = function(selection) {
58       selection.each(show);
59     };
60
61     tooltip.hide = function(selection) {
62       selection.each(hide);
63     };
64
65     tooltip.toggle = function(selection) {
66       selection.each(toggle);
67     };
68
69     tooltip.destroy = function(selection) {
70       selection
71         .on(over, null)
72         .on(out, null)
73         .attr("title", function() {
74           return this.getAttribute("data-original-title") || this.getAttribute("title");
75         })
76         .attr("data-original-title", null)
77         .select(".tooltip")
78         .remove();
79     };
80
81     function setup() {
82       var root = d3.select(this),
83           animate = animation.apply(this, arguments),
84           tip = root.append("div")
85             .attr("class", "tooltip");
86
87       if (animate) {
88         tip.classed("fade", true);
89       }
90
91       // TODO "inside" checks?
92
93       tip.append("div")
94         .attr("class", "tooltip-arrow");
95       tip.append("div")
96         .attr("class", "tooltip-inner");
97
98       var place = placement.apply(this, arguments);
99       tip.classed(place, true);
100
101       root.on(over, show);
102       root.on(out, hide);
103     }
104
105     function show() {
106       var root = d3.select(this),
107           content = title.apply(this, arguments),
108           tip = root.select(".tooltip")
109             .classed("in", true),
110           markup = html.apply(this, arguments),
111           innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content),
112           place = placement.apply(this, arguments),
113           outer = getPosition(root.node()),
114           inner = getPosition(tip.node()),
115           pos;
116
117       switch (place) {
118         case "top":
119           pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
120           break;
121         case "right":
122           pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
123           break;
124         case "left":
125           pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
126           break;
127         case "bottom":
128           pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h};
129           break;
130       }
131
132       tip.style(pos ?
133         {left: ~~pos.x + "px", top: ~~pos.y + "px"} :
134         {left: null, top: null});
135
136       this.tooltipVisible = true;
137     }
138
139     function hide() {
140       d3.select(this).select(".tooltip")
141         .classed("in", false);
142
143       this.tooltipVisible = false;
144     }
145
146     function toggle() {
147       if (this.tooltipVisible) {
148         hide.apply(this, arguments);
149       } else {
150         show.apply(this, arguments);
151       }
152     }
153
154     return tooltip;
155   };
156
157   function getPosition(node) {
158     var mode = d3.select(node).style('position');
159     if (mode === 'absolute' || mode === 'static') {
160       return {
161         x: node.offsetLeft,
162         y: node.offsetTop,
163         w: node.offsetWidth,
164         h: node.offsetHeight
165       };
166     } else {
167       return {
168         x: 0,
169         y: 0,
170         w: node.offsetWidth,
171         h: node.offsetHeight
172       };
173     }
174   }
175
176 })(this);
177 !function(){
178   var d3 = {version: "3.5.5"}; // semver
179 d3.ascending = d3_ascending;
180
181 function d3_ascending(a, b) {
182   return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
183 }
184 d3.descending = function(a, b) {
185   return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
186 };
187 d3.min = function(array, f) {
188   var i = -1,
189       n = array.length,
190       a,
191       b;
192   if (arguments.length === 1) {
193     while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
194     while (++i < n) if ((b = array[i]) != null && a > b) a = b;
195   } else {
196     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
197     while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
198   }
199   return a;
200 };
201 d3.max = function(array, f) {
202   var i = -1,
203       n = array.length,
204       a,
205       b;
206   if (arguments.length === 1) {
207     while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
208     while (++i < n) if ((b = array[i]) != null && b > a) a = b;
209   } else {
210     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
211     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
212   }
213   return a;
214 };
215 d3.extent = function(array, f) {
216   var i = -1,
217       n = array.length,
218       a,
219       b,
220       c;
221   if (arguments.length === 1) {
222     while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; }
223     while (++i < n) if ((b = array[i]) != null) {
224       if (a > b) a = b;
225       if (c < b) c = b;
226     }
227   } else {
228     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = c = b; break; }
229     while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
230       if (a > b) a = b;
231       if (c < b) c = b;
232     }
233   }
234   return [a, c];
235 };
236 function d3_number(x) {
237   return x === null ? NaN : +x;
238 }
239
240 function d3_numeric(x) {
241   return !isNaN(x);
242 }
243
244 d3.sum = function(array, f) {
245   var s = 0,
246       n = array.length,
247       a,
248       i = -1;
249   if (arguments.length === 1) {
250     while (++i < n) if (d3_numeric(a = +array[i])) s += a; // zero and null are equivalent
251   } else {
252     while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
253   }
254   return s;
255 };
256
257 d3.mean = function(array, f) {
258   var s = 0,
259       n = array.length,
260       a,
261       i = -1,
262       j = n;
263   if (arguments.length === 1) {
264     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
265   } else {
266     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
267   }
268   if (j) return s / j;
269 };
270 // R-7 per <http://en.wikipedia.org/wiki/Quantile>
271 d3.quantile = function(values, p) {
272   var H = (values.length - 1) * p + 1,
273       h = Math.floor(H),
274       v = +values[h - 1],
275       e = H - h;
276   return e ? v + e * (values[h] - v) : v;
277 };
278
279 d3.median = function(array, f) {
280   var numbers = [],
281       n = array.length,
282       a,
283       i = -1;
284   if (arguments.length === 1) {
285     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
286   } else {
287     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
288   }
289   if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
290 };
291
292 d3.variance = function(array, f) {
293   var n = array.length,
294       m = 0,
295       a,
296       d,
297       s = 0,
298       i = -1,
299       j = 0;
300   if (arguments.length === 1) {
301     while (++i < n) {
302       if (d3_numeric(a = d3_number(array[i]))) {
303         d = a - m;
304         m += d / ++j;
305         s += d * (a - m);
306       }
307     }
308   } else {
309     while (++i < n) {
310       if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
311         d = a - m;
312         m += d / ++j;
313         s += d * (a - m);
314       }
315     }
316   }
317   if (j > 1) return s / (j - 1);
318 };
319
320 d3.deviation = function() {
321   var v = d3.variance.apply(this, arguments);
322   return v ? Math.sqrt(v) : v;
323 };
324
325 function d3_bisector(compare) {
326   return {
327     left: function(a, x, lo, hi) {
328       if (arguments.length < 3) lo = 0;
329       if (arguments.length < 4) hi = a.length;
330       while (lo < hi) {
331         var mid = lo + hi >>> 1;
332         if (compare(a[mid], x) < 0) lo = mid + 1;
333         else hi = mid;
334       }
335       return lo;
336     },
337     right: function(a, x, lo, hi) {
338       if (arguments.length < 3) lo = 0;
339       if (arguments.length < 4) hi = a.length;
340       while (lo < hi) {
341         var mid = lo + hi >>> 1;
342         if (compare(a[mid], x) > 0) hi = mid;
343         else lo = mid + 1;
344       }
345       return lo;
346     }
347   };
348 }
349
350 var d3_bisect = d3_bisector(d3_ascending);
351 d3.bisectLeft = d3_bisect.left;
352 d3.bisect = d3.bisectRight = d3_bisect.right;
353
354 d3.bisector = function(f) {
355   return d3_bisector(f.length === 1
356       ? function(d, x) { return d3_ascending(f(d), x); }
357       : f);
358 };
359 d3.shuffle = function(array, i0, i1) {
360   if ((m = arguments.length) < 3) { i1 = array.length; if (m < 2) i0 = 0; }
361   var m = i1 - i0, t, i;
362   while (m) {
363     i = Math.random() * m-- | 0;
364     t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
365   }
366   return array;
367 };
368 d3.permute = function(array, indexes) {
369   var i = indexes.length, permutes = new Array(i);
370   while (i--) permutes[i] = array[indexes[i]];
371   return permutes;
372 };
373 d3.pairs = function(array) {
374   var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
375   while (i < n) pairs[i] = [p0 = p1, p1 = array[++i]];
376   return pairs;
377 };
378
379 d3.zip = function() {
380   if (!(n = arguments.length)) return [];
381   for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m;) {
382     for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n;) {
383       zip[j] = arguments[j][i];
384     }
385   }
386   return zips;
387 };
388
389 function d3_zipLength(d) {
390   return d.length;
391 }
392
393 d3.transpose = function(matrix) {
394   return d3.zip.apply(d3, matrix);
395 };
396 d3.keys = function(map) {
397   var keys = [];
398   for (var key in map) keys.push(key);
399   return keys;
400 };
401 d3.values = function(map) {
402   var values = [];
403   for (var key in map) values.push(map[key]);
404   return values;
405 };
406 d3.entries = function(map) {
407   var entries = [];
408   for (var key in map) entries.push({key: key, value: map[key]});
409   return entries;
410 };
411 d3.merge = function(arrays) {
412   var n = arrays.length,
413       m,
414       i = -1,
415       j = 0,
416       merged,
417       array;
418
419   while (++i < n) j += arrays[i].length;
420   merged = new Array(j);
421
422   while (--n >= 0) {
423     array = arrays[n];
424     m = array.length;
425     while (--m >= 0) {
426       merged[--j] = array[m];
427     }
428   }
429
430   return merged;
431 };
432 var abs = Math.abs;
433
434 d3.range = function(start, stop, step) {
435   if (arguments.length < 3) {
436     step = 1;
437     if (arguments.length < 2) {
438       stop = start;
439       start = 0;
440     }
441   }
442   if ((stop - start) / step === Infinity) throw new Error("infinite range");
443   var range = [],
444        k = d3_range_integerScale(abs(step)),
445        i = -1,
446        j;
447   start *= k, stop *= k, step *= k;
448   if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k);
449   else while ((j = start + step * ++i) < stop) range.push(j / k);
450   return range;
451 };
452
453 function d3_range_integerScale(x) {
454   var k = 1;
455   while (x * k % 1) k *= 10;
456   return k;
457 }
458 function d3_class(ctor, properties) {
459   for (var key in properties) {
460     Object.defineProperty(ctor.prototype, key, {
461       value: properties[key],
462       enumerable: false
463     });
464   }
465 }
466
467 d3.map = function(object, f) {
468   var map = new d3_Map;
469   if (object instanceof d3_Map) {
470     object.forEach(function(key, value) { map.set(key, value); });
471   } else if (Array.isArray(object)) {
472     var i = -1,
473         n = object.length,
474         o;
475     if (arguments.length === 1) while (++i < n) map.set(i, object[i]);
476     else while (++i < n) map.set(f.call(object, o = object[i], i), o);
477   } else {
478     for (var key in object) map.set(key, object[key]);
479   }
480   return map;
481 };
482
483 function d3_Map() {
484   this._ = Object.create(null);
485 }
486
487 var d3_map_proto = "__proto__",
488     d3_map_zero = "\0";
489
490 d3_class(d3_Map, {
491   has: d3_map_has,
492   get: function(key) {
493     return this._[d3_map_escape(key)];
494   },
495   set: function(key, value) {
496     return this._[d3_map_escape(key)] = value;
497   },
498   remove: d3_map_remove,
499   keys: d3_map_keys,
500   values: function() {
501     var values = [];
502     for (var key in this._) values.push(this._[key]);
503     return values;
504   },
505   entries: function() {
506     var entries = [];
507     for (var key in this._) entries.push({key: d3_map_unescape(key), value: this._[key]});
508     return entries;
509   },
510   size: d3_map_size,
511   empty: d3_map_empty,
512   forEach: function(f) {
513     for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
514   }
515 });
516
517 function d3_map_escape(key) {
518   return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
519 }
520
521 function d3_map_unescape(key) {
522   return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
523 }
524
525 function d3_map_has(key) {
526   return d3_map_escape(key) in this._;
527 }
528
529 function d3_map_remove(key) {
530   return (key = d3_map_escape(key)) in this._ && delete this._[key];
531 }
532
533 function d3_map_keys() {
534   var keys = [];
535   for (var key in this._) keys.push(d3_map_unescape(key));
536   return keys;
537 }
538
539 function d3_map_size() {
540   var size = 0;
541   for (var key in this._) ++size;
542   return size;
543 }
544
545 function d3_map_empty() {
546   for (var key in this._) return false;
547   return true;
548 }
549
550 d3.nest = function() {
551   var nest = {},
552       keys = [],
553       sortKeys = [],
554       sortValues,
555       rollup;
556
557   function map(mapType, array, depth) {
558     if (depth >= keys.length) return rollup
559         ? rollup.call(nest, array) : (sortValues
560         ? array.sort(sortValues)
561         : array);
562
563     var i = -1,
564         n = array.length,
565         key = keys[depth++],
566         keyValue,
567         object,
568         setter,
569         valuesByKey = new d3_Map,
570         values;
571
572     while (++i < n) {
573       if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
574         values.push(object);
575       } else {
576         valuesByKey.set(keyValue, [object]);
577       }
578     }
579
580     if (mapType) {
581       object = mapType();
582       setter = function(keyValue, values) {
583         object.set(keyValue, map(mapType, values, depth));
584       };
585     } else {
586       object = {};
587       setter = function(keyValue, values) {
588         object[keyValue] = map(mapType, values, depth);
589       };
590     }
591
592     valuesByKey.forEach(setter);
593     return object;
594   }
595
596   function entries(map, depth) {
597     if (depth >= keys.length) return map;
598
599     var array = [],
600         sortKey = sortKeys[depth++];
601
602     map.forEach(function(key, keyMap) {
603       array.push({key: key, values: entries(keyMap, depth)});
604     });
605
606     return sortKey
607         ? array.sort(function(a, b) { return sortKey(a.key, b.key); })
608         : array;
609   }
610
611   nest.map = function(array, mapType) {
612     return map(mapType, array, 0);
613   };
614
615   nest.entries = function(array) {
616     return entries(map(d3.map, array, 0), 0);
617   };
618
619   nest.key = function(d) {
620     keys.push(d);
621     return nest;
622   };
623
624   // Specifies the order for the most-recently specified key.
625   // Note: only applies to entries. Map keys are unordered!
626   nest.sortKeys = function(order) {
627     sortKeys[keys.length - 1] = order;
628     return nest;
629   };
630
631   // Specifies the order for leaf values.
632   // Applies to both maps and entries array.
633   nest.sortValues = function(order) {
634     sortValues = order;
635     return nest;
636   };
637
638   nest.rollup = function(f) {
639     rollup = f;
640     return nest;
641   };
642
643   return nest;
644 };
645
646 d3.set = function(array) {
647   var set = new d3_Set;
648   if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
649   return set;
650 };
651
652 function d3_Set() {
653   this._ = Object.create(null);
654 }
655
656 d3_class(d3_Set, {
657   has: d3_map_has,
658   add: function(key) {
659     this._[d3_map_escape(key += "")] = true;
660     return key;
661   },
662   remove: d3_map_remove,
663   values: d3_map_keys,
664   size: d3_map_size,
665   empty: d3_map_empty,
666   forEach: function(f) {
667     for (var key in this._) f.call(this, d3_map_unescape(key));
668   }
669 });
670 d3.behavior = {};
671 var d3_document = this.document;
672
673 function d3_documentElement(node) {
674   return node
675       && (node.ownerDocument // node is a Node
676       || node.document // node is a Window
677       || node).documentElement; // node is a Document
678 }
679
680 function d3_window(node) {
681   return node
682       && ((node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
683         || (node.document && node) // node is a Window
684         || node.defaultView); // node is a Document
685 }
686 // Copies a variable number of methods from source to target.
687 d3.rebind = function(target, source) {
688   var i = 1, n = arguments.length, method;
689   while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
690   return target;
691 };
692
693 // Method is assumed to be a standard D3 getter-setter:
694 // If passed with no arguments, gets the value.
695 // If passed with arguments, sets the value and returns the target.
696 function d3_rebind(target, source, method) {
697   return function() {
698     var value = method.apply(source, arguments);
699     return value === source ? target : value;
700   };
701 }
702 function d3_vendorSymbol(object, name) {
703   if (name in object) return name;
704   name = name.charAt(0).toUpperCase() + name.slice(1);
705   for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
706     var prefixName = d3_vendorPrefixes[i] + name;
707     if (prefixName in object) return prefixName;
708   }
709 }
710
711 var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"];
712 var d3_arraySlice = [].slice,
713     d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists
714 function d3_noop() {}
715
716 d3.dispatch = function() {
717   var dispatch = new d3_dispatch,
718       i = -1,
719       n = arguments.length;
720   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
721   return dispatch;
722 };
723
724 function d3_dispatch() {}
725
726 d3_dispatch.prototype.on = function(type, listener) {
727   var i = type.indexOf("."),
728       name = "";
729
730   // Extract optional namespace, e.g., "click.foo"
731   if (i >= 0) {
732     name = type.slice(i + 1);
733     type = type.slice(0, i);
734   }
735
736   if (type) return arguments.length < 2
737       ? this[type].on(name)
738       : this[type].on(name, listener);
739
740   if (arguments.length === 2) {
741     if (listener == null) for (type in this) {
742       if (this.hasOwnProperty(type)) this[type].on(name, null);
743     }
744     return this;
745   }
746 };
747
748 function d3_dispatch_event(dispatch) {
749   var listeners = [],
750       listenerByName = new d3_Map;
751
752   function event() {
753     var z = listeners, // defensive reference
754         i = -1,
755         n = z.length,
756         l;
757     while (++i < n) if (l = z[i].on) l.apply(this, arguments);
758     return dispatch;
759   }
760
761   event.on = function(name, listener) {
762     var l = listenerByName.get(name),
763         i;
764
765     // return the current listener, if any
766     if (arguments.length < 2) return l && l.on;
767
768     // remove the old listener, if any (with copy-on-write)
769     if (l) {
770       l.on = null;
771       listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
772       listenerByName.remove(name);
773     }
774
775     // add the new listener, if any
776     if (listener) listeners.push(listenerByName.set(name, {on: listener}));
777
778     return dispatch;
779   };
780
781   return event;
782 }
783
784 d3.event = null;
785
786 function d3_eventPreventDefault() {
787   d3.event.preventDefault();
788 }
789
790 function d3_eventCancel() {
791   d3.event.preventDefault();
792   d3.event.stopPropagation();
793 }
794
795 function d3_eventSource() {
796   var e = d3.event, s;
797   while (s = e.sourceEvent) e = s;
798   return e;
799 }
800
801 // Like d3.dispatch, but for custom events abstracting native UI events. These
802 // events have a target component (such as a brush), a target element (such as
803 // the svg:g element containing the brush) and the standard arguments `d` (the
804 // target element's data) and `i` (the selection index of the target element).
805 function d3_eventDispatch(target) {
806   var dispatch = new d3_dispatch,
807       i = 0,
808       n = arguments.length;
809
810   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
811
812   // Creates a dispatch context for the specified `thiz` (typically, the target
813   // DOM element that received the source event) and `argumentz` (typically, the
814   // data `d` and index `i` of the target element). The returned function can be
815   // used to dispatch an event to any registered listeners; the function takes a
816   // single argument as input, being the event to dispatch. The event must have
817   // a "type" attribute which corresponds to a type registered in the
818   // constructor. This context will automatically populate the "sourceEvent" and
819   // "target" attributes of the event, as well as setting the `d3.event` global
820   // for the duration of the notification.
821   dispatch.of = function(thiz, argumentz) {
822     return function(e1) {
823       try {
824         var e0 =
825         e1.sourceEvent = d3.event;
826         e1.target = target;
827         d3.event = e1;
828         dispatch[e1.type].apply(thiz, argumentz);
829       } finally {
830         d3.event = e0;
831       }
832     };
833   };
834
835   return dispatch;
836 }
837 d3.requote = function(s) {
838   return s.replace(d3_requote_re, "\\$&");
839 };
840
841 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
842 var d3_subclass = {}.__proto__?
843
844 // Until ECMAScript supports array subclassing, prototype injection works well.
845 function(object, prototype) {
846   object.__proto__ = prototype;
847 }:
848
849 // And if your browser doesn't support __proto__, we'll use direct extension.
850 function(object, prototype) {
851   for (var property in prototype) object[property] = prototype[property];
852 };
853
854 function d3_selection(groups) {
855   d3_subclass(groups, d3_selectionPrototype);
856   return groups;
857 }
858
859 var d3_select = function(s, n) { return n.querySelector(s); },
860     d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
861     d3_selectMatches = function(n, s) {
862       var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
863       d3_selectMatches = function(n, s) {
864         return d3_selectMatcher.call(n, s);
865       };
866       return d3_selectMatches(n, s);
867     };
868
869 // Prefer Sizzle, if available.
870 if (typeof Sizzle === "function") {
871   d3_select = function(s, n) { return Sizzle(s, n)[0] || null; };
872   d3_selectAll = Sizzle;
873   d3_selectMatches = Sizzle.matchesSelector;
874 }
875
876 d3.selection = function() {
877   return d3.select(d3_document.documentElement);
878 };
879
880 var d3_selectionPrototype = d3.selection.prototype = [];
881
882
883 d3_selectionPrototype.select = function(selector) {
884   var subgroups = [],
885       subgroup,
886       subnode,
887       group,
888       node;
889
890   selector = d3_selection_selector(selector);
891
892   for (var j = -1, m = this.length; ++j < m;) {
893     subgroups.push(subgroup = []);
894     subgroup.parentNode = (group = this[j]).parentNode;
895     for (var i = -1, n = group.length; ++i < n;) {
896       if (node = group[i]) {
897         subgroup.push(subnode = selector.call(node, node.__data__, i, j));
898         if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
899       } else {
900         subgroup.push(null);
901       }
902     }
903   }
904
905   return d3_selection(subgroups);
906 };
907
908 function d3_selection_selector(selector) {
909   return typeof selector === "function" ? selector : function() {
910     return d3_select(selector, this);
911   };
912 }
913
914 d3_selectionPrototype.selectAll = function(selector) {
915   var subgroups = [],
916       subgroup,
917       node;
918
919   selector = d3_selection_selectorAll(selector);
920
921   for (var j = -1, m = this.length; ++j < m;) {
922     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
923       if (node = group[i]) {
924         subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
925         subgroup.parentNode = node;
926       }
927     }
928   }
929
930   return d3_selection(subgroups);
931 };
932
933 function d3_selection_selectorAll(selector) {
934   return typeof selector === "function" ? selector : function() {
935     return d3_selectAll(selector, this);
936   };
937 }
938 var d3_nsPrefix = {
939   svg: "http://www.w3.org/2000/svg",
940   xhtml: "http://www.w3.org/1999/xhtml",
941   xlink: "http://www.w3.org/1999/xlink",
942   xml: "http://www.w3.org/XML/1998/namespace",
943   xmlns: "http://www.w3.org/2000/xmlns/"
944 };
945
946 d3.ns = {
947   prefix: d3_nsPrefix,
948   qualify: function(name) {
949     var i = name.indexOf(":"),
950         prefix = name;
951     if (i >= 0) {
952       prefix = name.slice(0, i);
953       name = name.slice(i + 1);
954     }
955     return d3_nsPrefix.hasOwnProperty(prefix)
956         ? {space: d3_nsPrefix[prefix], local: name}
957         : name;
958   }
959 };
960
961 d3_selectionPrototype.attr = function(name, value) {
962   if (arguments.length < 2) {
963
964     // For attr(string), return the attribute value for the first node.
965     if (typeof name === "string") {
966       var node = this.node();
967       name = d3.ns.qualify(name);
968       return name.local
969           ? node.getAttributeNS(name.space, name.local)
970           : node.getAttribute(name);
971     }
972
973     // For attr(object), the object specifies the names and values of the
974     // attributes to set or remove. The values may be functions that are
975     // evaluated for each element.
976     for (value in name) this.each(d3_selection_attr(value, name[value]));
977     return this;
978   }
979
980   return this.each(d3_selection_attr(name, value));
981 };
982
983 function d3_selection_attr(name, value) {
984   name = d3.ns.qualify(name);
985
986   // For attr(string, null), remove the attribute with the specified name.
987   function attrNull() {
988     this.removeAttribute(name);
989   }
990   function attrNullNS() {
991     this.removeAttributeNS(name.space, name.local);
992   }
993
994   // For attr(string, string), set the attribute with the specified name.
995   function attrConstant() {
996     this.setAttribute(name, value);
997   }
998   function attrConstantNS() {
999     this.setAttributeNS(name.space, name.local, value);
1000   }
1001
1002   // For attr(string, function), evaluate the function for each element, and set
1003   // or remove the attribute as appropriate.
1004   function attrFunction() {
1005     var x = value.apply(this, arguments);
1006     if (x == null) this.removeAttribute(name);
1007     else this.setAttribute(name, x);
1008   }
1009   function attrFunctionNS() {
1010     var x = value.apply(this, arguments);
1011     if (x == null) this.removeAttributeNS(name.space, name.local);
1012     else this.setAttributeNS(name.space, name.local, x);
1013   }
1014
1015   return value == null
1016       ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
1017       ? (name.local ? attrFunctionNS : attrFunction)
1018       : (name.local ? attrConstantNS : attrConstant));
1019 }
1020 function d3_collapse(s) {
1021   return s.trim().replace(/\s+/g, " ");
1022 }
1023
1024 d3_selectionPrototype.classed = function(name, value) {
1025   if (arguments.length < 2) {
1026
1027     // For classed(string), return true only if the first node has the specified
1028     // class or classes. Note that even if the browser supports DOMTokenList, it
1029     // probably doesn't support it on SVG elements (which can be animated).
1030     if (typeof name === "string") {
1031       var node = this.node(),
1032           n = (name = d3_selection_classes(name)).length,
1033           i = -1;
1034       if (value = node.classList) {
1035         while (++i < n) if (!value.contains(name[i])) return false;
1036       } else {
1037         value = node.getAttribute("class");
1038         while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
1039       }
1040       return true;
1041     }
1042
1043     // For classed(object), the object specifies the names of classes to add or
1044     // remove. The values may be functions that are evaluated for each element.
1045     for (value in name) this.each(d3_selection_classed(value, name[value]));
1046     return this;
1047   }
1048
1049   // Otherwise, both a name and a value are specified, and are handled as below.
1050   return this.each(d3_selection_classed(name, value));
1051 };
1052
1053 function d3_selection_classedRe(name) {
1054   return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
1055 }
1056
1057 function d3_selection_classes(name) {
1058   return (name + "").trim().split(/^|\s+/);
1059 }
1060
1061 // Multiple class names are allowed (e.g., "foo bar").
1062 function d3_selection_classed(name, value) {
1063   name = d3_selection_classes(name).map(d3_selection_classedName);
1064   var n = name.length;
1065
1066   function classedConstant() {
1067     var i = -1;
1068     while (++i < n) name[i](this, value);
1069   }
1070
1071   // When the value is a function, the function is still evaluated only once per
1072   // element even if there are multiple class names.
1073   function classedFunction() {
1074     var i = -1, x = value.apply(this, arguments);
1075     while (++i < n) name[i](this, x);
1076   }
1077
1078   return typeof value === "function"
1079       ? classedFunction
1080       : classedConstant;
1081 }
1082
1083 function d3_selection_classedName(name) {
1084   var re = d3_selection_classedRe(name);
1085   return function(node, value) {
1086     if (c = node.classList) return value ? c.add(name) : c.remove(name);
1087     var c = node.getAttribute("class") || "";
1088     if (value) {
1089       re.lastIndex = 0;
1090       if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
1091     } else {
1092       node.setAttribute("class", d3_collapse(c.replace(re, " ")));
1093     }
1094   };
1095 }
1096
1097 d3_selectionPrototype.style = function(name, value, priority) {
1098   var n = arguments.length;
1099   if (n < 3) {
1100
1101     // For style(object) or style(object, string), the object specifies the
1102     // names and values of the attributes to set or remove. The values may be
1103     // functions that are evaluated for each element. The optional string
1104     // specifies the priority.
1105     if (typeof name !== "string") {
1106       if (n < 2) value = "";
1107       for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
1108       return this;
1109     }
1110
1111     // For style(string), return the computed style value for the first node.
1112     if (n < 2) {
1113       var node = this.node();
1114       return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
1115     }
1116
1117     // For style(string, string) or style(string, function), use the default
1118     // priority. The priority is ignored for style(string, null).
1119     priority = "";
1120   }
1121
1122   // Otherwise, a name, value and priority are specified, and handled as below.
1123   return this.each(d3_selection_style(name, value, priority));
1124 };
1125
1126 function d3_selection_style(name, value, priority) {
1127
1128   // For style(name, null) or style(name, null, priority), remove the style
1129   // property with the specified name. The priority is ignored.
1130   function styleNull() {
1131     this.style.removeProperty(name);
1132   }
1133
1134   // For style(name, string) or style(name, string, priority), set the style
1135   // property with the specified name, using the specified priority.
1136   function styleConstant() {
1137     this.style.setProperty(name, value, priority);
1138   }
1139
1140   // For style(name, function) or style(name, function, priority), evaluate the
1141   // function for each element, and set or remove the style property as
1142   // appropriate. When setting, use the specified priority.
1143   function styleFunction() {
1144     var x = value.apply(this, arguments);
1145     if (x == null) this.style.removeProperty(name);
1146     else this.style.setProperty(name, x, priority);
1147   }
1148
1149   return value == null
1150       ? styleNull : (typeof value === "function"
1151       ? styleFunction : styleConstant);
1152 }
1153
1154 d3_selectionPrototype.property = function(name, value) {
1155   if (arguments.length < 2) {
1156
1157     // For property(string), return the property value for the first node.
1158     if (typeof name === "string") return this.node()[name];
1159
1160     // For property(object), the object specifies the names and values of the
1161     // properties to set or remove. The values may be functions that are
1162     // evaluated for each element.
1163     for (value in name) this.each(d3_selection_property(value, name[value]));
1164     return this;
1165   }
1166
1167   // Otherwise, both a name and a value are specified, and are handled as below.
1168   return this.each(d3_selection_property(name, value));
1169 };
1170
1171 function d3_selection_property(name, value) {
1172
1173   // For property(name, null), remove the property with the specified name.
1174   function propertyNull() {
1175     delete this[name];
1176   }
1177
1178   // For property(name, string), set the property with the specified name.
1179   function propertyConstant() {
1180     this[name] = value;
1181   }
1182
1183   // For property(name, function), evaluate the function for each element, and
1184   // set or remove the property as appropriate.
1185   function propertyFunction() {
1186     var x = value.apply(this, arguments);
1187     if (x == null) delete this[name];
1188     else this[name] = x;
1189   }
1190
1191   return value == null
1192       ? propertyNull : (typeof value === "function"
1193       ? propertyFunction : propertyConstant);
1194 }
1195
1196 d3_selectionPrototype.text = function(value) {
1197   return arguments.length
1198       ? this.each(typeof value === "function"
1199       ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null
1200       ? function() { if (this.textContent !== "") this.textContent = ""; }
1201       : function() { if (this.textContent !== value) this.textContent = value; })
1202       : this.node().textContent;
1203 };
1204
1205 d3_selectionPrototype.html = function(value) {
1206   return arguments.length
1207       ? this.each(typeof value === "function"
1208       ? function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; } : value == null
1209       ? function() { this.innerHTML = ""; }
1210       : function() { this.innerHTML = value; })
1211       : this.node().innerHTML;
1212 };
1213
1214 d3_selectionPrototype.append = function(name) {
1215   name = d3_selection_creator(name);
1216   return this.select(function() {
1217     return this.appendChild(name.apply(this, arguments));
1218   });
1219 };
1220
1221 function d3_selection_creator(name) {
1222
1223   function create() {
1224     var document = this.ownerDocument,
1225         namespace = this.namespaceURI;
1226     return namespace
1227         ? document.createElementNS(namespace, name)
1228         : document.createElement(name);
1229   }
1230
1231   function createNS() {
1232     return this.ownerDocument.createElementNS(name.space, name.local);
1233   }
1234
1235   return typeof name === "function" ? name
1236       : (name = d3.ns.qualify(name)).local ? createNS
1237       : create;
1238 }
1239
1240 d3_selectionPrototype.insert = function(name, before) {
1241   name = d3_selection_creator(name);
1242   before = d3_selection_selector(before);
1243   return this.select(function() {
1244     return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
1245   });
1246 };
1247
1248 // TODO remove(selector)?
1249 // TODO remove(node)?
1250 // TODO remove(function)?
1251 d3_selectionPrototype.remove = function() {
1252   return this.each(d3_selectionRemove);
1253 };
1254
1255 function d3_selectionRemove() {
1256   var parent = this.parentNode;
1257   if (parent) parent.removeChild(this);
1258 }
1259
1260 d3_selectionPrototype.data = function(value, key) {
1261   var i = -1,
1262       n = this.length,
1263       group,
1264       node;
1265
1266   // If no value is specified, return the first value.
1267   if (!arguments.length) {
1268     value = new Array(n = (group = this[0]).length);
1269     while (++i < n) {
1270       if (node = group[i]) {
1271         value[i] = node.__data__;
1272       }
1273     }
1274     return value;
1275   }
1276
1277   function bind(group, groupData) {
1278     var i,
1279         n = group.length,
1280         m = groupData.length,
1281         n0 = Math.min(n, m),
1282         updateNodes = new Array(m),
1283         enterNodes = new Array(m),
1284         exitNodes = new Array(n),
1285         node,
1286         nodeData;
1287
1288     if (key) {
1289       var nodeByKeyValue = new d3_Map,
1290           keyValues = new Array(n),
1291           keyValue;
1292
1293       for (i = -1; ++i < n;) {
1294         if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) {
1295           exitNodes[i] = node; // duplicate selection key
1296         } else {
1297           nodeByKeyValue.set(keyValue, node);
1298         }
1299         keyValues[i] = keyValue;
1300       }
1301
1302       for (i = -1; ++i < m;) {
1303         if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
1304           enterNodes[i] = d3_selection_dataNode(nodeData);
1305         } else if (node !== true) { // no duplicate data key
1306           updateNodes[i] = node;
1307           node.__data__ = nodeData;
1308         }
1309         nodeByKeyValue.set(keyValue, true);
1310       }
1311
1312       for (i = -1; ++i < n;) {
1313         if (nodeByKeyValue.get(keyValues[i]) !== true) {
1314           exitNodes[i] = group[i];
1315         }
1316       }
1317     } else {
1318       for (i = -1; ++i < n0;) {
1319         node = group[i];
1320         nodeData = groupData[i];
1321         if (node) {
1322           node.__data__ = nodeData;
1323           updateNodes[i] = node;
1324         } else {
1325           enterNodes[i] = d3_selection_dataNode(nodeData);
1326         }
1327       }
1328       for (; i < m; ++i) {
1329         enterNodes[i] = d3_selection_dataNode(groupData[i]);
1330       }
1331       for (; i < n; ++i) {
1332         exitNodes[i] = group[i];
1333       }
1334     }
1335
1336     enterNodes.update
1337         = updateNodes;
1338
1339     enterNodes.parentNode
1340         = updateNodes.parentNode
1341         = exitNodes.parentNode
1342         = group.parentNode;
1343
1344     enter.push(enterNodes);
1345     update.push(updateNodes);
1346     exit.push(exitNodes);
1347   }
1348
1349   var enter = d3_selection_enter([]),
1350       update = d3_selection([]),
1351       exit = d3_selection([]);
1352
1353   if (typeof value === "function") {
1354     while (++i < n) {
1355       bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1356     }
1357   } else {
1358     while (++i < n) {
1359       bind(group = this[i], value);
1360     }
1361   }
1362
1363   update.enter = function() { return enter; };
1364   update.exit = function() { return exit; };
1365   return update;
1366 };
1367
1368 function d3_selection_dataNode(data) {
1369   return {__data__: data};
1370 }
1371
1372 d3_selectionPrototype.datum = function(value) {
1373   return arguments.length
1374       ? this.property("__data__", value)
1375       : this.property("__data__");
1376 };
1377
1378 d3_selectionPrototype.filter = function(filter) {
1379   var subgroups = [],
1380       subgroup,
1381       group,
1382       node;
1383
1384   if (typeof filter !== "function") filter = d3_selection_filter(filter);
1385
1386   for (var j = 0, m = this.length; j < m; j++) {
1387     subgroups.push(subgroup = []);
1388     subgroup.parentNode = (group = this[j]).parentNode;
1389     for (var i = 0, n = group.length; i < n; i++) {
1390       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
1391         subgroup.push(node);
1392       }
1393     }
1394   }
1395
1396   return d3_selection(subgroups);
1397 };
1398
1399 function d3_selection_filter(selector) {
1400   return function() {
1401     return d3_selectMatches(this, selector);
1402   };
1403 }
1404
1405 d3_selectionPrototype.order = function() {
1406   for (var j = -1, m = this.length; ++j < m;) {
1407     for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
1408       if (node = group[i]) {
1409         if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
1410         next = node;
1411       }
1412     }
1413   }
1414   return this;
1415 };
1416
1417 d3_selectionPrototype.sort = function(comparator) {
1418   comparator = d3_selection_sortComparator.apply(this, arguments);
1419   for (var j = -1, m = this.length; ++j < m;) this[j].sort(comparator);
1420   return this.order();
1421 };
1422
1423 function d3_selection_sortComparator(comparator) {
1424   if (!arguments.length) comparator = d3_ascending;
1425   return function(a, b) {
1426     return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
1427   };
1428 }
1429
1430 d3_selectionPrototype.each = function(callback) {
1431   return d3_selection_each(this, function(node, i, j) {
1432     callback.call(node, node.__data__, i, j);
1433   });
1434 };
1435
1436 function d3_selection_each(groups, callback) {
1437   for (var j = 0, m = groups.length; j < m; j++) {
1438     for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
1439       if (node = group[i]) callback(node, i, j);
1440     }
1441   }
1442   return groups;
1443 }
1444
1445 d3_selectionPrototype.call = function(callback) {
1446   var args = d3_array(arguments);
1447   callback.apply(args[0] = this, args);
1448   return this;
1449 };
1450
1451 d3_selectionPrototype.empty = function() {
1452   return !this.node();
1453 };
1454
1455 d3_selectionPrototype.node = function() {
1456   for (var j = 0, m = this.length; j < m; j++) {
1457     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
1458       var node = group[i];
1459       if (node) return node;
1460     }
1461   }
1462   return null;
1463 };
1464
1465 d3_selectionPrototype.size = function() {
1466   var n = 0;
1467   d3_selection_each(this, function() { ++n; });
1468   return n;
1469 };
1470
1471 function d3_selection_enter(selection) {
1472   d3_subclass(selection, d3_selection_enterPrototype);
1473   return selection;
1474 }
1475
1476 var d3_selection_enterPrototype = [];
1477
1478 d3.selection.enter = d3_selection_enter;
1479 d3.selection.enter.prototype = d3_selection_enterPrototype;
1480
1481 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
1482 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
1483 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
1484 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
1485 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
1486
1487
1488 d3_selection_enterPrototype.select = function(selector) {
1489   var subgroups = [],
1490       subgroup,
1491       subnode,
1492       upgroup,
1493       group,
1494       node;
1495
1496   for (var j = -1, m = this.length; ++j < m;) {
1497     upgroup = (group = this[j]).update;
1498     subgroups.push(subgroup = []);
1499     subgroup.parentNode = group.parentNode;
1500     for (var i = -1, n = group.length; ++i < n;) {
1501       if (node = group[i]) {
1502         subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
1503         subnode.__data__ = node.__data__;
1504       } else {
1505         subgroup.push(null);
1506       }
1507     }
1508   }
1509
1510   return d3_selection(subgroups);
1511 };
1512
1513 d3_selection_enterPrototype.insert = function(name, before) {
1514   if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
1515   return d3_selectionPrototype.insert.call(this, name, before);
1516 };
1517
1518 function d3_selection_enterInsertBefore(enter) {
1519   var i0, j0;
1520   return function(d, i, j) {
1521     var group = enter[j].update,
1522         n = group.length,
1523         node;
1524     if (j != j0) j0 = j, i0 = 0;
1525     if (i >= i0) i0 = i + 1;
1526     while (!(node = group[i0]) && ++i0 < n);
1527     return node;
1528   };
1529 }
1530
1531 // TODO fast singleton implementation?
1532 d3.select = function(node) {
1533   var group;
1534   if (typeof node === "string") {
1535     group = [d3_select(node, d3_document)];
1536     group.parentNode = d3_document.documentElement;
1537   } else {
1538     group = [node];
1539     group.parentNode = d3_documentElement(node);
1540   }
1541   return d3_selection([group]);
1542 };
1543
1544 d3.selectAll = function(nodes) {
1545   var group;
1546   if (typeof nodes === "string") {
1547     group = d3_array(d3_selectAll(nodes, d3_document));
1548     group.parentNode = d3_document.documentElement;
1549   } else {
1550     group = nodes;
1551     group.parentNode = null;
1552   }
1553   return d3_selection([group]);
1554 };
1555
1556 d3_selectionPrototype.on = function(type, listener, capture) {
1557   var n = arguments.length;
1558   if (n < 3) {
1559
1560     // For on(object) or on(object, boolean), the object specifies the event
1561     // types and listeners to add or remove. The optional boolean specifies
1562     // whether the listener captures events.
1563     if (typeof type !== "string") {
1564       if (n < 2) listener = false;
1565       for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
1566       return this;
1567     }
1568
1569     // For on(string), return the listener for the first node.
1570     if (n < 2) return (n = this.node()["__on" + type]) && n._;
1571
1572     // For on(string, function), use the default capture.
1573     capture = false;
1574   }
1575
1576   // Otherwise, a type, listener and capture are specified, and handled as below.
1577   return this.each(d3_selection_on(type, listener, capture));
1578 };
1579
1580 function d3_selection_on(type, listener, capture) {
1581   var name = "__on" + type,
1582       i = type.indexOf("."),
1583       wrap = d3_selection_onListener;
1584
1585   if (i > 0) type = type.slice(0, i);
1586   var filter = d3_selection_onFilters.get(type);
1587   if (filter) type = filter, wrap = d3_selection_onFilter;
1588
1589   function onRemove() {
1590     var l = this[name];
1591     if (l) {
1592       this.removeEventListener(type, l, l.$);
1593       delete this[name];
1594     }
1595   }
1596
1597   function onAdd() {
1598     var l = wrap(listener, d3_array(arguments));
1599     if (typeof Raven !== 'undefined') l = Raven.wrap(l);
1600     onRemove.call(this);
1601     this.addEventListener(type, this[name] = l, l.$ = capture);
1602     l._ = listener;
1603   }
1604
1605   function removeAll() {
1606     var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"),
1607         match;
1608     for (var name in this) {
1609       if (match = name.match(re)) {
1610         var l = this[name];
1611         this.removeEventListener(match[1], l, l.$);
1612         delete this[name];
1613       }
1614     }
1615   }
1616
1617   return i
1618       ? listener ? onAdd : onRemove
1619       : listener ? d3_noop : removeAll;
1620 }
1621
1622 var d3_selection_onFilters = d3.map({
1623   mouseenter: "mouseover",
1624   mouseleave: "mouseout"
1625 });
1626
1627 if (d3_document) {
1628   d3_selection_onFilters.forEach(function(k) {
1629     if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1630   });
1631 }
1632
1633 function d3_selection_onListener(listener, argumentz) {
1634   return function(e) {
1635     var o = d3.event; // Events can be reentrant (e.g., focus).
1636     d3.event = e;
1637     argumentz[0] = this.__data__;
1638     try {
1639       listener.apply(this, argumentz);
1640     } finally {
1641       d3.event = o;
1642     }
1643   };
1644 }
1645
1646 function d3_selection_onFilter(listener, argumentz) {
1647   var l = d3_selection_onListener(listener, argumentz);
1648   return function(e) {
1649     var target = this, related = e.relatedTarget;
1650     if (!related || (related !== target && !(related.compareDocumentPosition(target) & 8))) {
1651       l.call(target, e);
1652     }
1653   };
1654 }
1655
1656 var d3_event_dragSelect,
1657     d3_event_dragId = 0;
1658
1659 function d3_event_dragSuppress(node) {
1660   var name = ".dragsuppress-" + ++d3_event_dragId,
1661       click = "click" + name,
1662       w = d3.select(d3_window(node))
1663           .on("touchmove" + name, d3_eventPreventDefault)
1664           .on("dragstart" + name, d3_eventPreventDefault)
1665           .on("selectstart" + name, d3_eventPreventDefault);
1666
1667   if (d3_event_dragSelect == null) {
1668     d3_event_dragSelect = "onselectstart" in node ? false
1669         : d3_vendorSymbol(node.style, "userSelect");
1670   }
1671
1672   if (d3_event_dragSelect) {
1673     var style = d3_documentElement(node).style,
1674         select = style[d3_event_dragSelect];
1675     style[d3_event_dragSelect] = "none";
1676   }
1677
1678   return function(suppressClick) {
1679     w.on(name, null);
1680     if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
1681     if (suppressClick) { // suppress the next click, but only if it’s immediate
1682       var off = function() { w.on(click, null); };
1683       w.on(click, function() { d3_eventCancel(); off(); }, true);
1684       setTimeout(off, 0);
1685     }
1686   };
1687 }
1688
1689 d3.mouse = function(container) {
1690   return d3_mousePoint(container, d3_eventSource());
1691 };
1692
1693 // https://bugs.webkit.org/show_bug.cgi?id=44083
1694 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1695
1696 function d3_mousePoint(container, e) {
1697   if (e.changedTouches) e = e.changedTouches[0];
1698   var svg = container.ownerSVGElement || container;
1699   if (svg.createSVGPoint) {
1700     var point = svg.createSVGPoint();
1701     if (d3_mouse_bug44083 < 0) {
1702       var window = d3_window(container);
1703       if (window.scrollX || window.scrollY) {
1704         svg = d3.select("body").append("svg").style({
1705           position: "absolute",
1706           top: 0,
1707           left: 0,
1708           margin: 0,
1709           padding: 0,
1710           border: "none"
1711         }, "important");
1712         var ctm = svg[0][0].getScreenCTM();
1713         d3_mouse_bug44083 = !(ctm.f || ctm.e);
1714         svg.remove();
1715       }
1716     }
1717     if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY;
1718     else point.x = e.clientX, point.y = e.clientY;
1719     point = point.matrixTransform(container.getScreenCTM().inverse());
1720     return [point.x, point.y];
1721   }
1722   var rect = container.getBoundingClientRect();
1723   return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop];
1724 };
1725
1726 d3.touches = function(container, touches) {
1727   if (arguments.length < 2) touches = d3_eventSource().touches;
1728   return touches ? d3_array(touches).map(function(touch) {
1729     var point = d3_mousePoint(container, touch);
1730     point.identifier = touch.identifier;
1731     return point;
1732   }) : [];
1733 };
1734 var ε = 1e-6,
1735     ε2 = ε * ε,
1736     π = Math.PI,
1737     τ = 2 * π,
1738     τε = τ - ε,
1739     halfπ = π / 2,
1740     d3_radians = π / 180,
1741     d3_degrees = 180 / π;
1742
1743 function d3_sgn(x) {
1744   return x > 0 ? 1 : x < 0 ? -1 : 0;
1745 }
1746
1747 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
1748 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
1749 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
1750 // negative if clockwise, and zero if the points are collinear.
1751 function d3_cross2d(a, b, c) {
1752   return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1753 }
1754
1755 function d3_acos(x) {
1756   return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
1757 }
1758
1759 function d3_asin(x) {
1760   return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
1761 }
1762
1763 function d3_sinh(x) {
1764   return ((x = Math.exp(x)) - 1 / x) / 2;
1765 }
1766
1767 function d3_cosh(x) {
1768   return ((x = Math.exp(x)) + 1 / x) / 2;
1769 }
1770
1771 function d3_tanh(x) {
1772   return ((x = Math.exp(2 * x)) - 1) / (x + 1);
1773 }
1774
1775 function d3_haversin(x) {
1776   return (x = Math.sin(x / 2)) * x;
1777 }
1778
1779 var ρ = Math.SQRT2,
1780     ρ2 = 2,
1781     ρ4 = 4;
1782
1783 // p0 = [ux0, uy0, w0]
1784 // p1 = [ux1, uy1, w1]
1785 d3.interpolateZoom = function(p0, p1) {
1786   var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
1787       ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
1788
1789   var dx = ux1 - ux0,
1790       dy = uy1 - uy0,
1791       d2 = dx * dx + dy * dy,
1792       d1 = Math.sqrt(d2),
1793       b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1),
1794       b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1),
1795       r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
1796       r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1),
1797       dr = r1 - r0,
1798       S = (dr || Math.log(w1 / w0)) / ρ;
1799
1800   function interpolate(t) {
1801     var s = t * S;
1802     if (dr) {
1803       // General case.
1804       var coshr0 = d3_cosh(r0),
1805           u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
1806       return [
1807         ux0 + u * dx,
1808         uy0 + u * dy,
1809         w0 * coshr0 / d3_cosh(ρ * s + r0)
1810       ];
1811     }
1812     // Special case for u0 ~= u1.
1813     return [
1814       ux0 + t * dx,
1815       uy0 + t * dy,
1816       w0 * Math.exp(ρ * s)
1817     ];
1818   }
1819
1820   interpolate.duration = S * 1000;
1821
1822   return interpolate;
1823 };
1824
1825 d3.behavior.zoom = function() {
1826   var view = {x: 0, y: 0, k: 1},
1827       translate0, // translate when we started zooming (to avoid drift)
1828       center0, // implicit desired position of translate0 after zooming
1829       center, // explicit desired position of translate0 after zooming
1830       size = [960, 500], // viewport size; required for zoom interpolation
1831       scaleExtent = d3_behavior_zoomInfinity,
1832       duration = 250,
1833       zooming = 0,
1834       mousedown = "mousedown.zoom",
1835       mousemove = "mousemove.zoom",
1836       mouseup = "mouseup.zoom",
1837       mousewheelTimer,
1838       touchstart = "touchstart.zoom",
1839       touchtime, // time of last touchstart (to detect double-tap)
1840       event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
1841       x0,
1842       x1,
1843       y0,
1844       y1;
1845
1846   // Lazily determine the DOM’s support for Wheel events.
1847   // https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
1848   if (!d3_behavior_zoomWheel) {
1849     d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
1850         : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
1851         : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
1852   }
1853
1854   function zoom(g) {
1855     g   .on(mousedown, mousedowned)
1856         .on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
1857         .on("dblclick.zoom", dblclicked)
1858         .on(touchstart, touchstarted);
1859   }
1860
1861   zoom.event = function(g) {
1862     g.each(function() {
1863       var dispatch = event.of(this, arguments),
1864           view1 = view;
1865       if (d3_transitionInheritId) {
1866         d3.select(this).transition()
1867             .each("start.zoom", function() {
1868               view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state
1869               zoomstarted(dispatch);
1870             })
1871             .tween("zoom:zoom", function() {
1872               var dx = size[0],
1873                   dy = size[1],
1874                   cx = center0 ? center0[0] : dx / 2,
1875                   cy = center0 ? center0[1] : dy / 2,
1876                   i = d3.interpolateZoom(
1877                     [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k],
1878                     [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k]
1879                   );
1880               return function(t) {
1881                 var l = i(t), k = dx / l[2];
1882                 this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k};
1883                 zoomed(dispatch);
1884               };
1885             })
1886             .each("interrupt.zoom", function() {
1887               zoomended(dispatch);
1888             })
1889             .each("end.zoom", function() {
1890               zoomended(dispatch);
1891             });
1892       } else {
1893         this.__chart__ = view;
1894         zoomstarted(dispatch);
1895         zoomed(dispatch);
1896         zoomended(dispatch);
1897       }
1898     });
1899   }
1900
1901   zoom.translate = function(_) {
1902     if (!arguments.length) return [view.x, view.y];
1903     view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write
1904     rescale();
1905     return zoom;
1906   };
1907
1908   zoom.scale = function(_) {
1909     if (!arguments.length) return view.k;
1910     view = {x: view.x, y: view.y, k: +_}; // copy-on-write
1911     rescale();
1912     return zoom;
1913   };
1914
1915   zoom.scaleExtent = function(_) {
1916     if (!arguments.length) return scaleExtent;
1917     scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
1918     return zoom;
1919   };
1920
1921   zoom.center = function(_) {
1922     if (!arguments.length) return center;
1923     center = _ && [+_[0], +_[1]];
1924     return zoom;
1925   };
1926
1927   zoom.size = function(_) {
1928     if (!arguments.length) return size;
1929     size = _ && [+_[0], +_[1]];
1930     return zoom;
1931   };
1932
1933   zoom.duration = function(_) {
1934     if (!arguments.length) return duration;
1935     duration = +_; // TODO function based on interpolateZoom distance?
1936     return zoom;
1937   };
1938
1939   zoom.x = function(z) {
1940     if (!arguments.length) return x1;
1941     x1 = z;
1942     x0 = z.copy();
1943     view = {x: 0, y: 0, k: 1}; // copy-on-write
1944     return zoom;
1945   };
1946
1947   zoom.y = function(z) {
1948     if (!arguments.length) return y1;
1949     y1 = z;
1950     y0 = z.copy();
1951     view = {x: 0, y: 0, k: 1}; // copy-on-write
1952     return zoom;
1953   };
1954
1955   function location(p) {
1956     return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
1957   }
1958
1959   function point(l) {
1960     return [l[0] * view.k + view.x, l[1] * view.k + view.y];
1961   }
1962
1963   function scaleTo(s) {
1964     view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
1965   }
1966
1967   function translateTo(p, l) {
1968     l = point(l);
1969     view.x += p[0] - l[0];
1970     view.y += p[1] - l[1];
1971   }
1972
1973   function zoomTo(that, p, l, k) {
1974     that.__chart__ = {x: view.x, y: view.y, k: view.k};
1975
1976     scaleTo(Math.pow(2, k));
1977     translateTo(center0 = p, l);
1978
1979     that = d3.select(that);
1980     if (duration > 0) that = that.transition().duration(duration);
1981     that.call(zoom.event);
1982   }
1983
1984   function rescale() {
1985     if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert));
1986     if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert));
1987   }
1988
1989   function zoomstarted(dispatch) {
1990     if (!zooming++) dispatch({type: "zoomstart"});
1991   }
1992
1993   function zoomed(dispatch) {
1994     rescale();
1995     dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]});
1996   }
1997
1998   function zoomended(dispatch) {
1999     if (!--zooming) dispatch({type: "zoomend"});
2000     center0 = null;
2001   }
2002
2003   function mousedowned() {
2004     var that = this,
2005         target = d3.event.target,
2006         dispatch = event.of(that, arguments),
2007         dragged = 0,
2008         subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
2009         location0 = location(d3.mouse(that)),
2010         dragRestore = d3_event_dragSuppress(that);
2011
2012     d3_selection_interrupt.call(that);
2013     zoomstarted(dispatch);
2014
2015     function moved() {
2016       dragged = 1;
2017       translateTo(d3.mouse(that), location0);
2018       zoomed(dispatch);
2019     }
2020
2021     function ended() {
2022       subject.on(mousemove, null).on(mouseup, null);
2023       dragRestore(dragged && d3.event.target === target);
2024       zoomended(dispatch);
2025     }
2026   }
2027
2028   // These closures persist for as long as at least one touch is active.
2029   function touchstarted() {
2030     var that = this,
2031         dispatch = event.of(that, arguments),
2032         locations0 = {}, // touchstart locations
2033         distance0 = 0, // distance² between initial touches
2034         scale0, // scale when we started touching
2035         zoomName = ".zoom-" + d3.event.changedTouches[0].identifier,
2036         touchmove = "touchmove" + zoomName,
2037         touchend = "touchend" + zoomName,
2038         targets = [],
2039         subject = d3.select(that),
2040         dragRestore = d3_event_dragSuppress(that);
2041
2042     started();
2043     zoomstarted(dispatch);
2044
2045     // Workaround for Chrome issue 412723: the touchstart listener must be set
2046     // after the touchmove listener.
2047     subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events
2048
2049     // Updates locations of any touches in locations0.
2050     function relocate() {
2051       var touches = d3.touches(that);
2052       scale0 = view.k;
2053       touches.forEach(function(t) {
2054         if (t.identifier in locations0) locations0[t.identifier] = location(t);
2055       });
2056       return touches;
2057     }
2058
2059     // Temporarily override touchstart while gesture is active.
2060     function started() {
2061
2062       // Listen for touchmove and touchend on the target of touchstart.
2063       var target = d3.event.target;
2064       d3.select(target).on(touchmove, moved).on(touchend, ended);
2065       targets.push(target);
2066
2067       // Only track touches started on the same subject element.
2068       var changed = d3.event.changedTouches;
2069       for (var i = 0, n = changed.length; i < n; ++i) {
2070         locations0[changed[i].identifier] = null;
2071       }
2072
2073       var touches = relocate(),
2074           now = Date.now();
2075
2076       if (touches.length === 1) {
2077         if (now - touchtime < 500) { // dbltap
2078           var p = touches[0];
2079           zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2080           d3_eventPreventDefault();
2081         }
2082         touchtime = now;
2083       } else if (touches.length > 1) {
2084         var p = touches[0], q = touches[1],
2085             dx = p[0] - q[0], dy = p[1] - q[1];
2086         distance0 = dx * dx + dy * dy;
2087       }
2088     }
2089
2090     function moved() {
2091       var touches = d3.touches(that),
2092           p0, l0,
2093           p1, l1;
2094
2095       d3_selection_interrupt.call(that);
2096
2097       for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2098         p1 = touches[i];
2099         if (l1 = locations0[p1.identifier]) {
2100           if (l0) break;
2101           p0 = p1, l0 = l1;
2102         }
2103       }
2104
2105       if (l1) {
2106         var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
2107             scale1 = distance0 && Math.sqrt(distance1 / distance0);
2108         p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
2109         l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
2110         scaleTo(scale1 * scale0);
2111       }
2112
2113       touchtime = null;
2114       translateTo(p0, l0);
2115       zoomed(dispatch);
2116     }
2117
2118     function ended() {
2119       // If there are any globally-active touches remaining, remove the ended
2120       // touches from locations0.
2121       if (d3.event.touches.length) {
2122         var changed = d3.event.changedTouches;
2123         for (var i = 0, n = changed.length; i < n; ++i) {
2124           delete locations0[changed[i].identifier];
2125         }
2126         // If locations0 is not empty, then relocate and continue listening for
2127         // touchmove and touchend.
2128         for (var identifier in locations0) {
2129           return void relocate(); // locations may have detached due to rotation
2130         }
2131       }
2132       // Otherwise, remove touchmove and touchend listeners.
2133       d3.selectAll(targets).on(zoomName, null);
2134       subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2135       dragRestore();
2136       zoomended(dispatch);
2137     }
2138   }
2139
2140   function mousewheeled() {
2141     var dispatch = event.of(this, arguments);
2142     if (mousewheelTimer) clearTimeout(mousewheelTimer);
2143     else translate0 = location(center0 = center || d3.mouse(this)), d3_selection_interrupt.call(this), zoomstarted(dispatch);
2144     mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50);
2145     d3_eventPreventDefault();
2146     scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
2147     translateTo(center0, translate0);
2148     zoomed(dispatch);
2149   }
2150
2151   function dblclicked() {
2152     var p = d3.mouse(this),
2153         k = Math.log(view.k) / Math.LN2;
2154
2155     zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2156   }
2157
2158   return d3.rebind(zoom, event, "on");
2159 };
2160
2161 var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
2162     d3_behavior_zoomDelta, // initialized lazily
2163     d3_behavior_zoomWheel;
2164 function d3_functor(v) {
2165   return typeof v === "function" ? v : function() { return v; };
2166 }
2167
2168 d3.functor = d3_functor;
2169
2170 d3.touch = function(container, touches, identifier) {
2171   if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
2172   if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
2173     if ((touch = touches[i]).identifier === identifier) {
2174       return d3_mousePoint(container, touch);
2175     }
2176   }
2177 };
2178
2179 var d3_timer_queueHead,
2180     d3_timer_queueTail,
2181     d3_timer_interval, // is an interval (or frame) active?
2182     d3_timer_timeout, // is a timeout active?
2183     d3_timer_active, // active timer object
2184     d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };
2185
2186 // The timer will continue to fire until callback returns true.
2187 d3.timer = function(callback, delay, then) {
2188   var n = arguments.length;
2189   if (n < 2) delay = 0;
2190   if (n < 3) then = Date.now();
2191
2192   // Add the callback to the tail of the queue.
2193   var time = then + delay, timer = {c: callback, t: time, f: false, n: null};
2194   if (d3_timer_queueTail) d3_timer_queueTail.n = timer;
2195   else d3_timer_queueHead = timer;
2196   d3_timer_queueTail = timer;
2197
2198   // Start animatin'!
2199   if (!d3_timer_interval) {
2200     d3_timer_timeout = clearTimeout(d3_timer_timeout);
2201     d3_timer_interval = 1;
2202     d3_timer_frame(d3_timer_step);
2203   }
2204 };
2205
2206 function d3_timer_step() {
2207   var now = d3_timer_mark(),
2208       delay = d3_timer_sweep() - now;
2209   if (delay > 24) {
2210     if (isFinite(delay)) {
2211       clearTimeout(d3_timer_timeout);
2212       d3_timer_timeout = setTimeout(d3_timer_step, delay);
2213     }
2214     d3_timer_interval = 0;
2215   } else {
2216     d3_timer_interval = 1;
2217     d3_timer_frame(d3_timer_step);
2218   }
2219 }
2220
2221 d3.timer.flush = function() {
2222   d3_timer_mark();
2223   d3_timer_sweep();
2224 };
2225
2226 function d3_timer_mark() {
2227   var now = Date.now();
2228   d3_timer_active = d3_timer_queueHead;
2229   while (d3_timer_active) {
2230     if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
2231     d3_timer_active = d3_timer_active.n;
2232   }
2233   return now;
2234 }
2235
2236 // Flush after callbacks to avoid concurrent queue modification.
2237 // Returns the time of the earliest active timer, post-sweep.
2238 function d3_timer_sweep() {
2239   var t0,
2240       t1 = d3_timer_queueHead,
2241       time = Infinity;
2242   while (t1) {
2243     if (t1.f) {
2244       t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2245     } else {
2246       if (t1.t < time) time = t1.t;
2247       t1 = (t0 = t1).n;
2248     }
2249   }
2250   d3_timer_queueTail = t0;
2251   return time;
2252 }
2253 d3.geo = {};
2254
2255 d3.geo.stream = function(object, listener) {
2256   if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
2257     d3_geo_streamObjectType[object.type](object, listener);
2258   } else {
2259     d3_geo_streamGeometry(object, listener);
2260   }
2261 };
2262
2263 function d3_geo_streamGeometry(geometry, listener) {
2264   if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
2265     d3_geo_streamGeometryType[geometry.type](geometry, listener);
2266   }
2267 }
2268
2269 var d3_geo_streamObjectType = {
2270   Feature: function(feature, listener) {
2271     d3_geo_streamGeometry(feature.geometry, listener);
2272   },
2273   FeatureCollection: function(object, listener) {
2274     var features = object.features, i = -1, n = features.length;
2275     while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
2276   }
2277 };
2278
2279 var d3_geo_streamGeometryType = {
2280   Sphere: function(object, listener) {
2281     listener.sphere();
2282   },
2283   Point: function(object, listener) {
2284     object = object.coordinates;
2285     listener.point(object[0], object[1], object[2]);
2286   },
2287   MultiPoint: function(object, listener) {
2288     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2289     while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
2290   },
2291   LineString: function(object, listener) {
2292     d3_geo_streamLine(object.coordinates, listener, 0);
2293   },
2294   MultiLineString: function(object, listener) {
2295     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2296     while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
2297   },
2298   Polygon: function(object, listener) {
2299     d3_geo_streamPolygon(object.coordinates, listener);
2300   },
2301   MultiPolygon: function(object, listener) {
2302     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2303     while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
2304   },
2305   GeometryCollection: function(object, listener) {
2306     var geometries = object.geometries, i = -1, n = geometries.length;
2307     while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
2308   }
2309 };
2310
2311 function d3_geo_streamLine(coordinates, listener, closed) {
2312   var i = -1, n = coordinates.length - closed, coordinate;
2313   listener.lineStart();
2314   while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
2315   listener.lineEnd();
2316 }
2317
2318 function d3_geo_streamPolygon(coordinates, listener) {
2319   var i = -1, n = coordinates.length;
2320   listener.polygonStart();
2321   while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
2322   listener.polygonEnd();
2323 }
2324
2325 d3.geo.length = function(object) {
2326   d3_geo_lengthSum = 0;
2327   d3.geo.stream(object, d3_geo_length);
2328   return d3_geo_lengthSum;
2329 };
2330
2331 var d3_geo_lengthSum;
2332
2333 var d3_geo_length = {
2334   sphere: d3_noop,
2335   point: d3_noop,
2336   lineStart: d3_geo_lengthLineStart,
2337   lineEnd: d3_noop,
2338   polygonStart: d3_noop,
2339   polygonEnd: d3_noop
2340 };
2341
2342 function d3_geo_lengthLineStart() {
2343   var λ0, sinφ0, cosφ0;
2344
2345   d3_geo_length.point = function(λ, φ) {
2346     λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
2347     d3_geo_length.point = nextPoint;
2348   };
2349
2350   d3_geo_length.lineEnd = function() {
2351     d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
2352   };
2353
2354   function nextPoint(λ, φ) {
2355     var sinφ = Math.sin(φ *= d3_radians),
2356         cosφ = Math.cos(φ),
2357         t = abs((λ *= d3_radians) - λ0),
2358         cosΔλ = Math.cos(t);
2359     d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
2360     λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
2361   }
2362 }
2363 function d3_identity(d) {
2364   return d;
2365 }
2366 function d3_true() {
2367   return true;
2368 }
2369
2370 function d3_geo_spherical(cartesian) {
2371   return [
2372     Math.atan2(cartesian[1], cartesian[0]),
2373     d3_asin(cartesian[2])
2374   ];
2375 }
2376
2377 function d3_geo_sphericalEqual(a, b) {
2378   return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
2379 }
2380
2381 // General spherical polygon clipping algorithm: takes a polygon, cuts it into
2382 // visible line segments and rejoins the segments by interpolating along the
2383 // clip edge.
2384 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
2385   var subject = [],
2386       clip = [];
2387
2388   segments.forEach(function(segment) {
2389     if ((n = segment.length - 1) <= 0) return;
2390     var n, p0 = segment[0], p1 = segment[n];
2391
2392     // If the first and last points of a segment are coincident, then treat as
2393     // a closed ring.
2394     // TODO if all rings are closed, then the winding order of the exterior
2395     // ring should be checked.
2396     if (d3_geo_sphericalEqual(p0, p1)) {
2397       listener.lineStart();
2398       for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
2399       listener.lineEnd();
2400       return;
2401     }
2402
2403     var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
2404         b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
2405     a.o = b;
2406     subject.push(a);
2407     clip.push(b);
2408     a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
2409     b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
2410     a.o = b;
2411     subject.push(a);
2412     clip.push(b);
2413   });
2414   clip.sort(compare);
2415   d3_geo_clipPolygonLinkCircular(subject);
2416   d3_geo_clipPolygonLinkCircular(clip);
2417   if (!subject.length) return;
2418
2419   for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
2420     clip[i].e = entry = !entry;
2421   }
2422
2423   var start = subject[0],
2424       points,
2425       point;
2426   while (1) {
2427     // Find first unvisited intersection.
2428     var current = start,
2429         isSubject = true;
2430     while (current.v) if ((current = current.n) === start) return;
2431     points = current.z;
2432     listener.lineStart();
2433     do {
2434       current.v = current.o.v = true;
2435       if (current.e) {
2436         if (isSubject) {
2437           for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
2438         } else {
2439           interpolate(current.x, current.n.x, 1, listener);
2440         }
2441         current = current.n;
2442       } else {
2443         if (isSubject) {
2444           points = current.p.z;
2445           for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
2446         } else {
2447           interpolate(current.x, current.p.x, -1, listener);
2448         }
2449         current = current.p;
2450       }
2451       current = current.o;
2452       points = current.z;
2453       isSubject = !isSubject;
2454     } while (!current.v);
2455     listener.lineEnd();
2456   }
2457 }
2458
2459 function d3_geo_clipPolygonLinkCircular(array) {
2460   if (!(n = array.length)) return;
2461   var n,
2462       i = 0,
2463       a = array[0],
2464       b;
2465   while (++i < n) {
2466     a.n = b = array[i];
2467     b.p = a;
2468     a = b;
2469   }
2470   a.n = b = array[0];
2471   b.p = a;
2472 }
2473
2474 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
2475   this.x = point;
2476   this.z = points;
2477   this.o = other; // another intersection
2478   this.e = entry; // is an entry?
2479   this.v = false; // visited
2480   this.n = this.p = null; // next & previous
2481 }
2482
2483 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
2484   return function(rotate, listener) {
2485     var line = clipLine(listener),
2486         rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
2487
2488     var clip = {
2489       point: point,
2490       lineStart: lineStart,
2491       lineEnd: lineEnd,
2492       polygonStart: function() {
2493         clip.point = pointRing;
2494         clip.lineStart = ringStart;
2495         clip.lineEnd = ringEnd;
2496         segments = [];
2497         polygon = [];
2498       },
2499       polygonEnd: function() {
2500         clip.point = point;
2501         clip.lineStart = lineStart;
2502         clip.lineEnd = lineEnd;
2503
2504         segments = d3.merge(segments);
2505         var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
2506         if (segments.length) {
2507           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2508           d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
2509         } else if (clipStartInside) {
2510           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2511           listener.lineStart();
2512           interpolate(null, null, 1, listener);
2513           listener.lineEnd();
2514         }
2515         if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
2516         segments = polygon = null;
2517       },
2518       sphere: function() {
2519         listener.polygonStart();
2520         listener.lineStart();
2521         interpolate(null, null, 1, listener);
2522         listener.lineEnd();
2523         listener.polygonEnd();
2524       }
2525     };
2526
2527     function point(λ, φ) {
2528       var point = rotate(λ, φ);
2529       if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
2530     }
2531     function pointLine(λ, φ) {
2532       var point = rotate(λ, φ);
2533       line.point(point[0], point[1]);
2534     }
2535     function lineStart() { clip.point = pointLine; line.lineStart(); }
2536     function lineEnd() { clip.point = point; line.lineEnd(); }
2537
2538     var segments;
2539
2540     var buffer = d3_geo_clipBufferListener(),
2541         ringListener = clipLine(buffer),
2542         polygonStarted = false,
2543         polygon,
2544         ring;
2545
2546     function pointRing(λ, φ) {
2547       ring.push([λ, φ]);
2548       var point = rotate(λ, φ);
2549       ringListener.point(point[0], point[1]);
2550     }
2551
2552     function ringStart() {
2553       ringListener.lineStart();
2554       ring = [];
2555     }
2556
2557     function ringEnd() {
2558       pointRing(ring[0][0], ring[0][1]);
2559       ringListener.lineEnd();
2560
2561       var clean = ringListener.clean(),
2562           ringSegments = buffer.buffer(),
2563           segment,
2564           n = ringSegments.length;
2565
2566       ring.pop();
2567       polygon.push(ring);
2568       ring = null;
2569
2570       if (!n) return;
2571
2572       // No intersections.
2573       if (clean & 1) {
2574         segment = ringSegments[0];
2575         var n = segment.length - 1,
2576             i = -1,
2577             point;
2578         if (n > 0) {
2579           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2580           listener.lineStart();
2581           while (++i < n) listener.point((point = segment[i])[0], point[1]);
2582           listener.lineEnd();
2583         }
2584         return;
2585       }
2586
2587       // Rejoin connected segments.
2588       // TODO reuse bufferListener.rejoin()?
2589       if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
2590
2591       segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
2592     }
2593
2594     return clip;
2595   };
2596 }
2597
2598 function d3_geo_clipSegmentLength1(segment) {
2599   return segment.length > 1;
2600 }
2601
2602 function d3_geo_clipBufferListener() {
2603   var lines = [],
2604       line;
2605   return {
2606     lineStart: function() { lines.push(line = []); },
2607     point: function(λ, φ) { line.push([λ, φ]); },
2608     lineEnd: d3_noop,
2609     buffer: function() {
2610       var buffer = lines;
2611       lines = [];
2612       line = null;
2613       return buffer;
2614     },
2615     rejoin: function() {
2616       if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
2617     }
2618   };
2619 }
2620
2621 // Intersection points are sorted along the clip edge. For both antimeridian
2622 // cutting and circle clipping, the same comparison is used.
2623 function d3_geo_clipSort(a, b) {
2624   return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1])
2625        - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
2626 }
2627
2628 var d3_geo_clipAntimeridian = d3_geo_clip(
2629     d3_true,
2630     d3_geo_clipAntimeridianLine,
2631     d3_geo_clipAntimeridianInterpolate,
2632     [-π, -π / 2]);
2633
2634 // Takes a line and cuts into visible segments. Return values:
2635 //   0: there were intersections or the line was empty.
2636 //   1: no intersections.
2637 //   2: there were intersections, and the first and last segments should be
2638 //      rejoined.
2639 function d3_geo_clipAntimeridianLine(listener) {
2640   var λ0 = NaN,
2641       φ0 = NaN,
2642       sλ0 = NaN,
2643       clean; // no intersections
2644
2645   return {
2646     lineStart: function() {
2647       listener.lineStart();
2648       clean = 1;
2649     },
2650     point: function(λ1, φ1) {
2651       var sλ1 = λ1 > 0 ? π : -π,
2652           dλ = abs(λ1 - λ0);
2653       if (abs(dλ - π) < ε) { // line crosses a pole
2654         listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
2655         listener.point(sλ0, φ0);
2656         listener.lineEnd();
2657         listener.lineStart();
2658         listener.point(sλ1, φ0);
2659         listener.point(λ1, φ0);
2660         clean = 0;
2661       } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian
2662         // handle degeneracies
2663         if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
2664         if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
2665         φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
2666         listener.point(sλ0, φ0);
2667         listener.lineEnd();
2668         listener.lineStart();
2669         listener.point(sλ1, φ0);
2670         clean = 0;
2671       }
2672       listener.point(λ0 = λ1, φ0 = φ1);
2673       sλ0 = sλ1;
2674     },
2675     lineEnd: function() {
2676       listener.lineEnd();
2677       λ0 = φ0 = NaN;
2678     },
2679     // if there are intersections, we always rejoin the first and last segments.
2680     clean: function() { return 2 - clean; }
2681   };
2682 }
2683
2684 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
2685   var cosφ0,
2686       cosφ1,
2687       sinλ0_λ1 = Math.sin(λ0 - λ1);
2688   return abs(sinλ0_λ1) > ε
2689       ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1)
2690                  - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0))
2691                  / (cosφ0 * cosφ1 * sinλ0_λ1))
2692       : (φ0 + φ1) / 2;
2693 }
2694
2695 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
2696   var φ;
2697   if (from == null) {
2698     φ = direction * halfπ;
2699     listener.point(-π,  φ);
2700     listener.point( 0,  φ);
2701     listener.point( π,  φ);
2702     listener.point( π,  0);
2703     listener.point( π, -φ);
2704     listener.point( 0, -φ);
2705     listener.point(-π, -φ);
2706     listener.point(-π,  0);
2707     listener.point(-π,  φ);
2708   } else if (abs(from[0] - to[0]) > ε) {
2709     var s = from[0] < to[0] ? π : -π;
2710     φ = direction * s / 2;
2711     listener.point(-s, φ);
2712     listener.point( 0, φ);
2713     listener.point( s, φ);
2714   } else {
2715     listener.point(to[0], to[1]);
2716   }
2717 }
2718 // TODO
2719 // cross and scale return new vectors,
2720 // whereas add and normalize operate in-place
2721
2722 function d3_geo_cartesian(spherical) {
2723   var λ = spherical[0],
2724       φ = spherical[1],
2725       cosφ = Math.cos(φ);
2726   return [
2727     cosφ * Math.cos(λ),
2728     cosφ * Math.sin(λ),
2729     Math.sin(φ)
2730   ];
2731 }
2732
2733 function d3_geo_cartesianDot(a, b) {
2734   return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2735 }
2736
2737 function d3_geo_cartesianCross(a, b) {
2738   return [
2739     a[1] * b[2] - a[2] * b[1],
2740     a[2] * b[0] - a[0] * b[2],
2741     a[0] * b[1] - a[1] * b[0]
2742   ];
2743 }
2744
2745 function d3_geo_cartesianAdd(a, b) {
2746   a[0] += b[0];
2747   a[1] += b[1];
2748   a[2] += b[2];
2749 }
2750
2751 function d3_geo_cartesianScale(vector, k) {
2752   return [
2753     vector[0] * k,
2754     vector[1] * k,
2755     vector[2] * k
2756   ];
2757 }
2758
2759 function d3_geo_cartesianNormalize(d) {
2760   var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
2761   d[0] /= l;
2762   d[1] /= l;
2763   d[2] /= l;
2764 }
2765 function d3_geo_compose(a, b) {
2766
2767   function compose(x, y) {
2768     return x = a(x, y), b(x[0], x[1]);
2769   }
2770
2771   if (a.invert && b.invert) compose.invert = function(x, y) {
2772     return x = b.invert(x, y), x && a.invert(x[0], x[1]);
2773   };
2774
2775   return compose;
2776 }
2777
2778 function d3_geo_equirectangular(λ, φ) {
2779   return [λ, φ];
2780 }
2781
2782 (d3.geo.equirectangular = function() {
2783   return d3_geo_projection(d3_geo_equirectangular);
2784 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
2785
2786 d3.geo.rotation = function(rotate) {
2787   rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
2788
2789   function forward(coordinates) {
2790     coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2791     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2792   }
2793
2794   forward.invert = function(coordinates) {
2795     coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2796     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2797   };
2798
2799   return forward;
2800 };
2801
2802 function d3_geo_identityRotation(λ, φ) {
2803   return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2804 }
2805
2806 d3_geo_identityRotation.invert = d3_geo_equirectangular;
2807
2808 // Note: |δλ| must be < 2π
2809 function d3_geo_rotation(δλ, δφ, δγ) {
2810   return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ))
2811     : d3_geo_rotationλ(δλ))
2812     : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ)
2813     : d3_geo_identityRotation);
2814 }
2815
2816 function d3_geo_forwardRotationλ(δλ) {
2817   return function(λ, φ) {
2818     return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2819   };
2820 }
2821
2822 function d3_geo_rotationλ(δλ) {
2823   var rotation = d3_geo_forwardRotationλ(δλ);
2824   rotation.invert = d3_geo_forwardRotationλ(-δλ);
2825   return rotation;
2826 }
2827
2828 function d3_geo_rotationφγ(δφ, δγ) {
2829   var cosδφ = Math.cos(δφ),
2830       sinδφ = Math.sin(δφ),
2831       cosδγ = Math.cos(δγ),
2832       sinδγ = Math.sin(δγ);
2833
2834   function rotation(λ, φ) {
2835     var cosφ = Math.cos(φ),
2836         x = Math.cos(λ) * cosφ,
2837         y = Math.sin(λ) * cosφ,
2838         z = Math.sin(φ),
2839         k = z * cosδφ + x * sinδφ;
2840     return [
2841       Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
2842       d3_asin(k * cosδγ + y * sinδγ)
2843     ];
2844   }
2845
2846   rotation.invert = function(λ, φ) {
2847     var cosφ = Math.cos(φ),
2848         x = Math.cos(λ) * cosφ,
2849         y = Math.sin(λ) * cosφ,
2850         z = Math.sin(φ),
2851         k = z * cosδγ - y * sinδγ;
2852     return [
2853       Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
2854       d3_asin(k * cosδφ - x * sinδφ)
2855     ];
2856   };
2857
2858   return rotation;
2859 }
2860
2861 d3.geo.circle = function() {
2862   var origin = [0, 0],
2863       angle,
2864       precision = 6,
2865       interpolate;
2866
2867   function circle() {
2868     var center = typeof origin === "function" ? origin.apply(this, arguments) : origin,
2869         rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert,
2870         ring = [];
2871
2872     interpolate(null, null, 1, {
2873       point: function(x, y) {
2874         ring.push(x = rotate(x, y));
2875         x[0] *= d3_degrees, x[1] *= d3_degrees;
2876       }
2877     });
2878
2879     return {type: "Polygon", coordinates: [ring]};
2880   }
2881
2882   circle.origin = function(x) {
2883     if (!arguments.length) return origin;
2884     origin = x;
2885     return circle;
2886   };
2887
2888   circle.angle = function(x) {
2889     if (!arguments.length) return angle;
2890     interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
2891     return circle;
2892   };
2893
2894   circle.precision = function(_) {
2895     if (!arguments.length) return precision;
2896     interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
2897     return circle;
2898   };
2899
2900   return circle.angle(90);
2901 };
2902
2903 // Interpolates along a circle centered at [0°, 0°], with a given radius and
2904 // precision.
2905 function d3_geo_circleInterpolate(radius, precision) {
2906   var cr = Math.cos(radius),
2907       sr = Math.sin(radius);
2908   return function(from, to, direction, listener) {
2909     var step = direction * precision;
2910     if (from != null) {
2911       from = d3_geo_circleAngle(cr, from);
2912       to = d3_geo_circleAngle(cr, to);
2913       if (direction > 0 ? from < to: from > to) from += direction * τ;
2914     } else {
2915       from = radius + direction * τ;
2916       to = radius - .5 * step;
2917     }
2918     for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
2919       listener.point((point = d3_geo_spherical([
2920         cr,
2921         -sr * Math.cos(t),
2922         -sr * Math.sin(t)
2923       ]))[0], point[1]);
2924     }
2925   };
2926 }
2927
2928 // Signed angle of a cartesian point relative to [cr, 0, 0].
2929 function d3_geo_circleAngle(cr, point) {
2930   var a = d3_geo_cartesian(point);
2931   a[0] -= cr;
2932   d3_geo_cartesianNormalize(a);
2933   var angle = d3_acos(-a[1]);
2934   return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
2935 }
2936 // Adds floating point numbers with twice the normal precision.
2937 // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
2938 // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
2939 // 305–363 (1997).
2940 // Code adapted from GeographicLib by Charles F. F. Karney,
2941 // http://geographiclib.sourceforge.net/
2942 // See lib/geographiclib/LICENSE for details.
2943
2944 function d3_adder() {}
2945
2946 d3_adder.prototype = {
2947   s: 0, // rounded value
2948   t: 0, // exact error
2949   add: function(y) {
2950     d3_adderSum(y, this.t, d3_adderTemp);
2951     d3_adderSum(d3_adderTemp.s, this.s, this);
2952     if (this.s) this.t += d3_adderTemp.t;
2953     else this.s = d3_adderTemp.t;
2954   },
2955   reset: function() {
2956     this.s = this.t = 0;
2957   },
2958   valueOf: function() {
2959     return this.s;
2960   }
2961 };
2962
2963 var d3_adderTemp = new d3_adder;
2964
2965 function d3_adderSum(a, b, o) {
2966   var x = o.s = a + b, // a + b
2967       bv = x - a, av = x - bv; // b_virtual & a_virtual
2968   o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff
2969 }
2970
2971 d3.geo.area = function(object) {
2972   d3_geo_areaSum = 0;
2973   d3.geo.stream(object, d3_geo_area);
2974   return d3_geo_areaSum;
2975 };
2976
2977 var d3_geo_areaSum,
2978     d3_geo_areaRingSum = new d3_adder;
2979
2980 var d3_geo_area = {
2981   sphere: function() { d3_geo_areaSum += 4 * π; },
2982   point: d3_noop,
2983   lineStart: d3_noop,
2984   lineEnd: d3_noop,
2985
2986   // Only count area for polygon rings.
2987   polygonStart: function() {
2988     d3_geo_areaRingSum.reset();
2989     d3_geo_area.lineStart = d3_geo_areaRingStart;
2990   },
2991   polygonEnd: function() {
2992     var area = 2 * d3_geo_areaRingSum;
2993     d3_geo_areaSum += area < 0 ? 4 * π + area : area;
2994     d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
2995   }
2996 };
2997
2998 function d3_geo_areaRingStart() {
2999   var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
3000
3001   // For the first point, …
3002   d3_geo_area.point = function(λ, φ) {
3003     d3_geo_area.point = nextPoint;
3004     λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ);
3005   };
3006
3007   // For subsequent points, …
3008   function nextPoint(λ, φ) {
3009     λ *= d3_radians;
3010     φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole
3011
3012     // Spherical excess E for a spherical triangle with vertices: south pole,
3013     // previous point, current point.  Uses a formula derived from Cagnoli’s
3014     // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
3015     var dλ = λ - λ0,
3016         sdλ = dλ >= 0 ? 1 : -1,
3017         adλ = sdλ * dλ,
3018         cosφ = Math.cos(φ),
3019         sinφ = Math.sin(φ),
3020         k = sinφ0 * sinφ,
3021         u = cosφ0 * cosφ + k * Math.cos(adλ),
3022         v = k * sdλ * Math.sin(adλ);
3023     d3_geo_areaRingSum.add(Math.atan2(v, u));
3024
3025     // Advance the previous points.
3026     λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3027   }
3028
3029   // For the last point, return to the start.
3030   d3_geo_area.lineEnd = function() {
3031     nextPoint(λ00, φ00);
3032   };
3033 }
3034
3035 function d3_geo_pointInPolygon(point, polygon) {
3036   var meridian = point[0],
3037       parallel = point[1],
3038       meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
3039       polarAngle = 0,
3040       winding = 0;
3041   d3_geo_areaRingSum.reset();
3042
3043   for (var i = 0, n = polygon.length; i < n; ++i) {
3044     var ring = polygon[i],
3045         m = ring.length;
3046     if (!m) continue;
3047     var point0 = ring[0],
3048         λ0 = point0[0],
3049         φ0 = point0[1] / 2 + π / 4,
3050         sinφ0 = Math.sin(φ0),
3051         cosφ0 = Math.cos(φ0),
3052         j = 1;
3053
3054     while (true) {
3055       if (j === m) j = 0;
3056       point = ring[j];
3057       var λ = point[0],
3058           φ = point[1] / 2 + π / 4,
3059           sinφ = Math.sin(φ),
3060           cosφ = Math.cos(φ),
3061           dλ = λ - λ0,
3062           sdλ = dλ >= 0 ? 1 : -1,
3063           adλ = sdλ * dλ,
3064           antimeridian = adλ > π,
3065           k = sinφ0 * sinφ;
3066       d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
3067
3068       polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
3069
3070       // Are the longitudes either side of the point's meridian, and are the
3071       // latitudes smaller than the parallel?
3072       if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
3073         var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
3074         d3_geo_cartesianNormalize(arc);
3075         var intersection = d3_geo_cartesianCross(meridianNormal, arc);
3076         d3_geo_cartesianNormalize(intersection);
3077         var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
3078         if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
3079           winding += antimeridian ^ dλ >= 0 ? 1 : -1;
3080         }
3081       }
3082       if (!j++) break;
3083       λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
3084     }
3085   }
3086
3087   // First, determine whether the South pole is inside or outside:
3088   //
3089   // It is inside if:
3090   // * the polygon winds around it in a clockwise direction.
3091   // * the polygon does not (cumulatively) wind around it, but has a negative
3092   //   (counter-clockwise) area.
3093   //
3094   // Second, count the (signed) number of times a segment crosses a meridian
3095   // from the point to the South pole.  If it is zero, then the point is the
3096   // same side as the South pole.
3097
3098   return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
3099 }
3100
3101 // Clip features against a small circle centered at [0°, 0°].
3102 function d3_geo_clipCircle(radius) {
3103   var cr = Math.cos(radius),
3104       smallRadius = cr > 0,
3105       notHemisphere = abs(cr) > ε, // TODO optimise for this common case
3106       interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
3107
3108   return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]);
3109
3110   function visible(λ, φ) {
3111     return Math.cos(λ) * Math.cos(φ) > cr;
3112   }
3113
3114   // Takes a line and cuts into visible segments. Return values used for
3115   // polygon clipping:
3116   //   0: there were intersections or the line was empty.
3117   //   1: no intersections.
3118   //   2: there were intersections, and the first and last segments should be
3119   //      rejoined.
3120   function clipLine(listener) {
3121     var point0, // previous point
3122         c0, // code for previous point
3123         v0, // visibility of previous point
3124         v00, // visibility of first point
3125         clean; // no intersections
3126     return {
3127       lineStart: function() {
3128         v00 = v0 = false;
3129         clean = 1;
3130       },
3131       point: function(λ, φ) {
3132         var point1 = [λ, φ],
3133             point2,
3134             v = visible(λ, φ),
3135             c = smallRadius
3136               ? v ? 0 : code(λ, φ)
3137               : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
3138         if (!point0 && (v00 = v0 = v)) listener.lineStart();
3139         // Handle degeneracies.
3140         // TODO ignore if not clipping polygons.
3141         if (v !== v0) {
3142           point2 = intersect(point0, point1);
3143           if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3144             point1[0] += ε;
3145             point1[1] += ε;
3146             v = visible(point1[0], point1[1]);
3147           }
3148         }
3149         if (v !== v0) {
3150           clean = 0;
3151           if (v) {
3152             // outside going in
3153             listener.lineStart();
3154             point2 = intersect(point1, point0);
3155             listener.point(point2[0], point2[1]);
3156           } else {
3157             // inside going out
3158             point2 = intersect(point0, point1);
3159             listener.point(point2[0], point2[1]);
3160             listener.lineEnd();
3161           }
3162           point0 = point2;
3163         } else if (notHemisphere && point0 && smallRadius ^ v) {
3164           var t;
3165           // If the codes for two points are different, or are both zero,
3166           // and there this segment intersects with the small circle.
3167           if (!(c & c0) && (t = intersect(point1, point0, true))) {
3168             clean = 0;
3169             if (smallRadius) {
3170               listener.lineStart();
3171               listener.point(t[0][0], t[0][1]);
3172               listener.point(t[1][0], t[1][1]);
3173               listener.lineEnd();
3174             } else {
3175               listener.point(t[1][0], t[1][1]);
3176               listener.lineEnd();
3177               listener.lineStart();
3178               listener.point(t[0][0], t[0][1]);
3179             }
3180           }
3181         }
3182         if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3183           listener.point(point1[0], point1[1]);
3184         }
3185         point0 = point1, v0 = v, c0 = c;
3186       },
3187       lineEnd: function() {
3188         if (v0) listener.lineEnd();
3189         point0 = null;
3190       },
3191       // Rejoin first and last segments if there were intersections and the first
3192       // and last points were visible.
3193       clean: function() { return clean | ((v00 && v0) << 1); }
3194     };
3195   }
3196
3197   // Intersects the great circle between a and b with the clip circle.
3198   function intersect(a, b, two) {
3199     var pa = d3_geo_cartesian(a),
3200         pb = d3_geo_cartesian(b);
3201
3202     // We have two planes, n1.p = d1 and n2.p = d2.
3203     // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
3204     var n1 = [1, 0, 0], // normal
3205         n2 = d3_geo_cartesianCross(pa, pb),
3206         n2n2 = d3_geo_cartesianDot(n2, n2),
3207         n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2),
3208         determinant = n2n2 - n1n2 * n1n2;
3209
3210     // Two polar points.
3211     if (!determinant) return !two && a;
3212
3213     var c1 =  cr * n2n2 / determinant,
3214         c2 = -cr * n1n2 / determinant,
3215         n1xn2 = d3_geo_cartesianCross(n1, n2),
3216         A = d3_geo_cartesianScale(n1, c1),
3217         B = d3_geo_cartesianScale(n2, c2);
3218     d3_geo_cartesianAdd(A, B);
3219
3220     // Solve |p(t)|^2 = 1.
3221     var u = n1xn2,
3222         w = d3_geo_cartesianDot(A, u),
3223         uu = d3_geo_cartesianDot(u, u),
3224         t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
3225
3226     if (t2 < 0) return;
3227
3228     var t = Math.sqrt(t2),
3229         q = d3_geo_cartesianScale(u, (-w - t) / uu);
3230     d3_geo_cartesianAdd(q, A);
3231     q = d3_geo_spherical(q);
3232     if (!two) return q;
3233
3234     // Two intersection points.
3235     var λ0 = a[0],
3236         λ1 = b[0],
3237         φ0 = a[1],
3238         φ1 = b[1],
3239         z;
3240     if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3241     var δλ = λ1 - λ0,
3242         polar = abs(δλ - π) < ε,
3243         meridian = polar || δλ < ε;
3244
3245     if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3246
3247     // Check that the first point is between a and b.
3248     if (meridian
3249         ? polar
3250           ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1)
3251           : φ0 <= q[1] && q[1] <= φ1
3252         : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
3253       var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
3254       d3_geo_cartesianAdd(q1, A);
3255       return [q, d3_geo_spherical(q1)];
3256     }
3257   }
3258
3259   // Generates a 4-bit vector representing the location of a point relative to
3260   // the small circle's bounding box.
3261   function code(λ, φ) {
3262     var r = smallRadius ? radius : π - radius,
3263         code = 0;
3264     if (λ < -r) code |= 1; // left
3265     else if (λ > r) code |= 2; // right
3266     if (φ < -r) code |= 4; // below
3267     else if (φ > r) code |= 8; // above
3268     return code;
3269   }
3270 }
3271
3272 // Liang–Barsky line clipping.
3273 function d3_geom_clipLine(x0, y0, x1, y1) {
3274   return function(line) {
3275     var a = line.a,
3276         b = line.b,
3277         ax = a.x,
3278         ay = a.y,
3279         bx = b.x,
3280         by = b.y,
3281         t0 = 0,
3282         t1 = 1,
3283         dx = bx - ax,
3284         dy = by - ay,
3285         r;
3286
3287     r = x0 - ax;
3288     if (!dx && r > 0) return;
3289     r /= dx;
3290     if (dx < 0) {
3291       if (r < t0) return;
3292       if (r < t1) t1 = r;
3293     } else if (dx > 0) {
3294       if (r > t1) return;
3295       if (r > t0) t0 = r;
3296     }
3297
3298     r = x1 - ax;
3299     if (!dx && r < 0) return;
3300     r /= dx;
3301     if (dx < 0) {
3302       if (r > t1) return;
3303       if (r > t0) t0 = r;
3304     } else if (dx > 0) {
3305       if (r < t0) return;
3306       if (r < t1) t1 = r;
3307     }
3308
3309     r = y0 - ay;
3310     if (!dy && r > 0) return;
3311     r /= dy;
3312     if (dy < 0) {
3313       if (r < t0) return;
3314       if (r < t1) t1 = r;
3315     } else if (dy > 0) {
3316       if (r > t1) return;
3317       if (r > t0) t0 = r;
3318     }
3319
3320     r = y1 - ay;
3321     if (!dy && r < 0) return;
3322     r /= dy;
3323     if (dy < 0) {
3324       if (r > t1) return;
3325       if (r > t0) t0 = r;
3326     } else if (dy > 0) {
3327       if (r < t0) return;
3328       if (r < t1) t1 = r;
3329     }
3330
3331     if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy};
3332     if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy};
3333     return line;
3334   };
3335 }
3336
3337 var d3_geo_clipExtentMAX = 1e9;
3338
3339 d3.geo.clipExtent = function() {
3340   var x0, y0, x1, y1,
3341       stream,
3342       clip,
3343       clipExtent = {
3344         stream: function(output) {
3345           if (stream) stream.valid = false;
3346           stream = clip(output);
3347           stream.valid = true; // allow caching by d3.geo.path
3348           return stream;
3349         },
3350         extent: function(_) {
3351           if (!arguments.length) return [[x0, y0], [x1, y1]];
3352           clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
3353           if (stream) stream.valid = false, stream = null;
3354           return clipExtent;
3355         }
3356       };
3357   return clipExtent.extent([[0, 0], [960, 500]]);
3358 };
3359
3360 function d3_geo_clipExtent(x0, y0, x1, y1) {
3361   return function(listener) {
3362     var listener_ = listener,
3363         bufferListener = d3_geo_clipBufferListener(),
3364         clipLine = d3_geom_clipLine(x0, y0, x1, y1),
3365         segments,
3366         polygon,
3367         ring;
3368
3369     var clip = {
3370       point: point,
3371       lineStart: lineStart,
3372       lineEnd: lineEnd,
3373       polygonStart: function() {
3374         listener = bufferListener;
3375         segments = [];
3376         polygon = [];
3377         clean = true;
3378       },
3379       polygonEnd: function() {
3380         listener = listener_;
3381         segments = d3.merge(segments);
3382         var clipStartInside = insidePolygon([x0, y1]),
3383             inside = clean && clipStartInside,
3384             visible = segments.length;
3385         if (inside || visible) {
3386           listener.polygonStart();
3387           if (inside) {
3388             listener.lineStart();
3389             interpolate(null, null, 1, listener);
3390             listener.lineEnd();
3391           }
3392           if (visible) {
3393             d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
3394           }
3395           listener.polygonEnd();
3396         }
3397         segments = polygon = ring = null;
3398       }
3399     };
3400
3401     function insidePolygon(p) {
3402       var wn = 0, // the winding number counter
3403           n = polygon.length,
3404           y = p[1];
3405
3406       for (var i = 0; i < n; ++i) {
3407         for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
3408           b = v[j];
3409           if (a[1] <= y) {
3410             if (b[1] >  y && d3_cross2d(a, b, p) > 0) ++wn;
3411           } else {
3412             if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
3413           }
3414           a = b;
3415         }
3416       }
3417       return wn !== 0;
3418     }
3419
3420     function interpolate(from, to, direction, listener) {
3421       var a = 0, a1 = 0;
3422       if (from == null ||
3423           (a = corner(from, direction)) !== (a1 = corner(to, direction)) ||
3424           comparePoints(from, to) < 0 ^ direction > 0) {
3425         do {
3426           listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
3427         } while ((a = (a + direction + 4) % 4) !== a1);
3428       } else {
3429         listener.point(to[0], to[1]);
3430       }
3431     }
3432
3433     function pointVisible(x, y) {
3434       return x0 <= x && x <= x1 && y0 <= y && y <= y1;
3435     }
3436
3437     function point(x, y) {
3438       if (pointVisible(x, y)) listener.point(x, y);
3439     }
3440
3441     var x__, y__, v__, // first point
3442         x_, y_, v_, // previous point
3443         first,
3444         clean;
3445
3446     function lineStart() {
3447       clip.point = linePoint;
3448       if (polygon) polygon.push(ring = []);
3449       first = true;
3450       v_ = false;
3451       x_ = y_ = NaN;
3452     }
3453
3454     function lineEnd() {
3455       // TODO rather than special-case polygons, simply handle them separately.
3456       // Ideally, coincident intersection points should be jittered to avoid
3457       // clipping issues.
3458       if (segments) {
3459         linePoint(x__, y__);
3460         if (v__ && v_) bufferListener.rejoin();
3461         segments.push(bufferListener.buffer());
3462       }
3463       clip.point = point;
3464       if (v_) listener.lineEnd();
3465     }
3466
3467     function linePoint(x, y) {
3468       x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
3469       y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
3470       var v = pointVisible(x, y);
3471       if (polygon) ring.push([x, y]);
3472       if (first) {
3473         x__ = x, y__ = y, v__ = v;
3474         first = false;
3475         if (v) {
3476           listener.lineStart();
3477           listener.point(x, y);
3478         }
3479       } else {
3480         if (v && v_) listener.point(x, y);
3481         else {
3482           var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
3483           if (clipLine(l)) {
3484             if (!v_) {
3485               listener.lineStart();
3486               listener.point(l.a.x, l.a.y);
3487             }
3488             listener.point(l.b.x, l.b.y);
3489             if (!v) listener.lineEnd();
3490             clean = false;
3491           } else if (v) {
3492             listener.lineStart();
3493             listener.point(x, y);
3494             clean = false;
3495           }
3496         }
3497       }
3498       x_ = x, y_ = y, v_ = v;
3499     }
3500
3501     return clip;
3502   };
3503
3504   function corner(p, direction) {
3505     return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
3506         : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
3507         : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
3508         : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε
3509   }
3510
3511   function compare(a, b) {
3512     return comparePoints(a.x, b.x);
3513   }
3514
3515   function comparePoints(a, b) {
3516     var ca = corner(a, 1),
3517         cb = corner(b, 1);
3518     return ca !== cb ? ca - cb
3519         : ca === 0 ? b[1] - a[1]
3520         : ca === 1 ? a[0] - b[0]
3521         : ca === 2 ? a[1] - b[1]
3522         : b[0] - a[0];
3523   }
3524 }
3525
3526 function d3_geo_conic(projectAt) {
3527   var φ0 = 0,
3528       φ1 = π / 3,
3529       m = d3_geo_projectionMutator(projectAt),
3530       p = m(φ0, φ1);
3531
3532   p.parallels = function(_) {
3533     if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180];
3534     return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
3535   };
3536
3537   return p;
3538 }
3539
3540 function d3_geo_conicEqualArea(φ0, φ1) {
3541   var sinφ0 = Math.sin(φ0),
3542       n = (sinφ0 + Math.sin(φ1)) / 2,
3543       C = 1 + sinφ0 * (2 * n - sinφ0),
3544       ρ0 = Math.sqrt(C) / n;
3545
3546   function forward(λ, φ) {
3547     var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
3548     return [
3549       ρ * Math.sin(λ *= n),
3550       ρ0 - ρ * Math.cos(λ)
3551     ];
3552   }
3553
3554   forward.invert = function(x, y) {
3555     var ρ0_y = ρ0 - y;
3556     return [
3557       Math.atan2(x, ρ0_y) / n,
3558       d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n))
3559     ];
3560   };
3561
3562   return forward;
3563 }
3564
3565 (d3.geo.conicEqualArea = function() {
3566   return d3_geo_conic(d3_geo_conicEqualArea);
3567 }).raw = d3_geo_conicEqualArea;
3568
3569 // ESRI:102003
3570 d3.geo.albers = function() {
3571   return d3.geo.conicEqualArea()
3572       .rotate([96, 0])
3573       .center([-.6, 38.7])
3574       .parallels([29.5, 45.5])
3575       .scale(1070);
3576 };
3577
3578 // A composite projection for the United States, configured by default for
3579 // 960×500. Also works quite well at 960×600 with scale 1285. The set of
3580 // standard parallels for each region comes from USGS, which is published here:
3581 // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
3582 d3.geo.albersUsa = function() {
3583   var lower48 = d3.geo.albers();
3584
3585   // EPSG:3338
3586   var alaska = d3.geo.conicEqualArea()
3587       .rotate([154, 0])
3588       .center([-2, 58.5])
3589       .parallels([55, 65]);
3590
3591   // ESRI:102007
3592   var hawaii = d3.geo.conicEqualArea()
3593       .rotate([157, 0])
3594       .center([-3, 19.9])
3595       .parallels([8, 18]);
3596
3597   var point,
3598       pointStream = {point: function(x, y) { point = [x, y]; }},
3599       lower48Point,
3600       alaskaPoint,
3601       hawaiiPoint;
3602
3603   function albersUsa(coordinates) {
3604     var x = coordinates[0], y = coordinates[1];
3605     point = null;
3606     (lower48Point(x, y), point)
3607         || (alaskaPoint(x, y), point)
3608         || hawaiiPoint(x, y);
3609     return point;
3610   }
3611
3612   albersUsa.invert = function(coordinates) {
3613     var k = lower48.scale(),
3614         t = lower48.translate(),
3615         x = (coordinates[0] - t[0]) / k,
3616         y = (coordinates[1] - t[1]) / k;
3617     return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska
3618         : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii
3619         : lower48).invert(coordinates);
3620   };
3621
3622   // A naïve multi-projection stream.
3623   // The projections must have mutually exclusive clip regions on the sphere,
3624   // as this will avoid emitting interleaving lines and polygons.
3625   albersUsa.stream = function(stream) {
3626     var lower48Stream = lower48.stream(stream),
3627         alaskaStream = alaska.stream(stream),
3628         hawaiiStream = hawaii.stream(stream);
3629     return {
3630       point: function(x, y) {
3631         lower48Stream.point(x, y);
3632         alaskaStream.point(x, y);
3633         hawaiiStream.point(x, y);
3634       },
3635       sphere: function() {
3636         lower48Stream.sphere();
3637         alaskaStream.sphere();
3638         hawaiiStream.sphere();
3639       },
3640       lineStart: function() {
3641         lower48Stream.lineStart();
3642         alaskaStream.lineStart();
3643         hawaiiStream.lineStart();
3644       },
3645       lineEnd: function() {
3646         lower48Stream.lineEnd();
3647         alaskaStream.lineEnd();
3648         hawaiiStream.lineEnd();
3649       },
3650       polygonStart: function() {
3651         lower48Stream.polygonStart();
3652         alaskaStream.polygonStart();
3653         hawaiiStream.polygonStart();
3654       },
3655       polygonEnd: function() {
3656         lower48Stream.polygonEnd();
3657         alaskaStream.polygonEnd();
3658         hawaiiStream.polygonEnd();
3659       }
3660     };
3661   };
3662
3663   albersUsa.precision = function(_) {
3664     if (!arguments.length) return lower48.precision();
3665     lower48.precision(_);
3666     alaska.precision(_);
3667     hawaii.precision(_);
3668     return albersUsa;
3669   };
3670
3671   albersUsa.scale = function(_) {
3672     if (!arguments.length) return lower48.scale();
3673     lower48.scale(_);
3674     alaska.scale(_ * .35);
3675     hawaii.scale(_);
3676     return albersUsa.translate(lower48.translate());
3677   };
3678
3679   albersUsa.translate = function(_) {
3680     if (!arguments.length) return lower48.translate();
3681     var k = lower48.scale(), x = +_[0], y = +_[1];
3682
3683     lower48Point = lower48
3684         .translate(_)
3685         .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
3686         .stream(pointStream).point;
3687
3688     alaskaPoint = alaska
3689         .translate([x - .307 * k, y + .201 * k])
3690         .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]])
3691         .stream(pointStream).point;
3692
3693     hawaiiPoint = hawaii
3694         .translate([x - .205 * k, y + .212 * k])
3695         .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]])
3696         .stream(pointStream).point;
3697
3698     return albersUsa;
3699   };
3700
3701   return albersUsa.scale(1070);
3702 };
3703
3704 d3.geo.bounds = (function() {
3705   var λ0, φ0, λ1, φ1, // bounds
3706       λ_, // previous λ-coordinate
3707       λ__, φ__, // first point
3708       p0, // previous 3D point
3709       dλSum,
3710       ranges,
3711       range;
3712
3713   var bound = {
3714     point: point,
3715     lineStart: lineStart,
3716     lineEnd: lineEnd,
3717
3718     polygonStart: function() {
3719       bound.point = ringPoint;
3720       bound.lineStart = ringStart;
3721       bound.lineEnd = ringEnd;
3722       dλSum = 0;
3723       d3_geo_area.polygonStart();
3724     },
3725     polygonEnd: function() {
3726       d3_geo_area.polygonEnd();
3727       bound.point = point;
3728       bound.lineStart = lineStart;
3729       bound.lineEnd = lineEnd;
3730       if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90);
3731       else if (dλSum > ε) φ1 = 90;
3732       else if (dλSum < -ε) φ0 = -90;
3733       range[0] = λ0, range[1] = λ1;
3734     }
3735   };
3736
3737   function point(λ, φ) {
3738     ranges.push(range = [λ0 = λ, λ1 = λ]);
3739     if (φ < φ0) φ0 = φ;
3740     if (φ > φ1) φ1 = φ;
3741   }
3742
3743   function linePoint(λ, φ) {
3744     var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]);
3745     if (p0) {
3746       var normal = d3_geo_cartesianCross(p0, p),
3747           equatorial = [normal[1], -normal[0], 0],
3748           inflection = d3_geo_cartesianCross(equatorial, normal);
3749       d3_geo_cartesianNormalize(inflection);
3750       inflection = d3_geo_spherical(inflection);
3751       var dλ = λ - λ_,
3752           s = dλ > 0 ? 1 : -1,
3753           λi = inflection[0] * d3_degrees * s,
3754           antimeridian = abs(dλ) > 180;
3755       if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3756         var φi = inflection[1] * d3_degrees;
3757         if (φi > φ1) φ1 = φi;
3758       } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3759         var φi = -inflection[1] * d3_degrees;
3760         if (φi < φ0) φ0 = φi;
3761       } else {
3762         if (φ < φ0) φ0 = φ;
3763         if (φ > φ1) φ1 = φ;
3764       }
3765       if (antimeridian) {
3766         if (λ < λ_) {
3767           if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3768         } else {
3769           if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3770         }
3771       } else {
3772         if (λ1 >= λ0) {
3773           if (λ < λ0) λ0 = λ;
3774           if (λ > λ1) λ1 = λ;
3775         } else {
3776           if (λ > λ_) {
3777             if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3778           } else {
3779             if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3780           }
3781         }
3782       }
3783     } else {
3784       point(λ, φ);
3785     }
3786     p0 = p, λ_ = λ;
3787   }
3788
3789   function lineStart() { bound.point = linePoint; }
3790   function lineEnd() {
3791     range[0] = λ0, range[1] = λ1;
3792     bound.point = point;
3793     p0 = null;
3794   }
3795
3796   function ringPoint(λ, φ) {
3797     if (p0) {
3798       var dλ = λ - λ_;
3799       dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3800     } else λ__ = λ, φ__ = φ;
3801     d3_geo_area.point(λ, φ);
3802     linePoint(λ, φ);
3803   }
3804
3805   function ringStart() {
3806     d3_geo_area.lineStart();
3807   }
3808
3809   function ringEnd() {
3810     ringPoint(λ__, φ__);
3811     d3_geo_area.lineEnd();
3812     if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
3813     range[0] = λ0, range[1] = λ1;
3814     p0 = null;
3815   }
3816
3817   // Finds the left-right distance between two longitudes.
3818   // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want
3819   // the distance between ±180° to be 360°.
3820   function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; }
3821
3822   function compareRanges(a, b) { return a[0] - b[0]; }
3823
3824   function withinRange(x, range) {
3825     return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3826   }
3827
3828   return function(feature) {
3829     φ1 = λ1 = -(λ0 = φ0 = Infinity);
3830     ranges = [];
3831
3832     d3.geo.stream(feature, bound);
3833
3834     var n = ranges.length;
3835     if (n) {
3836       // First, sort ranges by their minimum longitudes.
3837       ranges.sort(compareRanges);
3838
3839       // Then, merge any ranges that overlap.
3840       for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
3841         b = ranges[i];
3842         if (withinRange(b[0], a) || withinRange(b[1], a)) {
3843           if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
3844           if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
3845         } else {
3846           merged.push(a = b);
3847         }
3848       }
3849
3850       // Finally, find the largest gap between the merged ranges.
3851       // The final bounding box will be the inverse of this gap.
3852       var best = -Infinity, dλ;
3853       for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
3854         b = merged[i];
3855         if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3856       }
3857     }
3858     ranges = range = null;
3859
3860     return λ0 === Infinity || φ0 === Infinity
3861         ? [[NaN, NaN], [NaN, NaN]]
3862         : [[λ0, φ0], [λ1, φ1]];
3863   };
3864 })();
3865
3866 d3.geo.centroid = function(object) {
3867   d3_geo_centroidW0 = d3_geo_centroidW1 =
3868   d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
3869   d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
3870   d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
3871   d3.geo.stream(object, d3_geo_centroid);
3872
3873   var x = d3_geo_centroidX2,
3874       y = d3_geo_centroidY2,
3875       z = d3_geo_centroidZ2,
3876       m = x * x + y * y + z * z;
3877
3878   // If the area-weighted centroid is undefined, fall back to length-weighted centroid.
3879   if (m < ε2) {
3880     x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
3881     // If the feature has zero length, fall back to arithmetic mean of point vectors.
3882     if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
3883     m = x * x + y * y + z * z;
3884     // If the feature still has an undefined centroid, then return.
3885     if (m < ε2) return [NaN, NaN];
3886   }
3887
3888   return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
3889 };
3890
3891 var d3_geo_centroidW0,
3892     d3_geo_centroidW1,
3893     d3_geo_centroidX0,
3894     d3_geo_centroidY0,
3895     d3_geo_centroidZ0,
3896     d3_geo_centroidX1,
3897     d3_geo_centroidY1,
3898     d3_geo_centroidZ1,
3899     d3_geo_centroidX2,
3900     d3_geo_centroidY2,
3901     d3_geo_centroidZ2;
3902
3903 var d3_geo_centroid = {
3904   sphere: d3_noop,
3905   point: d3_geo_centroidPoint,
3906   lineStart: d3_geo_centroidLineStart,
3907   lineEnd: d3_geo_centroidLineEnd,
3908   polygonStart: function() {
3909     d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
3910   },
3911   polygonEnd: function() {
3912     d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3913   }
3914 };
3915
3916 // Arithmetic mean of Cartesian vectors.
3917 function d3_geo_centroidPoint(λ, φ) {
3918   λ *= d3_radians;
3919   var cosφ = Math.cos(φ *= d3_radians);
3920   d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3921 }
3922
3923 function d3_geo_centroidPointXYZ(x, y, z) {
3924   ++d3_geo_centroidW0;
3925   d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
3926   d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
3927   d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
3928 }
3929
3930 function d3_geo_centroidLineStart() {
3931   var x0, y0, z0; // previous point
3932
3933   d3_geo_centroid.point = function(λ, φ) {
3934     λ *= d3_radians;
3935     var cosφ = Math.cos(φ *= d3_radians);
3936     x0 = cosφ * Math.cos(λ);
3937     y0 = cosφ * Math.sin(λ);
3938     z0 = Math.sin(φ);
3939     d3_geo_centroid.point = nextPoint;
3940     d3_geo_centroidPointXYZ(x0, y0, z0);
3941   };
3942
3943   function nextPoint(λ, φ) {
3944     λ *= d3_radians;
3945     var cosφ = Math.cos(φ *= d3_radians),
3946         x = cosφ * Math.cos(λ),
3947         y = cosφ * Math.sin(λ),
3948         z = Math.sin(φ),
3949         w = Math.atan2(
3950           Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
3951           x0 * x + y0 * y + z0 * z);
3952     d3_geo_centroidW1 += w;
3953     d3_geo_centroidX1 += w * (x0 + (x0 = x));
3954     d3_geo_centroidY1 += w * (y0 + (y0 = y));
3955     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
3956     d3_geo_centroidPointXYZ(x0, y0, z0);
3957   }
3958 }
3959
3960 function d3_geo_centroidLineEnd() {
3961   d3_geo_centroid.point = d3_geo_centroidPoint;
3962 }
3963
3964 // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
3965 // J. Applied Mechanics 42, 239 (1975).
3966 function d3_geo_centroidRingStart() {
3967   var λ00, φ00, // first point
3968       x0, y0, z0; // previous point
3969
3970   d3_geo_centroid.point = function(λ, φ) {
3971     λ00 = λ, φ00 = φ;
3972     d3_geo_centroid.point = nextPoint;
3973     λ *= d3_radians;
3974     var cosφ = Math.cos(φ *= d3_radians);
3975     x0 = cosφ * Math.cos(λ);
3976     y0 = cosφ * Math.sin(λ);
3977     z0 = Math.sin(φ);
3978     d3_geo_centroidPointXYZ(x0, y0, z0);
3979   };
3980
3981   d3_geo_centroid.lineEnd = function() {
3982     nextPoint(λ00, φ00);
3983     d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
3984     d3_geo_centroid.point = d3_geo_centroidPoint;
3985   };
3986
3987   function nextPoint(λ, φ) {
3988     λ *= d3_radians;
3989     var cosφ = Math.cos(φ *= d3_radians),
3990         x = cosφ * Math.cos(λ),
3991         y = cosφ * Math.sin(λ),
3992         z = Math.sin(φ),
3993         cx = y0 * z - z0 * y,
3994         cy = z0 * x - x0 * z,
3995         cz = x0 * y - y0 * x,
3996         m = Math.sqrt(cx * cx + cy * cy + cz * cz),
3997         u = x0 * x + y0 * y + z0 * z,
3998         v = m && -d3_acos(u) / m, // area weight
3999         w = Math.atan2(m, u); // line weight
4000     d3_geo_centroidX2 += v * cx;
4001     d3_geo_centroidY2 += v * cy;
4002     d3_geo_centroidZ2 += v * cz;
4003     d3_geo_centroidW1 += w;
4004     d3_geo_centroidX1 += w * (x0 + (x0 = x));
4005     d3_geo_centroidY1 += w * (y0 + (y0 = y));
4006     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
4007     d3_geo_centroidPointXYZ(x0, y0, z0);
4008   }
4009 }
4010
4011 // TODO Unify this code with d3.geom.polygon area?
4012
4013 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4014   point: d3_noop,
4015   lineStart: d3_noop,
4016   lineEnd: d3_noop,
4017
4018   // Only count area for polygon rings.
4019   polygonStart: function() {
4020     d3_geo_pathAreaPolygon = 0;
4021     d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4022   },
4023   polygonEnd: function() {
4024     d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
4025     d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
4026   }
4027 };
4028
4029 function d3_geo_pathAreaRingStart() {
4030   var x00, y00, x0, y0;
4031
4032   // For the first point, …
4033   d3_geo_pathArea.point = function(x, y) {
4034     d3_geo_pathArea.point = nextPoint;
4035     x00 = x0 = x, y00 = y0 = y;
4036   };
4037
4038   // For subsequent points, …
4039   function nextPoint(x, y) {
4040     d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4041     x0 = x, y0 = y;
4042   }
4043
4044   // For the last point, return to the start.
4045   d3_geo_pathArea.lineEnd = function() {
4046     nextPoint(x00, y00);
4047   };
4048 }
4049
4050 var d3_geo_pathBoundsX0,
4051     d3_geo_pathBoundsY0,
4052     d3_geo_pathBoundsX1,
4053     d3_geo_pathBoundsY1;
4054
4055 var d3_geo_pathBounds = {
4056   point: d3_geo_pathBoundsPoint,
4057   lineStart: d3_noop,
4058   lineEnd: d3_noop,
4059   polygonStart: d3_noop,
4060   polygonEnd: d3_noop
4061 };
4062
4063 function d3_geo_pathBoundsPoint(x, y) {
4064   if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
4065   if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
4066   if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
4067   if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
4068 }
4069 function d3_geo_pathBuffer() {
4070   var pointCircle = d3_geo_pathBufferCircle(4.5),
4071       buffer = [];
4072
4073   var stream = {
4074     point: point,
4075
4076     // While inside a line, override point to moveTo then lineTo.
4077     lineStart: function() { stream.point = pointLineStart; },
4078     lineEnd: lineEnd,
4079
4080     // While inside a polygon, override lineEnd to closePath.
4081     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4082     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4083
4084     pointRadius: function(_) {
4085       pointCircle = d3_geo_pathBufferCircle(_);
4086       return stream;
4087     },
4088
4089     result: function() {
4090       if (buffer.length) {
4091         var result = buffer.join("");
4092         buffer = [];
4093         return result;
4094       }
4095     }
4096   };
4097
4098   function point(x, y) {
4099     buffer.push("M", x, ",", y, pointCircle);
4100   }
4101
4102   function pointLineStart(x, y) {
4103     buffer.push("M", x, ",", y);
4104     stream.point = pointLine;
4105   }
4106
4107   function pointLine(x, y) {
4108     buffer.push("L", x, ",", y);
4109   }
4110
4111   function lineEnd() {
4112     stream.point = point;
4113   }
4114
4115   function lineEndPolygon() {
4116     buffer.push("Z");
4117   }
4118
4119   return stream;
4120 }
4121
4122 function d3_geo_pathBufferCircle(radius) {
4123   return "m0," + radius
4124       + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
4125       + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
4126       + "z";
4127 }
4128
4129 // TODO Unify this code with d3.geom.polygon centroid?
4130 // TODO Enforce positive area for exterior, negative area for interior?
4131
4132 var d3_geo_pathCentroid = {
4133   point: d3_geo_pathCentroidPoint,
4134
4135   // For lines, weight by length.
4136   lineStart: d3_geo_pathCentroidLineStart,
4137   lineEnd: d3_geo_pathCentroidLineEnd,
4138
4139   // For polygons, weight by area.
4140   polygonStart: function() {
4141     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4142   },
4143   polygonEnd: function() {
4144     d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4145     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
4146     d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
4147   }
4148 };
4149
4150 function d3_geo_pathCentroidPoint(x, y) {
4151   d3_geo_centroidX0 += x;
4152   d3_geo_centroidY0 += y;
4153   ++d3_geo_centroidZ0;
4154 }
4155
4156 function d3_geo_pathCentroidLineStart() {
4157   var x0, y0;
4158
4159   d3_geo_pathCentroid.point = function(x, y) {
4160     d3_geo_pathCentroid.point = nextPoint;
4161     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4162   };
4163
4164   function nextPoint(x, y) {
4165     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4166     d3_geo_centroidX1 += z * (x0 + x) / 2;
4167     d3_geo_centroidY1 += z * (y0 + y) / 2;
4168     d3_geo_centroidZ1 += z;
4169     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4170   }
4171 }
4172
4173 function d3_geo_pathCentroidLineEnd() {
4174   d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4175 }
4176
4177 function d3_geo_pathCentroidRingStart() {
4178   var x00, y00, x0, y0;
4179
4180   // For the first point, …
4181   d3_geo_pathCentroid.point = function(x, y) {
4182     d3_geo_pathCentroid.point = nextPoint;
4183     d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
4184   };
4185
4186   // For subsequent points, …
4187   function nextPoint(x, y) {
4188     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4189     d3_geo_centroidX1 += z * (x0 + x) / 2;
4190     d3_geo_centroidY1 += z * (y0 + y) / 2;
4191     d3_geo_centroidZ1 += z;
4192
4193     z = y0 * x - x0 * y;
4194     d3_geo_centroidX2 += z * (x0 + x);
4195     d3_geo_centroidY2 += z * (y0 + y);
4196     d3_geo_centroidZ2 += z * 3;
4197     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4198   }
4199
4200   // For the last point, return to the start.
4201   d3_geo_pathCentroid.lineEnd = function() {
4202     nextPoint(x00, y00);
4203   };
4204 }
4205
4206 function d3_geo_pathContext(context) {
4207   var pointRadius = 4.5;
4208
4209   var stream = {
4210     point: point,
4211
4212     // While inside a line, override point to moveTo then lineTo.
4213     lineStart: function() { stream.point = pointLineStart; },
4214     lineEnd: lineEnd,
4215
4216     // While inside a polygon, override lineEnd to closePath.
4217     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4218     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4219
4220     pointRadius: function(_) {
4221       pointRadius = _;
4222       return stream;
4223     },
4224
4225     result: d3_noop
4226   };
4227
4228   function point(x, y) {
4229     context.moveTo(x + pointRadius, y);
4230     context.arc(x, y, pointRadius, 0, τ);
4231   }
4232
4233   function pointLineStart(x, y) {
4234     context.moveTo(x, y);
4235     stream.point = pointLine;
4236   }
4237
4238   function pointLine(x, y) {
4239     context.lineTo(x, y);
4240   }
4241
4242   function lineEnd() {
4243     stream.point = point;
4244   }
4245
4246   function lineEndPolygon() {
4247     context.closePath();
4248   }
4249
4250   return stream;
4251 }
4252
4253 function d3_geo_resample(project) {
4254   var δ2 = .5, // precision, px²
4255       cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance)
4256       maxDepth = 16;
4257
4258   function resample(stream) {
4259     return (maxDepth ? resampleRecursive : resampleNone)(stream);
4260   }
4261
4262   function resampleNone(stream) {
4263     return d3_geo_transformPoint(stream, function(x, y) {
4264       x = project(x, y);
4265       stream.point(x[0], x[1]);
4266     });
4267   }
4268
4269   function resampleRecursive(stream) {
4270     var λ00, φ00, x00, y00, a00, b00, c00, // first point
4271         λ0, x0, y0, a0, b0, c0; // previous point
4272
4273     var resample = {
4274       point: point,
4275       lineStart: lineStart,
4276       lineEnd: lineEnd,
4277       polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
4278       polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
4279     };
4280
4281     function point(x, y) {
4282       x = project(x, y);
4283       stream.point(x[0], x[1]);
4284     }
4285
4286     function lineStart() {
4287       x0 = NaN;
4288       resample.point = linePoint;
4289       stream.lineStart();
4290     }
4291
4292     function linePoint(λ, φ) {
4293       var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ);
4294       resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
4295       stream.point(x0, y0);
4296     }
4297
4298     function lineEnd() {
4299       resample.point = point;
4300       stream.lineEnd();
4301     }
4302
4303     function ringStart() {
4304       lineStart();
4305       resample.point = ringPoint;
4306       resample.lineEnd = ringEnd;
4307     }
4308
4309     function ringPoint(λ, φ) {
4310       linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4311       resample.point = linePoint;
4312     }
4313
4314     function ringEnd() {
4315       resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4316       resample.lineEnd = lineEnd;
4317       lineEnd();
4318     }
4319
4320     return resample;
4321   }
4322
4323   function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4324     var dx = x1 - x0,
4325         dy = y1 - y0,
4326         d2 = dx * dx + dy * dy;
4327     if (d2 > 4 * δ2 && depth--) {
4328       var a = a0 + a1,
4329           b = b0 + b1,
4330           c = c0 + c1,
4331           m = Math.sqrt(a * a + b * b + c * c),
4332           φ2 = Math.asin(c /= m),
4333           λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
4334           p = project(λ2, φ2),
4335           x2 = p[0],
4336           y2 = p[1],
4337           dx2 = x2 - x0,
4338           dy2 = y2 - y0,
4339           dz = dy * dx2 - dx * dy2;
4340       if (dz * dz / d2 > δ2 // perpendicular projected distance
4341           || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
4342           || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
4343         resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
4344         stream.point(x2, y2);
4345         resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
4346       }
4347     }
4348   }
4349
4350   resample.precision = function(_) {
4351     if (!arguments.length) return Math.sqrt(δ2);
4352     maxDepth = (δ2 = _ * _) > 0 && 16;
4353     return resample;
4354   };
4355
4356   return resample;
4357 }
4358
4359 d3.geo.path = function() {
4360   var pointRadius = 4.5,
4361       projection,
4362       context,
4363       projectStream,
4364       contextStream,
4365       cacheStream;
4366
4367   function path(object) {
4368     if (object) {
4369       if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
4370       if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
4371       d3.geo.stream(object, cacheStream);
4372     }
4373     return contextStream.result();
4374   }
4375
4376   path.area = function(object) {
4377     d3_geo_pathAreaSum = 0;
4378     d3.geo.stream(object, projectStream(d3_geo_pathArea));
4379     return d3_geo_pathAreaSum;
4380   };
4381
4382   path.centroid = function(object) {
4383     d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
4384     d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
4385     d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
4386     d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
4387     return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2]
4388         : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1]
4389         : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0]
4390         : [NaN, NaN];
4391   };
4392
4393   path.bounds = function(object) {
4394     d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
4395     d3.geo.stream(object, projectStream(d3_geo_pathBounds));
4396     return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]];
4397   };
4398
4399   path.projection = function(_) {
4400     if (!arguments.length) return projection;
4401     projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
4402     return reset();
4403   };
4404
4405   path.context = function(_) {
4406     if (!arguments.length) return context;
4407     contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_);
4408     if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
4409     return reset();
4410   };
4411
4412   path.pointRadius = function(_) {
4413     if (!arguments.length) return pointRadius;
4414     pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
4415     return path;
4416   };
4417
4418   function reset() {
4419     cacheStream = null;
4420     return path;
4421   }
4422
4423   return path.projection(d3.geo.albersUsa()).context(null);
4424 };
4425
4426 function d3_geo_pathProjectStream(project) {
4427   var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); });
4428   return function(stream) { return d3_geo_projectionRadians(resample(stream)); };
4429 }
4430
4431 d3.geo.transform = function(methods) {
4432   return {
4433     stream: function(stream) {
4434       var transform = new d3_geo_transform(stream);
4435       for (var k in methods) transform[k] = methods[k];
4436       return transform;
4437     }
4438   };
4439 };
4440
4441 function d3_geo_transform(stream) {
4442   this.stream = stream;
4443 }
4444
4445 d3_geo_transform.prototype = {
4446   point: function(x, y) { this.stream.point(x, y); },
4447   sphere: function() { this.stream.sphere(); },
4448   lineStart: function() { this.stream.lineStart(); },
4449   lineEnd: function() { this.stream.lineEnd(); },
4450   polygonStart: function() { this.stream.polygonStart(); },
4451   polygonEnd: function() { this.stream.polygonEnd(); }
4452 };
4453
4454 function d3_geo_transformPoint(stream, point) {
4455   return {
4456     point: point,
4457     sphere: function() { stream.sphere(); },
4458     lineStart: function() { stream.lineStart(); },
4459     lineEnd: function() { stream.lineEnd(); },
4460     polygonStart: function() { stream.polygonStart(); },
4461     polygonEnd: function() { stream.polygonEnd(); },
4462   };
4463 }
4464
4465 d3.geo.projection = d3_geo_projection;
4466 d3.geo.projectionMutator = d3_geo_projectionMutator;
4467
4468 function d3_geo_projection(project) {
4469   return d3_geo_projectionMutator(function() { return project; })();
4470 }
4471
4472 function d3_geo_projectionMutator(projectAt) {
4473   var project,
4474       rotate,
4475       projectRotate,
4476       projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
4477       k = 150, // scale
4478       x = 480, y = 250, // translate
4479       λ = 0, φ = 0, // center
4480       δλ = 0, δφ = 0, δγ = 0, // rotate
4481       δx, δy, // center
4482       preclip = d3_geo_clipAntimeridian,
4483       postclip = d3_identity,
4484       clipAngle = null,
4485       clipExtent = null,
4486       stream;
4487
4488   function projection(point) {
4489     point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
4490     return [point[0] * k + δx, δy - point[1] * k];
4491   }
4492
4493   function invert(point) {
4494     point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
4495     return point && [point[0] * d3_degrees, point[1] * d3_degrees];
4496   }
4497
4498   projection.stream = function(output) {
4499     if (stream) stream.valid = false;
4500     stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
4501     stream.valid = true; // allow caching by d3.geo.path
4502     return stream;
4503   };
4504
4505   projection.clipAngle = function(_) {
4506     if (!arguments.length) return clipAngle;
4507     preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
4508     return invalidate();
4509   };
4510
4511   projection.clipExtent = function(_) {
4512     if (!arguments.length) return clipExtent;
4513     clipExtent = _;
4514     postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
4515     return invalidate();
4516   };
4517
4518   projection.scale = function(_) {
4519     if (!arguments.length) return k;
4520     k = +_;
4521     return reset();
4522   };
4523
4524   projection.translate = function(_) {
4525     if (!arguments.length) return [x, y];
4526     x = +_[0];
4527     y = +_[1];
4528     return reset();
4529   };
4530
4531   projection.center = function(_) {
4532     if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
4533     λ = _[0] % 360 * d3_radians;
4534     φ = _[1] % 360 * d3_radians;
4535     return reset();
4536   };
4537
4538   projection.rotate = function(_) {
4539     if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees];
4540     δλ = _[0] % 360 * d3_radians;
4541     δφ = _[1] % 360 * d3_radians;
4542     δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
4543     return reset();
4544   };
4545
4546   d3.rebind(projection, projectResample, "precision");
4547
4548   function reset() {
4549     projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
4550     var center = project(λ, φ);
4551     δx = x - center[0] * k;
4552     δy = y + center[1] * k;
4553     return invalidate();
4554   }
4555
4556   function invalidate() {
4557     if (stream) stream.valid = false, stream = null;
4558     return projection;
4559   }
4560
4561   return function() {
4562     project = projectAt.apply(this, arguments);
4563     projection.invert = project.invert && invert;
4564     return reset();
4565   };
4566 }
4567
4568 function d3_geo_projectionRadians(stream) {
4569   return d3_geo_transformPoint(stream, function(x, y) {
4570     stream.point(x * d3_radians, y * d3_radians);
4571   });
4572 }
4573
4574 function d3_geo_mercator(λ, φ) {
4575   return [λ, Math.log(Math.tan(π / 4 + φ / 2))];
4576 }
4577
4578 d3_geo_mercator.invert = function(x, y) {
4579   return [x, 2 * Math.atan(Math.exp(y)) - halfπ];
4580 };
4581
4582 function d3_geo_mercatorProjection(project) {
4583   var m = d3_geo_projection(project),
4584       scale = m.scale,
4585       translate = m.translate,
4586       clipExtent = m.clipExtent,
4587       clipAuto;
4588
4589   m.scale = function() {
4590     var v = scale.apply(m, arguments);
4591     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4592   };
4593
4594   m.translate = function() {
4595     var v = translate.apply(m, arguments);
4596     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4597   };
4598
4599   m.clipExtent = function(_) {
4600     var v = clipExtent.apply(m, arguments);
4601     if (v === m) {
4602       if (clipAuto = _ == null) {
4603         var k = π * scale(), t = translate();
4604         clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]);
4605       }
4606     } else if (clipAuto) {
4607       v = null;
4608     }
4609     return v;
4610   };
4611
4612   return m.clipExtent(null);
4613 }
4614
4615 (d3.geo.mercator = function() {
4616   return d3_geo_mercatorProjection(d3_geo_mercator);
4617 }).raw = d3_geo_mercator;
4618 d3.geom = {};
4619
4620 d3.geom.polygon = function(coordinates) {
4621   d3_subclass(coordinates, d3_geom_polygonPrototype);
4622   return coordinates;
4623 };
4624
4625 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
4626
4627 d3_geom_polygonPrototype.area = function() {
4628   var i = -1,
4629       n = this.length,
4630       a,
4631       b = this[n - 1],
4632       area = 0;
4633
4634   while (++i < n) {
4635     a = b;
4636     b = this[i];
4637     area += a[1] * b[0] - a[0] * b[1];
4638   }
4639
4640   return area * .5;
4641 };
4642
4643 d3_geom_polygonPrototype.centroid = function(k) {
4644   var i = -1,
4645       n = this.length,
4646       x = 0,
4647       y = 0,
4648       a,
4649       b = this[n - 1],
4650       c;
4651
4652   if (!arguments.length) k = -1 / (6 * this.area());
4653
4654   while (++i < n) {
4655     a = b;
4656     b = this[i];
4657     c = a[0] * b[1] - b[0] * a[1];
4658     x += (a[0] + b[0]) * c;
4659     y += (a[1] + b[1]) * c;
4660   }
4661
4662   return [x * k, y * k];
4663 };
4664
4665 // The Sutherland-Hodgman clipping algorithm.
4666 // Note: requires the clip polygon to be counterclockwise and convex.
4667 d3_geom_polygonPrototype.clip = function(subject) {
4668   var input,
4669       closed = d3_geom_polygonClosed(subject),
4670       i = -1,
4671       n = this.length - d3_geom_polygonClosed(this),
4672       j,
4673       m,
4674       a = this[n - 1],
4675       b,
4676       c,
4677       d;
4678
4679   while (++i < n) {
4680     input = subject.slice();
4681     subject.length = 0;
4682     b = this[i];
4683     c = input[(m = input.length - closed) - 1];
4684     j = -1;
4685     while (++j < m) {
4686       d = input[j];
4687       if (d3_geom_polygonInside(d, a, b)) {
4688         if (!d3_geom_polygonInside(c, a, b)) {
4689           subject.push(d3_geom_polygonIntersect(c, d, a, b));
4690         }
4691         subject.push(d);
4692       } else if (d3_geom_polygonInside(c, a, b)) {
4693         subject.push(d3_geom_polygonIntersect(c, d, a, b));
4694       }
4695       c = d;
4696     }
4697     if (closed) subject.push(subject[0]);
4698     a = b;
4699   }
4700
4701   return subject;
4702 };
4703
4704 function d3_geom_polygonInside(p, a, b) {
4705   return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
4706 }
4707
4708 // Intersect two infinite lines cd and ab.
4709 function d3_geom_polygonIntersect(c, d, a, b) {
4710   var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3,
4711       y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3,
4712       ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
4713   return [x1 + ua * x21, y1 + ua * y21];
4714 }
4715
4716 // Returns true if the polygon is closed.
4717 function d3_geom_polygonClosed(coordinates) {
4718   var a = coordinates[0],
4719       b = coordinates[coordinates.length - 1];
4720   return !(a[0] - b[0] || a[1] - b[1]);
4721 }
4722 function d3_geom_pointX(d) {
4723   return d[0];
4724 }
4725
4726 function d3_geom_pointY(d) {
4727   return d[1];
4728 }
4729
4730 /**
4731  * Computes the 2D convex hull of a set of points using the monotone chain
4732  * algorithm:
4733  * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain)
4734  *
4735  * The runtime of this algorithm is O(n log n), where n is the number of input
4736  * points. However in practice it outperforms other O(n log n) hulls.
4737  *
4738  * @param vertices [[x1, y1], [x2, y2], ...]
4739  * @returns polygon [[x1, y1], [x2, y2], ...]
4740  */
4741 d3.geom.hull = function(vertices) {
4742   var x = d3_geom_pointX,
4743       y = d3_geom_pointY;
4744
4745   if (arguments.length) return hull(vertices);
4746
4747   function hull(data) {
4748     // Hull of < 3 points is not well-defined
4749     if (data.length < 3) return [];
4750
4751     var fx = d3_functor(x),
4752         fy = d3_functor(y),
4753         i,
4754         n = data.length,
4755         points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]]
4756         flippedPoints = [];
4757
4758     for (i = 0 ; i < n; i++) {
4759       points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]);
4760     }
4761
4762     // sort ascending by x-coord first, y-coord second
4763     points.sort(d3_geom_hullOrder);
4764
4765     // we flip bottommost points across y axis so we can use the upper hull routine on both
4766     for (i = 0; i < n; i++) flippedPoints.push([points[i][0], -points[i][1]]);
4767
4768     var upper = d3_geom_hullUpper(points),
4769         lower = d3_geom_hullUpper(flippedPoints);
4770
4771     // construct the polygon, removing possible duplicate endpoints
4772     var skipLeft = lower[0] === upper[0],
4773         skipRight  = lower[lower.length - 1] === upper[upper.length - 1],
4774         polygon = [];
4775
4776     // add upper hull in r->l order
4777     // then add lower hull in l->r order
4778     for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
4779     for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
4780
4781     return polygon;
4782   }
4783
4784   hull.x = function(_) {
4785     return arguments.length ? (x = _, hull) : x;
4786   };
4787
4788   hull.y = function(_) {
4789     return arguments.length ? (y = _, hull) : y;
4790   };
4791
4792   return hull;
4793 };
4794
4795 // finds the 'upper convex hull' (see wiki link above)
4796 // assumes points arg has >=3 elements, is sorted by x, unique in y
4797 // returns array of indices into points in left to right order
4798 function d3_geom_hullUpper(points) {
4799   var n = points.length,
4800       hull = [0, 1],
4801       hs = 2; // hull size
4802
4803   for (var i = 2; i < n; i++) {
4804     while (hs > 1 && d3_cross2d(points[hull[hs-2]], points[hull[hs-1]], points[i]) <= 0) --hs;
4805     hull[hs++] = i;
4806   }
4807
4808   // we slice to make sure that the points we 'popped' from hull don't stay behind
4809   return hull.slice(0, hs);
4810 }
4811
4812 // comparator for ascending sort by x-coord first, y-coord second
4813 function d3_geom_hullOrder(a, b) {
4814   return a[0] - b[0] || a[1] - b[1];
4815 }
4816 // import "../transition/transition";
4817
4818 d3_selectionPrototype.transition = function(name) {
4819   var id = d3_transitionInheritId || ++d3_transitionId,
4820       ns = d3_transitionNamespace(name),
4821       subgroups = [],
4822       subgroup,
4823       node,
4824       transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250};
4825
4826   for (var j = -1, m = this.length; ++j < m;) {
4827     subgroups.push(subgroup = []);
4828     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4829       if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
4830       subgroup.push(node);
4831     }
4832   }
4833
4834   return d3_transition(subgroups, ns, id);
4835 };
4836 // import "../transition/transition";
4837
4838 // TODO Interrupt transitions for all namespaces?
4839 d3_selectionPrototype.interrupt = function(name) {
4840   return this.each(name == null
4841       ? d3_selection_interrupt
4842       : d3_selection_interruptNS(d3_transitionNamespace(name)));
4843 };
4844
4845 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
4846
4847 function d3_selection_interruptNS(ns) {
4848   return function() {
4849     var lock, active;
4850     if ((lock = this[ns]) && (active = lock[lock.active])) {
4851       if (--lock.count) delete lock[lock.active];
4852       else delete this[ns];
4853       lock.active += .5;
4854       active.event && active.event.interrupt.call(this, this.__data__, active.index);
4855     }
4856   };
4857 }
4858
4859 function d3_transition(groups, ns, id) {
4860   d3_subclass(groups, d3_transitionPrototype);
4861
4862   // Note: read-only!
4863   groups.namespace = ns;
4864   groups.id = id;
4865
4866   return groups;
4867 }
4868
4869 var d3_transitionPrototype = [],
4870     d3_transitionId = 0,
4871     d3_transitionInheritId,
4872     d3_transitionInherit;
4873
4874 d3_transitionPrototype.call = d3_selectionPrototype.call;
4875 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
4876 d3_transitionPrototype.node = d3_selectionPrototype.node;
4877 d3_transitionPrototype.size = d3_selectionPrototype.size;
4878
4879 d3.transition = function(selection, name) {
4880   return selection && selection.transition
4881       ? (d3_transitionInheritId ? selection.transition(name) : selection)
4882       : d3.selection().transition(selection);
4883 };
4884
4885 d3.transition.prototype = d3_transitionPrototype;
4886
4887
4888 d3_transitionPrototype.select = function(selector) {
4889   var id = this.id,
4890       ns = this.namespace,
4891       subgroups = [],
4892       subgroup,
4893       subnode,
4894       node;
4895
4896   selector = d3_selection_selector(selector);
4897
4898   for (var j = -1, m = this.length; ++j < m;) {
4899     subgroups.push(subgroup = []);
4900     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4901       if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
4902         if ("__data__" in node) subnode.__data__ = node.__data__;
4903         d3_transitionNode(subnode, i, ns, id, node[ns][id]);
4904         subgroup.push(subnode);
4905       } else {
4906         subgroup.push(null);
4907       }
4908     }
4909   }
4910
4911   return d3_transition(subgroups, ns, id);
4912 };
4913
4914 d3_transitionPrototype.selectAll = function(selector) {
4915   var id = this.id,
4916       ns = this.namespace,
4917       subgroups = [],
4918       subgroup,
4919       subnodes,
4920       node,
4921       subnode,
4922       transition;
4923
4924   selector = d3_selection_selectorAll(selector);
4925
4926   for (var j = -1, m = this.length; ++j < m;) {
4927     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4928       if (node = group[i]) {
4929         transition = node[ns][id];
4930         subnodes = selector.call(node, node.__data__, i, j);
4931         subgroups.push(subgroup = []);
4932         for (var k = -1, o = subnodes.length; ++k < o;) {
4933           if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
4934           subgroup.push(subnode);
4935         }
4936       }
4937     }
4938   }
4939
4940   return d3_transition(subgroups, ns, id);
4941 };
4942
4943 d3_transitionPrototype.filter = function(filter) {
4944   var subgroups = [],
4945       subgroup,
4946       group,
4947       node;
4948
4949   if (typeof filter !== "function") filter = d3_selection_filter(filter);
4950
4951   for (var j = 0, m = this.length; j < m; j++) {
4952     subgroups.push(subgroup = []);
4953     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
4954       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
4955         subgroup.push(node);
4956       }
4957     }
4958   }
4959
4960   return d3_transition(subgroups, this.namespace, this.id);
4961 };
4962 d3.color = d3_color;
4963
4964 function d3_color() {}
4965
4966 d3_color.prototype.toString = function() {
4967   return this.rgb() + "";
4968 };
4969
4970 d3.hsl = d3_hsl;
4971
4972 function d3_hsl(h, s, l) {
4973   return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l)
4974       : arguments.length < 2 ? (h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l)
4975       : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl))
4976       : new d3_hsl(h, s, l);
4977 }
4978
4979 var d3_hslPrototype = d3_hsl.prototype = new d3_color;
4980
4981 d3_hslPrototype.brighter = function(k) {
4982   k = Math.pow(0.7, arguments.length ? k : 1);
4983   return new d3_hsl(this.h, this.s, this.l / k);
4984 };
4985
4986 d3_hslPrototype.darker = function(k) {
4987   k = Math.pow(0.7, arguments.length ? k : 1);
4988   return new d3_hsl(this.h, this.s, k * this.l);
4989 };
4990
4991 d3_hslPrototype.rgb = function() {
4992   return d3_hsl_rgb(this.h, this.s, this.l);
4993 };
4994
4995 function d3_hsl_rgb(h, s, l) {
4996   var m1,
4997       m2;
4998
4999   /* Some simple corrections for h, s and l. */
5000   h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
5001   s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
5002   l = l < 0 ? 0 : l > 1 ? 1 : l;
5003
5004   /* From FvD 13.37, CSS Color Module Level 3 */
5005   m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
5006   m1 = 2 * l - m2;
5007
5008   function v(h) {
5009     if (h > 360) h -= 360;
5010     else if (h < 0) h += 360;
5011     if (h < 60) return m1 + (m2 - m1) * h / 60;
5012     if (h < 180) return m2;
5013     if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
5014     return m1;
5015   }
5016
5017   function vv(h) {
5018     return Math.round(v(h) * 255);
5019   }
5020
5021   return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
5022 }
5023
5024 d3.hcl = d3_hcl;
5025
5026 function d3_hcl(h, c, l) {
5027   return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l)
5028       : arguments.length < 2 ? (h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l)
5029       : (h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b)
5030       : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b)))
5031       : new d3_hcl(h, c, l);
5032 }
5033
5034 var d3_hclPrototype = d3_hcl.prototype = new d3_color;
5035
5036 d3_hclPrototype.brighter = function(k) {
5037   return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
5038 };
5039
5040 d3_hclPrototype.darker = function(k) {
5041   return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
5042 };
5043
5044 d3_hclPrototype.rgb = function() {
5045   return d3_hcl_lab(this.h, this.c, this.l).rgb();
5046 };
5047
5048 function d3_hcl_lab(h, c, l) {
5049   if (isNaN(h)) h = 0;
5050   if (isNaN(c)) c = 0;
5051   return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
5052 }
5053
5054 d3.lab = d3_lab;
5055
5056 function d3_lab(l, a, b) {
5057   return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b)
5058       : arguments.length < 2 ? (l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b)
5059       : (l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l)
5060       : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b)))
5061       : new d3_lab(l, a, b);
5062 }
5063
5064 // Corresponds roughly to RGB brighter/darker
5065 var d3_lab_K = 18;
5066
5067 // D65 standard referent
5068 var d3_lab_X = 0.950470,
5069     d3_lab_Y = 1,
5070     d3_lab_Z = 1.088830;
5071
5072 var d3_labPrototype = d3_lab.prototype = new d3_color;
5073
5074 d3_labPrototype.brighter = function(k) {
5075   return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5076 };
5077
5078 d3_labPrototype.darker = function(k) {
5079   return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5080 };
5081
5082 d3_labPrototype.rgb = function() {
5083   return d3_lab_rgb(this.l, this.a, this.b);
5084 };
5085
5086 function d3_lab_rgb(l, a, b) {
5087   var y = (l + 16) / 116,
5088       x = y + a / 500,
5089       z = y - b / 200;
5090   x = d3_lab_xyz(x) * d3_lab_X;
5091   y = d3_lab_xyz(y) * d3_lab_Y;
5092   z = d3_lab_xyz(z) * d3_lab_Z;
5093   return new d3_rgb(
5094     d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
5095     d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
5096     d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
5097   );
5098 }
5099
5100 function d3_lab_hcl(l, a, b) {
5101   return l > 0
5102       ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l)
5103       : new d3_hcl(NaN, NaN, l);
5104 }
5105
5106 function d3_lab_xyz(x) {
5107   return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
5108 }
5109 function d3_xyz_lab(x) {
5110   return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
5111 }
5112
5113 function d3_xyz_rgb(r) {
5114   return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
5115 }
5116
5117 d3.rgb = d3_rgb;
5118
5119 function d3_rgb(r, g, b) {
5120   return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b)
5121       : arguments.length < 2 ? (r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b)
5122       : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb))
5123       : new d3_rgb(r, g, b);
5124 }
5125
5126 function d3_rgbNumber(value) {
5127   return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff);
5128 }
5129
5130 function d3_rgbString(value) {
5131   return d3_rgbNumber(value) + "";
5132 }
5133
5134 var d3_rgbPrototype = d3_rgb.prototype = new d3_color;
5135
5136 d3_rgbPrototype.brighter = function(k) {
5137   k = Math.pow(0.7, arguments.length ? k : 1);
5138   var r = this.r,
5139       g = this.g,
5140       b = this.b,
5141       i = 30;
5142   if (!r && !g && !b) return new d3_rgb(i, i, i);
5143   if (r && r < i) r = i;
5144   if (g && g < i) g = i;
5145   if (b && b < i) b = i;
5146   return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
5147 };
5148
5149 d3_rgbPrototype.darker = function(k) {
5150   k = Math.pow(0.7, arguments.length ? k : 1);
5151   return new d3_rgb(k * this.r, k * this.g, k * this.b);
5152 };
5153
5154 d3_rgbPrototype.hsl = function() {
5155   return d3_rgb_hsl(this.r, this.g, this.b);
5156 };
5157
5158 d3_rgbPrototype.toString = function() {
5159   return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
5160 };
5161
5162 function d3_rgb_hex(v) {
5163   return v < 0x10
5164       ? "0" + Math.max(0, v).toString(16)
5165       : Math.min(255, v).toString(16);
5166 }
5167
5168 function d3_rgb_parse(format, rgb, hsl) {
5169   var r = 0, // red channel; int in [0, 255]
5170       g = 0, // green channel; int in [0, 255]
5171       b = 0, // blue channel; int in [0, 255]
5172       m1, // CSS color specification match
5173       m2, // CSS color specification type (e.g., rgb)
5174       color;
5175
5176   /* Handle hsl, rgb. */
5177   m1 = /([a-z]+)\((.*)\)/i.exec(format);
5178   if (m1) {
5179     m2 = m1[2].split(",");
5180     switch (m1[1]) {
5181       case "hsl": {
5182         return hsl(
5183           parseFloat(m2[0]), // degrees
5184           parseFloat(m2[1]) / 100, // percentage
5185           parseFloat(m2[2]) / 100 // percentage
5186         );
5187       }
5188       case "rgb": {
5189         return rgb(
5190           d3_rgb_parseNumber(m2[0]),
5191           d3_rgb_parseNumber(m2[1]),
5192           d3_rgb_parseNumber(m2[2])
5193         );
5194       }
5195     }
5196   }
5197
5198   /* Named colors. */
5199   if (color = d3_rgb_names.get(format.toLowerCase())) {
5200     return rgb(color.r, color.g, color.b);
5201   }
5202
5203   /* Hexadecimal colors: #rgb and #rrggbb. */
5204   if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
5205     if (format.length === 4) {
5206       r = (color & 0xf00) >> 4; r = (r >> 4) | r;
5207       g = (color & 0xf0); g = (g >> 4) | g;
5208       b = (color & 0xf); b = (b << 4) | b;
5209     } else if (format.length === 7) {
5210       r = (color & 0xff0000) >> 16;
5211       g = (color & 0xff00) >> 8;
5212       b = (color & 0xff);
5213     }
5214   }
5215
5216   return rgb(r, g, b);
5217 }
5218
5219 function d3_rgb_hsl(r, g, b) {
5220   var min = Math.min(r /= 255, g /= 255, b /= 255),
5221       max = Math.max(r, g, b),
5222       d = max - min,
5223       h,
5224       s,
5225       l = (max + min) / 2;
5226   if (d) {
5227     s = l < .5 ? d / (max + min) : d / (2 - max - min);
5228     if (r == max) h = (g - b) / d + (g < b ? 6 : 0);
5229     else if (g == max) h = (b - r) / d + 2;
5230     else h = (r - g) / d + 4;
5231     h *= 60;
5232   } else {
5233     h = NaN;
5234     s = l > 0 && l < 1 ? 0 : h;
5235   }
5236   return new d3_hsl(h, s, l);
5237 }
5238
5239 function d3_rgb_lab(r, g, b) {
5240   r = d3_rgb_xyz(r);
5241   g = d3_rgb_xyz(g);
5242   b = d3_rgb_xyz(b);
5243   var x = d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / d3_lab_X),
5244       y = d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / d3_lab_Y),
5245       z = d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / d3_lab_Z);
5246   return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
5247 }
5248
5249 function d3_rgb_xyz(r) {
5250   return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
5251 }
5252
5253 function d3_rgb_parseNumber(c) { // either integer or percentage
5254   var f = parseFloat(c);
5255   return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
5256 }
5257
5258 var d3_rgb_names = d3.map({
5259   aliceblue: 0xf0f8ff,
5260   antiquewhite: 0xfaebd7,
5261   aqua: 0x00ffff,
5262   aquamarine: 0x7fffd4,
5263   azure: 0xf0ffff,
5264   beige: 0xf5f5dc,
5265   bisque: 0xffe4c4,
5266   black: 0x000000,
5267   blanchedalmond: 0xffebcd,
5268   blue: 0x0000ff,
5269   blueviolet: 0x8a2be2,
5270   brown: 0xa52a2a,
5271   burlywood: 0xdeb887,
5272   cadetblue: 0x5f9ea0,
5273   chartreuse: 0x7fff00,
5274   chocolate: 0xd2691e,
5275   coral: 0xff7f50,
5276   cornflowerblue: 0x6495ed,
5277   cornsilk: 0xfff8dc,
5278   crimson: 0xdc143c,
5279   cyan: 0x00ffff,
5280   darkblue: 0x00008b,
5281   darkcyan: 0x008b8b,
5282   darkgoldenrod: 0xb8860b,
5283   darkgray: 0xa9a9a9,
5284   darkgreen: 0x006400,
5285   darkgrey: 0xa9a9a9,
5286   darkkhaki: 0xbdb76b,
5287   darkmagenta: 0x8b008b,
5288   darkolivegreen: 0x556b2f,
5289   darkorange: 0xff8c00,
5290   darkorchid: 0x9932cc,
5291   darkred: 0x8b0000,
5292   darksalmon: 0xe9967a,
5293   darkseagreen: 0x8fbc8f,
5294   darkslateblue: 0x483d8b,
5295   darkslategray: 0x2f4f4f,
5296   darkslategrey: 0x2f4f4f,
5297   darkturquoise: 0x00ced1,
5298   darkviolet: 0x9400d3,
5299   deeppink: 0xff1493,
5300   deepskyblue: 0x00bfff,
5301   dimgray: 0x696969,
5302   dimgrey: 0x696969,
5303   dodgerblue: 0x1e90ff,
5304   firebrick: 0xb22222,
5305   floralwhite: 0xfffaf0,
5306   forestgreen: 0x228b22,
5307   fuchsia: 0xff00ff,
5308   gainsboro: 0xdcdcdc,
5309   ghostwhite: 0xf8f8ff,
5310   gold: 0xffd700,
5311   goldenrod: 0xdaa520,
5312   gray: 0x808080,
5313   green: 0x008000,
5314   greenyellow: 0xadff2f,
5315   grey: 0x808080,
5316   honeydew: 0xf0fff0,
5317   hotpink: 0xff69b4,
5318   indianred: 0xcd5c5c,
5319   indigo: 0x4b0082,
5320   ivory: 0xfffff0,
5321   khaki: 0xf0e68c,
5322   lavender: 0xe6e6fa,
5323   lavenderblush: 0xfff0f5,
5324   lawngreen: 0x7cfc00,
5325   lemonchiffon: 0xfffacd,
5326   lightblue: 0xadd8e6,
5327   lightcoral: 0xf08080,
5328   lightcyan: 0xe0ffff,
5329   lightgoldenrodyellow: 0xfafad2,
5330   lightgray: 0xd3d3d3,
5331   lightgreen: 0x90ee90,
5332   lightgrey: 0xd3d3d3,
5333   lightpink: 0xffb6c1,
5334   lightsalmon: 0xffa07a,
5335   lightseagreen: 0x20b2aa,
5336   lightskyblue: 0x87cefa,
5337   lightslategray: 0x778899,
5338   lightslategrey: 0x778899,
5339   lightsteelblue: 0xb0c4de,
5340   lightyellow: 0xffffe0,
5341   lime: 0x00ff00,
5342   limegreen: 0x32cd32,
5343   linen: 0xfaf0e6,
5344   magenta: 0xff00ff,
5345   maroon: 0x800000,
5346   mediumaquamarine: 0x66cdaa,
5347   mediumblue: 0x0000cd,
5348   mediumorchid: 0xba55d3,
5349   mediumpurple: 0x9370db,
5350   mediumseagreen: 0x3cb371,
5351   mediumslateblue: 0x7b68ee,
5352   mediumspringgreen: 0x00fa9a,
5353   mediumturquoise: 0x48d1cc,
5354   mediumvioletred: 0xc71585,
5355   midnightblue: 0x191970,
5356   mintcream: 0xf5fffa,
5357   mistyrose: 0xffe4e1,
5358   moccasin: 0xffe4b5,
5359   navajowhite: 0xffdead,
5360   navy: 0x000080,
5361   oldlace: 0xfdf5e6,
5362   olive: 0x808000,
5363   olivedrab: 0x6b8e23,
5364   orange: 0xffa500,
5365   orangered: 0xff4500,
5366   orchid: 0xda70d6,
5367   palegoldenrod: 0xeee8aa,
5368   palegreen: 0x98fb98,
5369   paleturquoise: 0xafeeee,
5370   palevioletred: 0xdb7093,
5371   papayawhip: 0xffefd5,
5372   peachpuff: 0xffdab9,
5373   peru: 0xcd853f,
5374   pink: 0xffc0cb,
5375   plum: 0xdda0dd,
5376   powderblue: 0xb0e0e6,
5377   purple: 0x800080,
5378   rebeccapurple: 0x663399,
5379   red: 0xff0000,
5380   rosybrown: 0xbc8f8f,
5381   royalblue: 0x4169e1,
5382   saddlebrown: 0x8b4513,
5383   salmon: 0xfa8072,
5384   sandybrown: 0xf4a460,
5385   seagreen: 0x2e8b57,
5386   seashell: 0xfff5ee,
5387   sienna: 0xa0522d,
5388   silver: 0xc0c0c0,
5389   skyblue: 0x87ceeb,
5390   slateblue: 0x6a5acd,
5391   slategray: 0x708090,
5392   slategrey: 0x708090,
5393   snow: 0xfffafa,
5394   springgreen: 0x00ff7f,
5395   steelblue: 0x4682b4,
5396   tan: 0xd2b48c,
5397   teal: 0x008080,
5398   thistle: 0xd8bfd8,
5399   tomato: 0xff6347,
5400   turquoise: 0x40e0d0,
5401   violet: 0xee82ee,
5402   wheat: 0xf5deb3,
5403   white: 0xffffff,
5404   whitesmoke: 0xf5f5f5,
5405   yellow: 0xffff00,
5406   yellowgreen: 0x9acd32
5407 });
5408
5409 d3_rgb_names.forEach(function(key, value) {
5410   d3_rgb_names.set(key, d3_rgbNumber(value));
5411 });
5412
5413 d3.interpolateRgb = d3_interpolateRgb;
5414
5415 function d3_interpolateRgb(a, b) {
5416   a = d3.rgb(a);
5417   b = d3.rgb(b);
5418   var ar = a.r,
5419       ag = a.g,
5420       ab = a.b,
5421       br = b.r - ar,
5422       bg = b.g - ag,
5423       bb = b.b - ab;
5424   return function(t) {
5425     return "#"
5426         + d3_rgb_hex(Math.round(ar + br * t))
5427         + d3_rgb_hex(Math.round(ag + bg * t))
5428         + d3_rgb_hex(Math.round(ab + bb * t));
5429   };
5430 }
5431
5432 d3.interpolateObject = d3_interpolateObject;
5433
5434 function d3_interpolateObject(a, b) {
5435   var i = {},
5436       c = {},
5437       k;
5438   for (k in a) {
5439     if (k in b) {
5440       i[k] = d3_interpolate(a[k], b[k]);
5441     } else {
5442       c[k] = a[k];
5443     }
5444   }
5445   for (k in b) {
5446     if (!(k in a)) {
5447       c[k] = b[k];
5448     }
5449   }
5450   return function(t) {
5451     for (k in i) c[k] = i[k](t);
5452     return c;
5453   };
5454 }
5455
5456 d3.interpolateArray = d3_interpolateArray;
5457
5458 function d3_interpolateArray(a, b) {
5459   var x = [],
5460       c = [],
5461       na = a.length,
5462       nb = b.length,
5463       n0 = Math.min(a.length, b.length),
5464       i;
5465   for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
5466   for (; i < na; ++i) c[i] = a[i];
5467   for (; i < nb; ++i) c[i] = b[i];
5468   return function(t) {
5469     for (i = 0; i < n0; ++i) c[i] = x[i](t);
5470     return c;
5471   };
5472 }
5473 d3.interpolateNumber = d3_interpolateNumber;
5474
5475 function d3_interpolateNumber(a, b) {
5476   a = +a, b = +b;
5477   return function(t) { return a * (1 - t) + b * t; };
5478 }
5479
5480 d3.interpolateString = d3_interpolateString;
5481
5482 function d3_interpolateString(a, b) {
5483   var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b
5484       am, // current match in a
5485       bm, // current match in b
5486       bs, // string preceding current number in b, if any
5487       i = -1, // index in s
5488       s = [], // string constants and placeholders
5489       q = []; // number interpolators
5490
5491   // Coerce inputs to strings.
5492   a = a + "", b = b + "";
5493
5494   // Interpolate pairs of numbers in a & b.
5495   while ((am = d3_interpolate_numberA.exec(a))
5496       && (bm = d3_interpolate_numberB.exec(b))) {
5497     if ((bs = bm.index) > bi) { // a string precedes the next number in b
5498       bs = b.slice(bi, bs);
5499       if (s[i]) s[i] += bs; // coalesce with previous string
5500       else s[++i] = bs;
5501     }
5502     if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
5503       if (s[i]) s[i] += bm; // coalesce with previous string
5504       else s[++i] = bm;
5505     } else { // interpolate non-matching numbers
5506       s[++i] = null;
5507       q.push({i: i, x: d3_interpolateNumber(am, bm)});
5508     }
5509     bi = d3_interpolate_numberB.lastIndex;
5510   }
5511
5512   // Add remains of b.
5513   if (bi < b.length) {
5514     bs = b.slice(bi);
5515     if (s[i]) s[i] += bs; // coalesce with previous string
5516     else s[++i] = bs;
5517   }
5518
5519   // Special optimization for only a single match.
5520   // Otherwise, interpolate each of the numbers and rejoin the string.
5521   return s.length < 2
5522       ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; })
5523       : function() { return b; })
5524       : (b = q.length, function(t) {
5525           for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
5526           return s.join("");
5527         });
5528 }
5529
5530 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
5531     d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
5532
5533 d3.interpolate = d3_interpolate;
5534
5535 function d3_interpolate(a, b) {
5536   var i = d3.interpolators.length, f;
5537   while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
5538   return f;
5539 }
5540
5541 d3.interpolators = [
5542   function(a, b) {
5543     var t = typeof b;
5544     return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString)
5545         : b instanceof d3_color ? d3_interpolateRgb
5546         : Array.isArray(b) ? d3_interpolateArray
5547         : t === "object" && isNaN(b) ? d3_interpolateObject
5548         : d3_interpolateNumber)(a, b);
5549   }
5550 ];
5551
5552 d3.transform = function(string) {
5553   var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
5554   return (d3.transform = function(string) {
5555     if (string != null) {
5556       g.setAttribute("transform", string);
5557       var t = g.transform.baseVal.consolidate();
5558     }
5559     return new d3_transform(t ? t.matrix : d3_transformIdentity);
5560   })(string);
5561 };
5562
5563 // Compute x-scale and normalize the first row.
5564 // Compute shear and make second row orthogonal to first.
5565 // Compute y-scale and normalize the second row.
5566 // Finally, compute the rotation.
5567 function d3_transform(m) {
5568   var r0 = [m.a, m.b],
5569       r1 = [m.c, m.d],
5570       kx = d3_transformNormalize(r0),
5571       kz = d3_transformDot(r0, r1),
5572       ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
5573   if (r0[0] * r1[1] < r1[0] * r0[1]) {
5574     r0[0] *= -1;
5575     r0[1] *= -1;
5576     kx *= -1;
5577     kz *= -1;
5578   }
5579   this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
5580   this.translate = [m.e, m.f];
5581   this.scale = [kx, ky];
5582   this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
5583 };
5584
5585 d3_transform.prototype.toString = function() {
5586   return "translate(" + this.translate
5587       + ")rotate(" + this.rotate
5588       + ")skewX(" + this.skew
5589       + ")scale(" + this.scale
5590       + ")";
5591 };
5592
5593 function d3_transformDot(a, b) {
5594   return a[0] * b[0] + a[1] * b[1];
5595 }
5596
5597 function d3_transformNormalize(a) {
5598   var k = Math.sqrt(d3_transformDot(a, a));
5599   if (k) {
5600     a[0] /= k;
5601     a[1] /= k;
5602   }
5603   return k;
5604 }
5605
5606 function d3_transformCombine(a, b, k) {
5607   a[0] += k * b[0];
5608   a[1] += k * b[1];
5609   return a;
5610 }
5611
5612 var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
5613
5614 d3.interpolateTransform = d3_interpolateTransform;
5615
5616 function d3_interpolateTransform(a, b) {
5617   var s = [], // string constants and placeholders
5618       q = [], // number interpolators
5619       n,
5620       A = d3.transform(a),
5621       B = d3.transform(b),
5622       ta = A.translate,
5623       tb = B.translate,
5624       ra = A.rotate,
5625       rb = B.rotate,
5626       wa = A.skew,
5627       wb = B.skew,
5628       ka = A.scale,
5629       kb = B.scale;
5630
5631   if (ta[0] != tb[0] || ta[1] != tb[1]) {
5632     s.push("translate(", null, ",", null, ")");
5633     q.push({i: 1, x: d3_interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3_interpolateNumber(ta[1], tb[1])});
5634   } else if (tb[0] || tb[1]) {
5635     s.push("translate(" + tb + ")");
5636   } else {
5637     s.push("");
5638   }
5639
5640   if (ra != rb) {
5641     if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
5642     q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb)});
5643   } else if (rb) {
5644     s.push(s.pop() + "rotate(" + rb + ")");
5645   }
5646
5647   if (wa != wb) {
5648     q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)});
5649   } else if (wb) {
5650     s.push(s.pop() + "skewX(" + wb + ")");
5651   }
5652
5653   if (ka[0] != kb[0] || ka[1] != kb[1]) {
5654     n = s.push(s.pop() + "scale(", null, ",", null, ")");
5655     q.push({i: n - 4, x: d3_interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3_interpolateNumber(ka[1], kb[1])});
5656   } else if (kb[0] != 1 || kb[1] != 1) {
5657     s.push(s.pop() + "scale(" + kb + ")");
5658   }
5659
5660   n = q.length;
5661   return function(t) {
5662     var i = -1, o;
5663     while (++i < n) s[(o = q[i]).i] = o.x(t);
5664     return s.join("");
5665   };
5666 }
5667
5668 d3_transitionPrototype.tween = function(name, tween) {
5669   var id = this.id, ns = this.namespace;
5670   if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
5671   return d3_selection_each(this, tween == null
5672         ? function(node) { node[ns][id].tween.remove(name); }
5673         : function(node) { node[ns][id].tween.set(name, tween); });
5674 };
5675
5676 function d3_transition_tween(groups, name, value, tween) {
5677   var id = groups.id, ns = groups.namespace;
5678   return d3_selection_each(groups, typeof value === "function"
5679       ? function(node, i, j) { node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); }
5680       : (value = tween(value), function(node) { node[ns][id].tween.set(name, value); }));
5681 }
5682
5683 d3_transitionPrototype.attr = function(nameNS, value) {
5684   if (arguments.length < 2) {
5685
5686     // For attr(object), the object specifies the names and values of the
5687     // attributes to transition. The values may be functions that are
5688     // evaluated for each element.
5689     for (value in nameNS) this.attr(value, nameNS[value]);
5690     return this;
5691   }
5692
5693   var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
5694       name = d3.ns.qualify(nameNS);
5695
5696   // For attr(string, null), remove the attribute with the specified name.
5697   function attrNull() {
5698     this.removeAttribute(name);
5699   }
5700   function attrNullNS() {
5701     this.removeAttributeNS(name.space, name.local);
5702   }
5703
5704   // For attr(string, string), set the attribute with the specified name.
5705   function attrTween(b) {
5706     return b == null ? attrNull : (b += "", function() {
5707       var a = this.getAttribute(name), i;
5708       return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
5709     });
5710   }
5711   function attrTweenNS(b) {
5712     return b == null ? attrNullNS : (b += "", function() {
5713       var a = this.getAttributeNS(name.space, name.local), i;
5714       return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
5715     });
5716   }
5717
5718   return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
5719 };
5720
5721 d3_transitionPrototype.attrTween = function(nameNS, tween) {
5722   var name = d3.ns.qualify(nameNS);
5723
5724   function attrTween(d, i) {
5725     var f = tween.call(this, d, i, this.getAttribute(name));
5726     return f && function(t) { this.setAttribute(name, f(t)); };
5727   }
5728   function attrTweenNS(d, i) {
5729     var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
5730     return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
5731   }
5732
5733   return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
5734 };
5735
5736 d3_transitionPrototype.style = function(name, value, priority) {
5737   var n = arguments.length;
5738   if (n < 3) {
5739
5740     // For style(object) or style(object, string), the object specifies the
5741     // names and values of the attributes to set or remove. The values may be
5742     // functions that are evaluated for each element. The optional string
5743     // specifies the priority.
5744     if (typeof name !== "string") {
5745       if (n < 2) value = "";
5746       for (priority in name) this.style(priority, name[priority], value);
5747       return this;
5748     }
5749
5750     // For style(string, string) or style(string, function), use the default
5751     // priority. The priority is ignored for style(string, null).
5752     priority = "";
5753   }
5754
5755   // For style(name, null) or style(name, null, priority), remove the style
5756   // property with the specified name. The priority is ignored.
5757   function styleNull() {
5758     this.style.removeProperty(name);
5759   }
5760
5761   // For style(name, string) or style(name, string, priority), set the style
5762   // property with the specified name, using the specified priority.
5763   // Otherwise, a name, value and priority are specified, and handled as below.
5764   function styleString(b) {
5765     return b == null ? styleNull : (b += "", function() {
5766       var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
5767       return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
5768     });
5769   }
5770
5771   return d3_transition_tween(this, "style." + name, value, styleString);
5772 };
5773
5774 d3_transitionPrototype.styleTween = function(name, tween, priority) {
5775   if (arguments.length < 3) priority = "";
5776
5777   function styleTween(d, i) {
5778     var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
5779     return f && function(t) { this.style.setProperty(name, f(t), priority); };
5780   }
5781
5782   return this.tween("style." + name, styleTween);
5783 };
5784
5785 d3_transitionPrototype.text = function(value) {
5786   return d3_transition_tween(this, "text", value, d3_transition_text);
5787 };
5788
5789 function d3_transition_text(b) {
5790   if (b == null) b = "";
5791   return function() { this.textContent = b; };
5792 }
5793
5794 d3_transitionPrototype.remove = function() {
5795   var ns = this.namespace;
5796   return this.each("end.transition", function() {
5797     var p;
5798     if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
5799   });
5800 };
5801
5802 var d3_ease_default = function() { return d3_identity; };
5803
5804 var d3_ease = d3.map({
5805   linear: d3_ease_default,
5806   poly: d3_ease_poly,
5807   quad: function() { return d3_ease_quad; },
5808   cubic: function() { return d3_ease_cubic; },
5809   sin: function() { return d3_ease_sin; },
5810   exp: function() { return d3_ease_exp; },
5811   circle: function() { return d3_ease_circle; },
5812   elastic: d3_ease_elastic,
5813   back: d3_ease_back,
5814   bounce: function() { return d3_ease_bounce; }
5815 });
5816
5817 var d3_ease_mode = d3.map({
5818   "in": d3_identity,
5819   "out": d3_ease_reverse,
5820   "in-out": d3_ease_reflect,
5821   "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); }
5822 });
5823
5824 d3.ease = function(name) {
5825   var i = name.indexOf("-"),
5826       t = i >= 0 ? name.slice(0, i) : name,
5827       m = i >= 0 ? name.slice(i + 1) : "in";
5828   t = d3_ease.get(t) || d3_ease_default;
5829   m = d3_ease_mode.get(m) || d3_identity;
5830   return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
5831 };
5832
5833 function d3_ease_clamp(f) {
5834   return function(t) {
5835     return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
5836   };
5837 }
5838
5839 function d3_ease_reverse(f) {
5840   return function(t) {
5841     return 1 - f(1 - t);
5842   };
5843 }
5844
5845 function d3_ease_reflect(f) {
5846   return function(t) {
5847     return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
5848   };
5849 }
5850
5851 function d3_ease_quad(t) {
5852   return t * t;
5853 }
5854
5855 function d3_ease_cubic(t) {
5856   return t * t * t;
5857 }
5858
5859 // Optimized clamp(reflect(poly(3))).
5860 function d3_ease_cubicInOut(t) {
5861   if (t <= 0) return 0;
5862   if (t >= 1) return 1;
5863   var t2 = t * t, t3 = t2 * t;
5864   return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
5865 }
5866
5867 function d3_ease_poly(e) {
5868   return function(t) {
5869     return Math.pow(t, e);
5870   };
5871 }
5872
5873 function d3_ease_sin(t) {
5874   return 1 - Math.cos(t * halfπ);
5875 }
5876
5877 function d3_ease_exp(t) {
5878   return Math.pow(2, 10 * (t - 1));
5879 }
5880
5881 function d3_ease_circle(t) {
5882   return 1 - Math.sqrt(1 - t * t);
5883 }
5884
5885 function d3_ease_elastic(a, p) {
5886   var s;
5887   if (arguments.length < 2) p = 0.45;
5888   if (arguments.length) s = p / τ * Math.asin(1 / a);
5889   else a = 1, s = p / 4;
5890   return function(t) {
5891     return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
5892   };
5893 }
5894
5895 function d3_ease_back(s) {
5896   if (!s) s = 1.70158;
5897   return function(t) {
5898     return t * t * ((s + 1) * t - s);
5899   };
5900 }
5901
5902 function d3_ease_bounce(t) {
5903   return t < 1 / 2.75 ? 7.5625 * t * t
5904       : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75
5905       : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375
5906       : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
5907 }
5908
5909 d3_transitionPrototype.ease = function(value) {
5910   var id = this.id, ns = this.namespace;
5911   if (arguments.length < 1) return this.node()[ns][id].ease;
5912   if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
5913   return d3_selection_each(this, function(node) { node[ns][id].ease = value; });
5914 };
5915
5916 d3_transitionPrototype.delay = function(value) {
5917   var id = this.id, ns = this.namespace;
5918   if (arguments.length < 1) return this.node()[ns][id].delay;
5919   return d3_selection_each(this, typeof value === "function"
5920       ? function(node, i, j) { node[ns][id].delay = +value.call(node, node.__data__, i, j); }
5921       : (value = +value, function(node) { node[ns][id].delay = value; }));
5922 };
5923
5924 d3_transitionPrototype.duration = function(value) {
5925   var id = this.id, ns = this.namespace;
5926   if (arguments.length < 1) return this.node()[ns][id].duration;
5927   return d3_selection_each(this, typeof value === "function"
5928       ? function(node, i, j) { node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); }
5929       : (value = Math.max(1, value), function(node) { node[ns][id].duration = value; }));
5930 };
5931
5932 d3_transitionPrototype.each = function(type, listener) {
5933   var id = this.id, ns = this.namespace;
5934   if (arguments.length < 2) {
5935     var inherit = d3_transitionInherit,
5936         inheritId = d3_transitionInheritId;
5937     try {
5938       d3_transitionInheritId = id;
5939       d3_selection_each(this, function(node, i, j) {
5940         d3_transitionInherit = node[ns][id];
5941         type.call(node, node.__data__, i, j);
5942       });
5943     } finally {
5944       d3_transitionInherit = inherit;
5945       d3_transitionInheritId = inheritId;
5946     }
5947   } else {
5948     d3_selection_each(this, function(node) {
5949       var transition = node[ns][id];
5950       (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
5951     });
5952   }
5953   return this;
5954 };
5955
5956 d3_transitionPrototype.transition = function() {
5957   var id0 = this.id,
5958       id1 = ++d3_transitionId,
5959       ns = this.namespace,
5960       subgroups = [],
5961       subgroup,
5962       group,
5963       node,
5964       transition;
5965
5966   for (var j = 0, m = this.length; j < m; j++) {
5967     subgroups.push(subgroup = []);
5968     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
5969       if (node = group[i]) {
5970         transition = node[ns][id0];
5971         d3_transitionNode(node, i, ns, id1, {time: transition.time, ease: transition.ease, delay: transition.delay + transition.duration, duration: transition.duration});
5972       }
5973       subgroup.push(node);
5974     }
5975   }
5976
5977   return d3_transition(subgroups, ns, id1);
5978 };
5979
5980 function d3_transitionNamespace(name) {
5981   return name == null ? "__transition__" : "__transition_" + name + "__";
5982 }
5983
5984 function d3_transitionNode(node, i, ns, id, inherit) {
5985   var lock = node[ns] || (node[ns] = {active: 0, count: 0}),
5986       transition = lock[id];
5987
5988   if (!transition) {
5989     var time = inherit.time;
5990
5991     transition = lock[id] = {
5992       tween: new d3_Map,
5993       time: time,
5994       delay: inherit.delay,
5995       duration: inherit.duration,
5996       ease: inherit.ease,
5997       index: i
5998     };
5999
6000     inherit = null; // allow gc
6001
6002     ++lock.count;
6003
6004     d3.timer(function(elapsed) {
6005       var delay = transition.delay,
6006           duration,
6007           ease,
6008           timer = d3_timer_active,
6009           tweened = [];
6010
6011       timer.t = delay + time;
6012       if (delay <= elapsed) return start(elapsed - delay);
6013       timer.c = start;
6014
6015       function start(elapsed) {
6016         if (lock.active > id) return stop();
6017
6018         var active = lock[lock.active];
6019         if (active) {
6020           --lock.count;
6021           delete lock[lock.active];
6022           active.event && active.event.interrupt.call(node, node.__data__, active.index);
6023         }
6024
6025         lock.active = id;
6026
6027         transition.event && transition.event.start.call(node, node.__data__, i);
6028
6029         transition.tween.forEach(function(key, value) {
6030           if (value = value.call(node, node.__data__, i)) {
6031             tweened.push(value);
6032           }
6033         });
6034
6035         // Deferred capture to allow tweens to initialize ease & duration.
6036         ease = transition.ease;
6037         duration = transition.duration;
6038
6039         d3.timer(function() { // defer to end of current frame
6040           timer.c = tick(elapsed || 1) ? d3_true : tick;
6041           return 1;
6042         }, 0, time);
6043       }
6044
6045       function tick(elapsed) {
6046         if (lock.active !== id) return 1;
6047
6048         var t = elapsed / duration,
6049             e = ease(t),
6050             n = tweened.length;
6051
6052         while (n > 0) {
6053           tweened[--n].call(node, e);
6054         }
6055
6056         if (t >= 1) {
6057           transition.event && transition.event.end.call(node, node.__data__, i);
6058           return stop();
6059         }
6060       }
6061
6062       function stop() {
6063         if (--lock.count) delete lock[id];
6064         else delete node[ns];
6065         return 1;
6066       }
6067     }, 0, time);
6068   }
6069 }
6070
6071 d3.xhr = d3_xhrType(d3_identity);
6072
6073 function d3_xhrType(response) {
6074   return function(url, mimeType, callback) {
6075     if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null;
6076     return d3_xhr(url, mimeType, response, callback);
6077   };
6078 }
6079
6080 function d3_xhr(url, mimeType, response, callback) {
6081   var xhr = {},
6082       dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
6083       headers = {},
6084       request = new XMLHttpRequest,
6085       responseType = null;
6086
6087   // If IE does not support CORS, use XDomainRequest.
6088   if (this.XDomainRequest
6089       && !("withCredentials" in request)
6090       && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest;
6091
6092   "onload" in request
6093       ? request.onload = request.onerror = respond
6094       : request.onreadystatechange = function() { request.readyState > 3 && respond(); };
6095
6096   function respond() {
6097     var status = request.status, result;
6098     if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
6099       try {
6100         result = response.call(xhr, request);
6101       } catch (e) {
6102         dispatch.error.call(xhr, e);
6103         return;
6104       }
6105       dispatch.load.call(xhr, result);
6106     } else {
6107       dispatch.error.call(xhr, request);
6108     }
6109   }
6110
6111   request.onprogress = function(event) {
6112     var o = d3.event;
6113     d3.event = event;
6114     try { dispatch.progress.call(xhr, request); }
6115     finally { d3.event = o; }
6116   };
6117
6118   xhr.header = function(name, value) {
6119     name = (name + "").toLowerCase();
6120     if (arguments.length < 2) return headers[name];
6121     if (value == null) delete headers[name];
6122     else headers[name] = value + "";
6123     return xhr;
6124   };
6125
6126   // If mimeType is non-null and no Accept header is set, a default is used.
6127   xhr.mimeType = function(value) {
6128     if (!arguments.length) return mimeType;
6129     mimeType = value == null ? null : value + "";
6130     return xhr;
6131   };
6132
6133   // Specifies what type the response value should take;
6134   // for instance, arraybuffer, blob, document, or text.
6135   xhr.responseType = function(value) {
6136     if (!arguments.length) return responseType;
6137     responseType = value;
6138     return xhr;
6139   };
6140
6141   // Specify how to convert the response content to a specific type;
6142   // changes the callback value on "load" events.
6143   xhr.response = function(value) {
6144     response = value;
6145     return xhr;
6146   };
6147
6148   // Convenience methods.
6149   ["get", "post"].forEach(function(method) {
6150     xhr[method] = function() {
6151       return xhr.send.apply(xhr, [method].concat(d3_array(arguments)));
6152     };
6153   });
6154
6155   // If callback is non-null, it will be used for error and load events.
6156   xhr.send = function(method, data, callback) {
6157     if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
6158     request.open(method, url, true);
6159     if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
6160     if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
6161     if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
6162     if (responseType != null) request.responseType = responseType;
6163     if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
6164     dispatch.beforesend.call(xhr, request);
6165     request.send(data == null ? null : data);
6166     return xhr;
6167   };
6168
6169   xhr.abort = function() {
6170     request.abort();
6171     return xhr;
6172   };
6173
6174   d3.rebind(xhr, dispatch, "on");
6175
6176   return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
6177 };
6178
6179 function d3_xhr_fixCallback(callback) {
6180   return callback.length === 1
6181       ? function(error, request) { callback(error == null ? request : null); }
6182       : callback;
6183 }
6184
6185 function d3_xhrHasResponse(request) {
6186   var type = request.responseType;
6187   return type && type !== "text"
6188       ? request.response // null on error
6189       : request.responseText; // "" on error
6190 }
6191
6192 d3.text = d3_xhrType(function(request) {
6193   return request.responseText;
6194 });
6195
6196 d3.json = function(url, callback) {
6197   return d3_xhr(url, "application/json", d3_json, callback);
6198 };
6199
6200 function d3_json(request) {
6201   return JSON.parse(request.responseText);
6202 }
6203
6204 d3.html = function(url, callback) {
6205   return d3_xhr(url, "text/html", d3_html, callback);
6206 };
6207
6208 function d3_html(request) {
6209   var range = d3_document.createRange();
6210   range.selectNode(d3_document.body);
6211   return range.createContextualFragment(request.responseText);
6212 }
6213
6214 d3.xml = d3_xhrType(function(request) {
6215   return request.responseXML;
6216 });
6217   if (typeof define === "function" && define.amd) define(d3);
6218   else if (typeof module === "object" && module.exports) module.exports = d3;
6219   this.d3 = d3;
6220 }();
6221 d3.combobox = function() {
6222     var event = d3.dispatch('accept'),
6223         data = [],
6224         suggestions = [],
6225         minItems = 2;
6226
6227     var fetcher = function(val, cb) {
6228         cb(data.filter(function(d) {
6229             return d.value
6230                 .toString()
6231                 .toLowerCase()
6232                 .indexOf(val.toLowerCase()) !== -1;
6233         }));
6234     };
6235
6236     var combobox = function(input) {
6237         var idx = -1,
6238             container = d3.select(document.body)
6239                 .selectAll('div.combobox')
6240                 .filter(function(d) { return d === input.node(); }),
6241             shown = !container.empty();
6242
6243         input
6244             .classed('combobox-input', true)
6245             .on('focus.typeahead', focus)
6246             .on('blur.typeahead', blur)
6247             .on('keydown.typeahead', keydown)
6248             .on('keyup.typeahead', keyup)
6249             .on('input.typeahead', change)
6250             .each(function() {
6251                 var parent = this.parentNode,
6252                     sibling = this.nextSibling;
6253
6254                 var caret = d3.select(parent).selectAll('.combobox-caret')
6255                     .filter(function(d) { return d === input.node(); })
6256                     .data([input.node()]);
6257
6258                 caret.enter().insert('div', function() { return sibling; })
6259                     .attr('class', 'combobox-caret');
6260
6261                 caret
6262                     .on('mousedown', function () {
6263                         // prevent the form element from blurring. it blurs
6264                         // on mousedown
6265                         d3.event.stopPropagation();
6266                         d3.event.preventDefault();
6267                         if (!shown) {
6268                             input.node().focus();
6269                             fetch('', render);
6270                         } else {
6271                             hide();
6272                         }
6273                     });
6274             });
6275
6276         function focus() {
6277             fetch(value(), render);
6278         }
6279
6280         function blur() {
6281             window.setTimeout(hide, 150);
6282         }
6283
6284         function show() {
6285             if (!shown) {
6286                 container = d3.select(document.body)
6287                     .insert('div', ':first-child')
6288                     .datum(input.node())
6289                     .attr('class', 'combobox')
6290                     .style({
6291                         position: 'absolute',
6292                         display: 'block',
6293                         left: '0px'
6294                     })
6295                     .on('mousedown', function () {
6296                         // prevent moving focus out of the text field
6297                         d3.event.preventDefault();
6298                     });
6299
6300                 d3.select(document.body)
6301                     .on('scroll.combobox', render, true);
6302
6303                 shown = true;
6304             }
6305         }
6306
6307         function hide() {
6308             if (shown) {
6309                 idx = -1;
6310                 container.remove();
6311
6312                 d3.select(document.body)
6313                     .on('scroll.combobox', null);
6314
6315                 shown = false;
6316             }
6317         }
6318
6319         function keydown() {
6320            switch (d3.event.keyCode) {
6321                // backspace, delete
6322                case 8:
6323                case 46:
6324                    input.on('input.typeahead', function() {
6325                        idx = -1;
6326                        render();
6327                        var start = input.property('selectionStart');
6328                        input.node().setSelectionRange(start, start);
6329                        input.on('input.typeahead', change);
6330                    });
6331                    break;
6332                // tab
6333                case 9:
6334                    container.selectAll('a.selected').each(event.accept);
6335                    break;
6336                // return
6337                case 13:
6338                    d3.event.preventDefault();
6339                    break;
6340                // up arrow
6341                case 38:
6342                    nav(-1);
6343                    d3.event.preventDefault();
6344                    break;
6345                // down arrow
6346                case 40:
6347                    nav(+1);
6348                    d3.event.preventDefault();
6349                    break;
6350            }
6351            d3.event.stopPropagation();
6352         }
6353
6354         function keyup() {
6355             switch (d3.event.keyCode) {
6356                 // escape
6357                 case 27:
6358                     hide();
6359                     break;
6360                 // return
6361                 case 13:
6362                     container.selectAll('a.selected').each(event.accept);
6363                     hide();
6364                     break;
6365             }
6366         }
6367
6368         function change() {
6369             fetch(value(), function() {
6370                 autocomplete();
6371                 render();
6372             });
6373         }
6374
6375         function nav(dir) {
6376             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6377             input.property('value', suggestions[idx].value);
6378             render();
6379             ensureVisible();
6380         }
6381
6382         function value() {
6383             var value = input.property('value'),
6384                 start = input.property('selectionStart'),
6385                 end = input.property('selectionEnd');
6386
6387             if (start && end) {
6388                 value = value.substring(0, start);
6389             }
6390
6391             return value;
6392         }
6393
6394         function fetch(v, cb) {
6395             fetcher.call(input, v, function(_) {
6396                 suggestions = _;
6397                 cb();
6398             });
6399         }
6400
6401         function autocomplete() {
6402             var v = value();
6403
6404             idx = -1;
6405
6406             if (!v) return;
6407
6408             for (var i = 0; i < suggestions.length; i++) {
6409                 if (suggestions[i].value.toLowerCase().indexOf(v.toLowerCase()) === 0) {
6410                     var completion = suggestions[i].value;
6411                     idx = i;
6412                     input.property('value', completion);
6413                     input.node().setSelectionRange(v.length, completion.length);
6414                     return;
6415                 }
6416             }
6417         }
6418
6419         function render() {
6420             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6421                 show();
6422             } else {
6423                 hide();
6424                 return;
6425             }
6426
6427             var options = container
6428                 .selectAll('a.combobox-option')
6429                 .data(suggestions, function(d) { return d.value; });
6430
6431             options.enter().append('a')
6432                 .attr('class', 'combobox-option')
6433                 .text(function(d) { return d.value; });
6434
6435             options
6436                 .attr('title', function(d) { return d.title; })
6437                 .classed('selected', function(d, i) { return i == idx; })
6438                 .on('mouseover', select)
6439                 .on('click', accept)
6440                 .order();
6441
6442             options.exit()
6443                 .remove();
6444
6445             var rect = input.node().getBoundingClientRect();
6446
6447             container.style({
6448                 'left': rect.left + 'px',
6449                 'width': rect.width + 'px',
6450                 'top': rect.height + rect.top + 'px'
6451             });
6452         }
6453
6454         function select(d, i) {
6455             idx = i;
6456             render();
6457         }
6458
6459         function ensureVisible() {
6460             var node = container.selectAll('a.selected').node();
6461             if (node) node.scrollIntoView();
6462         }
6463
6464         function accept(d) {
6465             if (!shown) return;
6466             input
6467                 .property('value', d.value)
6468                 .trigger('change');
6469             event.accept(d);
6470             hide();
6471         }
6472     };
6473
6474     combobox.fetcher = function(_) {
6475         if (!arguments.length) return fetcher;
6476         fetcher = _;
6477         return combobox;
6478     };
6479
6480     combobox.data = function(_) {
6481         if (!arguments.length) return data;
6482         data = _;
6483         return combobox;
6484     };
6485
6486     combobox.minItems = function(_) {
6487         if (!arguments.length) return minItems;
6488         minItems = _;
6489         return combobox;
6490     };
6491
6492     return d3.rebind(combobox, event, 'on');
6493 };
6494
6495 d3.combobox.off = function(input) {
6496     data = null;
6497     fetcher = null;
6498
6499     input
6500         .on('focus.typeahead', null)
6501         .on('blur.typeahead', null)
6502         .on('keydown.typeahead', null)
6503         .on('keyup.typeahead', null)
6504         .on('input.typeahead', null)
6505         .each(function() {
6506             d3.select(this.parentNode).selectAll('.combobox-caret')
6507                 .filter(function(d) { return d === input.node(); })
6508                 .on('mousedown', null);
6509         });
6510
6511     d3.select(document.body)
6512         .on('scroll.combobox', null);
6513 };
6514 d3.geo.tile = function() {
6515   var size = [960, 500],
6516       scale = 256,
6517       scaleExtent = [0, 20],
6518       translate = [size[0] / 2, size[1] / 2],
6519       zoomDelta = 0;
6520
6521   function bound(_) {
6522       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6523   }
6524
6525   function tile() {
6526     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6527         z0 = bound(Math.round(z + zoomDelta)),
6528         k = Math.pow(2, z - z0 + 8),
6529         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6530         tiles = [],
6531         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6532         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6533
6534     rows.forEach(function(y) {
6535       cols.forEach(function(x) {
6536         tiles.push([x, y, z0]);
6537       });
6538     });
6539
6540     tiles.translate = origin;
6541     tiles.scale = k;
6542
6543     return tiles;
6544   }
6545
6546   tile.scaleExtent = function(_) {
6547     if (!arguments.length) return scaleExtent;
6548     scaleExtent = _;
6549     return tile;
6550   };
6551
6552   tile.size = function(_) {
6553     if (!arguments.length) return size;
6554     size = _;
6555     return tile;
6556   };
6557
6558   tile.scale = function(_) {
6559     if (!arguments.length) return scale;
6560     scale = _;
6561     return tile;
6562   };
6563
6564   tile.translate = function(_) {
6565     if (!arguments.length) return translate;
6566     translate = _;
6567     return tile;
6568   };
6569
6570   tile.zoomDelta = function(_) {
6571     if (!arguments.length) return zoomDelta;
6572     zoomDelta = +_;
6573     return tile;
6574   };
6575
6576   return tile;
6577 };
6578 d3.jsonp = function (url, callback) {
6579   function rand() {
6580     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6581       c = '', i = -1;
6582     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6583     return c;
6584   }
6585
6586   function create(url) {
6587     var e = url.match(/callback=d3.jsonp.(\w+)/),
6588       c = e ? e[1] : rand();
6589     d3.jsonp[c] = function(data) {
6590       callback(data);
6591       delete d3.jsonp[c];
6592       script.remove();
6593     };
6594     return 'd3.jsonp.' + c;
6595   }
6596
6597   var cb = create(url),
6598     script = d3.select('head')
6599     .append('script')
6600     .attr('type', 'text/javascript')
6601     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6602 };
6603 /*
6604  * This code is licensed under the MIT license.
6605  *
6606  * Copyright © 2013, iD authors.
6607  *
6608  * Portions copyright © 2011, Keith Cirkel
6609  * See https://github.com/keithamus/jwerty
6610  *
6611  */
6612 d3.keybinding = function(namespace) {
6613     var bindings = [];
6614
6615     function matches(binding, event) {
6616         for (var p in binding.event) {
6617             if (event[p] != binding.event[p])
6618                 return false;
6619         }
6620
6621         return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
6622     }
6623
6624     function capture() {
6625         for (var i = 0; i < bindings.length; i++) {
6626             var binding = bindings[i];
6627             if (matches(binding, d3.event)) {
6628                 binding.callback();
6629             }
6630         }
6631     }
6632
6633     function bubble() {
6634         var tagName = d3.select(d3.event.target).node().tagName;
6635         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6636             return;
6637         }
6638         capture();
6639     }
6640
6641     function keybinding(selection) {
6642         selection = selection || d3.select(document);
6643         selection.on('keydown.capture' + namespace, capture, true);
6644         selection.on('keydown.bubble' + namespace, bubble, false);
6645         return keybinding;
6646     }
6647
6648     keybinding.off = function(selection) {
6649         selection = selection || d3.select(document);
6650         selection.on('keydown.capture' + namespace, null);
6651         selection.on('keydown.bubble' + namespace, null);
6652         return keybinding;
6653     };
6654
6655     keybinding.on = function(code, callback, capture) {
6656         var binding = {
6657             event: {
6658                 keyCode: 0,
6659                 shiftKey: false,
6660                 ctrlKey: false,
6661                 altKey: false,
6662                 metaKey: false
6663             },
6664             capture: capture,
6665             callback: callback
6666         };
6667
6668         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6669
6670         for (var i = 0; i < code.length; i++) {
6671             // Normalise matching errors
6672             if (code[i] === '++') code[i] = '+';
6673
6674             if (code[i] in d3.keybinding.modifierCodes) {
6675                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6676             } else if (code[i] in d3.keybinding.keyCodes) {
6677                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6678             }
6679         }
6680
6681         bindings.push(binding);
6682
6683         return keybinding;
6684     };
6685
6686     return keybinding;
6687 };
6688
6689 (function () {
6690     d3.keybinding.modifierCodes = {
6691         // Shift key, ⇧
6692         '⇧': 16, shift: 16,
6693         // CTRL key, on Mac: ⌃
6694         '⌃': 17, ctrl: 17,
6695         // ALT key, on Mac: ⌥ (Alt)
6696         '⌥': 18, alt: 18, option: 18,
6697         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6698         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6699     };
6700
6701     d3.keybinding.modifierProperties = {
6702         16: 'shiftKey',
6703         17: 'ctrlKey',
6704         18: 'altKey',
6705         91: 'metaKey'
6706     };
6707
6708     d3.keybinding.keyCodes = {
6709         // Backspace key, on Mac: ⌫ (Backspace)
6710         '⌫': 8, backspace: 8,
6711         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6712         '⇥': 9, '⇆': 9, tab: 9,
6713         // Return key, ↩
6714         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6715         // Pause/Break key
6716         'pause': 19, 'pause-break': 19,
6717         // Caps Lock key, ⇪
6718         '⇪': 20, caps: 20, 'caps-lock': 20,
6719         // Escape key, on Mac: ⎋, on Windows: Esc
6720         '⎋': 27, escape: 27, esc: 27,
6721         // Space key
6722         space: 32,
6723         // Page-Up key, or pgup, on Mac: ↖
6724         '↖': 33, pgup: 33, 'page-up': 33,
6725         // Page-Down key, or pgdown, on Mac: ↘
6726         '↘': 34, pgdown: 34, 'page-down': 34,
6727         // END key, on Mac: ⇟
6728         '⇟': 35, end: 35,
6729         // HOME key, on Mac: ⇞
6730         '⇞': 36, home: 36,
6731         // Insert key, or ins
6732         ins: 45, insert: 45,
6733         // Delete key, on Mac: ⌦ (Delete)
6734         '⌦': 46, del: 46, 'delete': 46,
6735         // Left Arrow Key, or ←
6736         '←': 37, left: 37, 'arrow-left': 37,
6737         // Up Arrow Key, or ↑
6738         '↑': 38, up: 38, 'arrow-up': 38,
6739         // Right Arrow Key, or →
6740         '→': 39, right: 39, 'arrow-right': 39,
6741         // Up Arrow Key, or ↓
6742         '↓': 40, down: 40, 'arrow-down': 40,
6743         // odities, printing characters that come out wrong:
6744         // Firefox Equals
6745         'ffequals': 61,
6746         // Num-Multiply, or *
6747         '*': 106, star: 106, asterisk: 106, multiply: 106,
6748         // Num-Plus or +
6749         '+': 107, 'plus': 107,
6750         // Num-Subtract, or -
6751         '-': 109, subtract: 109,
6752         // Firefox Minus
6753         'ffplus': 171,
6754         // Firefox Minus
6755         'ffminus': 173,
6756         // Semicolon
6757         ';': 186, semicolon: 186,
6758         // = or equals
6759         '=': 187, 'equals': 187,
6760         // Comma, or ,
6761         ',': 188, comma: 188,
6762         'dash': 189, //???
6763         // Period, or ., or full-stop
6764         '.': 190, period: 190, 'full-stop': 190,
6765         // Slash, or /, or forward-slash
6766         '/': 191, slash: 191, 'forward-slash': 191,
6767         // Tick, or `, or back-quote
6768         '`': 192, tick: 192, 'back-quote': 192,
6769         // Open bracket, or [
6770         '[': 219, 'open-bracket': 219,
6771         // Back slash, or \
6772         '\\': 220, 'back-slash': 220,
6773         // Close backet, or ]
6774         ']': 221, 'close-bracket': 221,
6775         // Apostrophe, or Quote, or '
6776         '\'': 222, quote: 222, apostrophe: 222
6777     };
6778
6779     // NUMPAD 0-9
6780     var i = 95, n = 0;
6781     while (++i < 106) {
6782         d3.keybinding.keyCodes['num-' + n] = i;
6783         ++n;
6784     }
6785
6786     // 0-9
6787     i = 47; n = 0;
6788     while (++i < 58) {
6789         d3.keybinding.keyCodes[n] = i;
6790         ++n;
6791     }
6792
6793     // F1-F25
6794     i = 111; n = 1;
6795     while (++i < 136) {
6796         d3.keybinding.keyCodes['f' + n] = i;
6797         ++n;
6798     }
6799
6800     // a-z
6801     i = 64;
6802     while (++i < 91) {
6803         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6804     }
6805 })();
6806 d3.selection.prototype.one = function (type, listener, capture) {
6807     var target = this, typeOnce = type + ".once";
6808     function one() {
6809         target.on(typeOnce, null);
6810         listener.apply(this, arguments);
6811     }
6812     target.on(typeOnce, one, capture);
6813     return this;
6814 };
6815 d3.selection.prototype.dimensions = function (dimensions) {
6816     var refresh = (function(node) {
6817         var cr = node.getBoundingClientRect();
6818         prop = [cr.width, cr.height];
6819         this.property('__dimensions__', prop);
6820         return prop;
6821     }).bind(this);
6822
6823     var node = this.node();
6824
6825     if (!arguments.length) {
6826         if (!node) return [0,0];
6827         return this.property('__dimensions__') || refresh(node);
6828     }
6829     if (dimensions === null) {
6830         if (!node) return [0,0];
6831         return refresh(node);
6832     }
6833
6834     return this
6835         .property('__dimensions__', [dimensions[0], dimensions[1]])
6836         .attr({width: dimensions[0], height: dimensions[1]});
6837 };
6838 d3.selection.prototype.trigger = function (type) {
6839     this.each(function() {
6840         var evt = document.createEvent('HTMLEvents');
6841         evt.initEvent(type, true, true);
6842         this.dispatchEvent(evt);
6843     });
6844 };
6845 // Tooltips and svg mask used to highlight certain features
6846 d3.curtain = function() {
6847
6848     var event = d3.dispatch(),
6849         surface,
6850         tooltip,
6851         darkness;
6852
6853     function curtain(selection) {
6854
6855         surface = selection.append('svg')
6856             .attr('id', 'curtain')
6857             .style({
6858                 'z-index': 1000,
6859                 'pointer-events': 'none',
6860                 'position': 'absolute',
6861                 'top': 0,
6862                 'left': 0
6863             });
6864
6865         darkness = surface.append('path')
6866             .attr({
6867                 x: 0,
6868                 y: 0,
6869                 'class': 'curtain-darkness'
6870             });
6871
6872         d3.select(window).on('resize.curtain', resize);
6873
6874         tooltip = selection.append('div')
6875             .attr('class', 'tooltip')
6876             .style('z-index', 1002);
6877
6878         tooltip.append('div').attr('class', 'tooltip-arrow');
6879         tooltip.append('div').attr('class', 'tooltip-inner');
6880
6881         resize();
6882
6883         function resize() {
6884             surface.attr({
6885                 width: window.innerWidth,
6886                 height: window.innerHeight
6887             });
6888             curtain.cut(darkness.datum());
6889         }
6890     }
6891
6892     curtain.reveal = function(box, text, tooltipclass, duration) {
6893         if (typeof box === 'string') box = d3.select(box).node();
6894         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6895
6896         curtain.cut(box, duration);
6897
6898         if (text) {
6899             // pseudo markdown bold text hack
6900             var parts = text.split('**');
6901             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6902             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6903
6904             var dimensions = tooltip.classed('in', true)
6905                 .select('.tooltip-inner')
6906                     .html(html)
6907                     .dimensions();
6908
6909             var pos;
6910
6911             var w = window.innerWidth,
6912                 h = window.innerHeight;
6913
6914             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6915                 side = 'bottom';
6916                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6917
6918             } else if (box.left + box.width + 300 < window.innerWidth) {
6919                 side = 'right';
6920                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6921
6922             } else if (box.left > 300) {
6923                 side = 'left';
6924                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6925             } else {
6926                 side = 'bottom';
6927                 pos = [box.left, box.top + box.height];
6928             }
6929
6930             pos = [
6931                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6932                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6933             ];
6934
6935
6936             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6937
6938             tooltip
6939                 .style('top', pos[1] + 'px')
6940                 .style('left', pos[0] + 'px')
6941                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6942                 .select('.tooltip-inner')
6943                     .html(html);
6944
6945         } else {
6946             tooltip.call(iD.ui.Toggle(false));
6947         }
6948     };
6949
6950     curtain.cut = function(datum, duration) {
6951         darkness.datum(datum);
6952
6953         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6954             .attr('d', function(d) {
6955                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6956                     window.innerWidth + "," + window.innerHeight + "L" +
6957                     window.innerWidth + ",0 Z";
6958
6959                 if (!d) return string;
6960                 return string + 'M' +
6961                     d.left + ',' + d.top + 'L' +
6962                     d.left + ',' + (d.top + d.height) + 'L' +
6963                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6964                     (d.left + d.width) + ',' + (d.top) + 'Z';
6965
6966             });
6967     };
6968
6969     curtain.remove = function() {
6970         surface.remove();
6971         tooltip.remove();
6972     };
6973
6974     return d3.rebind(curtain, event, 'on');
6975 };
6976 // Like selection.property('value', ...), but avoids no-op value sets,
6977 // which can result in layout/repaint thrashing in some situations.
6978 d3.selection.prototype.value = function(value) {
6979     function d3_selection_value(value) {
6980       function valueNull() {
6981         delete this.value;
6982       }
6983
6984       function valueConstant() {
6985         if (this.value !== value) this.value = value;
6986       }
6987
6988       function valueFunction() {
6989         var x = value.apply(this, arguments);
6990         if (x == null) delete this.value;
6991         else if (this.value !== x) this.value = x;
6992       }
6993
6994       return value == null
6995           ? valueNull : (typeof value === "function"
6996           ? valueFunction : valueConstant);
6997     }
6998
6999     if (!arguments.length) return this.property('value');
7000     return this.each(d3_selection_value(value));
7001 };
7002 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7003 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7004 //
7005 // Permission is hereby granted, free of charge, to any person
7006 // obtaining a copy of this software and associated documentation files
7007 // (the "Software"), to deal in the Software without restriction,
7008 // including without limitation the rights to use, copy, modify, merge,
7009 // publish, distribute, sublicense, and/or sell copies of the Software,
7010 // and to permit persons to whom the Software is furnished to do so,
7011 // subject to the following conditions:
7012 //
7013 // The above copyright notice and this permission notice shall be
7014 // included in all copies or substantial portions of the Software.
7015 //
7016 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7017 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7018 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7019 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7020 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7021 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7022 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7023 // SOFTWARE.
7024
7025 // source:  https://bitbucket.org/lshift/synchrotron/src
7026
7027 Diff3 = (function() {
7028     'use strict';
7029
7030     var diff3 = {
7031         longest_common_subsequence: function(file1, file2) {
7032             /* Text diff algorithm following Hunt and McIlroy 1976.
7033              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7034              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7035              * http://www.cs.dartmouth.edu/~doug/
7036              *
7037              * Expects two arrays of strings.
7038              */
7039             var equivalenceClasses;
7040             var file2indices;
7041             var newCandidate;
7042             var candidates;
7043             var line;
7044             var c, i, j, jX, r, s;
7045
7046             equivalenceClasses = {};
7047             for (j = 0; j < file2.length; j++) {
7048                 line = file2[j];
7049                 if (equivalenceClasses[line]) {
7050                     equivalenceClasses[line].push(j);
7051                 } else {
7052                     equivalenceClasses[line] = [j];
7053                 }
7054             }
7055
7056             candidates = [{file1index: -1,
7057                            file2index: -1,
7058                            chain: null}];
7059
7060             for (i = 0; i < file1.length; i++) {
7061                 line = file1[i];
7062                 file2indices = equivalenceClasses[line] || [];
7063
7064                 r = 0;
7065                 c = candidates[0];
7066
7067                 for (jX = 0; jX < file2indices.length; jX++) {
7068                     j = file2indices[jX];
7069
7070                     for (s = 0; s < candidates.length; s++) {
7071                         if ((candidates[s].file2index < j) &&
7072                             ((s == candidates.length - 1) ||
7073                              (candidates[s + 1].file2index > j)))
7074                             break;
7075                     }
7076
7077                     if (s < candidates.length) {
7078                         newCandidate = {file1index: i,
7079                                         file2index: j,
7080                                         chain: candidates[s]};
7081                         if (r == candidates.length) {
7082                             candidates.push(c);
7083                         } else {
7084                             candidates[r] = c;
7085                         }
7086                         r = s + 1;
7087                         c = newCandidate;
7088                         if (r == candidates.length) {
7089                             break; // no point in examining further (j)s
7090                         }
7091                     }
7092                 }
7093
7094                 candidates[r] = c;
7095             }
7096
7097             // At this point, we know the LCS: it's in the reverse of the
7098             // linked-list through .chain of
7099             // candidates[candidates.length - 1].
7100
7101             return candidates[candidates.length - 1];
7102         },
7103
7104         diff_comm: function(file1, file2) {
7105             // We apply the LCS to build a "comm"-style picture of the
7106             // differences between file1 and file2.
7107
7108             var result = [];
7109             var tail1 = file1.length;
7110             var tail2 = file2.length;
7111             var common = {common: []};
7112
7113             function processCommon() {
7114                 if (common.common.length) {
7115                     common.common.reverse();
7116                     result.push(common);
7117                     common = {common: []};
7118                 }
7119             }
7120
7121             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7122                  candidate !== null;
7123                  candidate = candidate.chain)
7124             {
7125                 var different = {file1: [], file2: []};
7126
7127                 while (--tail1 > candidate.file1index) {
7128                     different.file1.push(file1[tail1]);
7129                 }
7130
7131                 while (--tail2 > candidate.file2index) {
7132                     different.file2.push(file2[tail2]);
7133                 }
7134
7135                 if (different.file1.length || different.file2.length) {
7136                     processCommon();
7137                     different.file1.reverse();
7138                     different.file2.reverse();
7139                     result.push(different);
7140                 }
7141
7142                 if (tail1 >= 0) {
7143                     common.common.push(file1[tail1]);
7144                 }
7145             }
7146
7147             processCommon();
7148
7149             result.reverse();
7150             return result;
7151         },
7152
7153         diff_patch: function(file1, file2) {
7154             // We apply the LCD to build a JSON representation of a
7155             // diff(1)-style patch.
7156
7157             var result = [];
7158             var tail1 = file1.length;
7159             var tail2 = file2.length;
7160
7161             function chunkDescription(file, offset, length) {
7162                 var chunk = [];
7163                 for (var i = 0; i < length; i++) {
7164                     chunk.push(file[offset + i]);
7165                 }
7166                 return {offset: offset,
7167                         length: length,
7168                         chunk: chunk};
7169             }
7170
7171             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7172                  candidate !== null;
7173                  candidate = candidate.chain)
7174             {
7175                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7176                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7177                 tail1 = candidate.file1index;
7178                 tail2 = candidate.file2index;
7179
7180                 if (mismatchLength1 || mismatchLength2) {
7181                     result.push({file1: chunkDescription(file1,
7182                                                          candidate.file1index + 1,
7183                                                          mismatchLength1),
7184                                  file2: chunkDescription(file2,
7185                                                          candidate.file2index + 1,
7186                                                          mismatchLength2)});
7187                 }
7188             }
7189
7190             result.reverse();
7191             return result;
7192         },
7193
7194         strip_patch: function(patch) {
7195         // Takes the output of Diff3.diff_patch(), and removes
7196         // information from it. It can still be used by patch(),
7197         // below, but can no longer be inverted.
7198         var newpatch = [];
7199         for (var i = 0; i < patch.length; i++) {
7200             var chunk = patch[i];
7201             newpatch.push({file1: {offset: chunk.file1.offset,
7202                        length: chunk.file1.length},
7203                    file2: {chunk: chunk.file2.chunk}});
7204         }
7205         return newpatch;
7206         },
7207
7208         invert_patch: function(patch) {
7209             // Takes the output of Diff3.diff_patch(), and inverts the
7210             // sense of it, so that it can be applied to file2 to give
7211             // file1 rather than the other way around.
7212
7213             for (var i = 0; i < patch.length; i++) {
7214                 var chunk = patch[i];
7215                 var tmp = chunk.file1;
7216                 chunk.file1 = chunk.file2;
7217                 chunk.file2 = tmp;
7218             }
7219         },
7220
7221         patch: function (file, patch) {
7222             // Applies a patch to a file.
7223             //
7224             // Given file1 and file2, Diff3.patch(file1,
7225             // Diff3.diff_patch(file1, file2)) should give file2.
7226
7227             var result = [];
7228             var commonOffset = 0;
7229
7230             function copyCommon(targetOffset) {
7231                 while (commonOffset < targetOffset) {
7232                     result.push(file[commonOffset]);
7233                     commonOffset++;
7234                 }
7235             }
7236
7237             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7238                 var chunk = patch[chunkIndex];
7239                 copyCommon(chunk.file1.offset);
7240                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7241                     result.push(chunk.file2.chunk[lineIndex]);
7242                 }
7243                 commonOffset += chunk.file1.length;
7244             }
7245
7246             copyCommon(file.length);
7247             return result;
7248         },
7249
7250         diff_indices: function(file1, file2) {
7251             // We apply the LCS to give a simple representation of the
7252             // offsets and lengths of mismatched chunks in the input
7253             // files. This is used by diff3_merge_indices below.
7254
7255             var result = [];
7256             var tail1 = file1.length;
7257             var tail2 = file2.length;
7258
7259             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7260                  candidate !== null;
7261                  candidate = candidate.chain)
7262             {
7263                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7264                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7265                 tail1 = candidate.file1index;
7266                 tail2 = candidate.file2index;
7267
7268                 if (mismatchLength1 || mismatchLength2) {
7269                     result.push({file1: [tail1 + 1, mismatchLength1],
7270                                  file2: [tail2 + 1, mismatchLength2]});
7271                 }
7272             }
7273
7274             result.reverse();
7275             return result;
7276         },
7277
7278         diff3_merge_indices: function (a, o, b) {
7279             // Given three files, A, O, and B, where both A and B are
7280             // independently derived from O, returns a fairly complicated
7281             // internal representation of merge decisions it's taken. The
7282             // interested reader may wish to consult
7283             //
7284             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7285             // Formal Investigation of Diff3." In Arvind and Prasad,
7286             // editors, Foundations of Software Technology and Theoretical
7287             // Computer Science (FSTTCS), December 2007.
7288             //
7289             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7290             var i;
7291
7292             var m1 = Diff3.diff_indices(o, a);
7293             var m2 = Diff3.diff_indices(o, b);
7294
7295             var hunks = [];
7296             function addHunk(h, side) {
7297                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7298             }
7299             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7300             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7301             hunks.sort();
7302
7303             var result = [];
7304             var commonOffset = 0;
7305             function copyCommon(targetOffset) {
7306                 if (targetOffset > commonOffset) {
7307                     result.push([1, commonOffset, targetOffset - commonOffset]);
7308                     commonOffset = targetOffset;
7309                 }
7310             }
7311
7312             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7313                 var firstHunkIndex = hunkIndex;
7314                 var hunk = hunks[hunkIndex];
7315                 var regionLhs = hunk[0];
7316                 var regionRhs = regionLhs + hunk[2];
7317                 while (hunkIndex < hunks.length - 1) {
7318                     var maybeOverlapping = hunks[hunkIndex + 1];
7319                     var maybeLhs = maybeOverlapping[0];
7320                     if (maybeLhs > regionRhs) break;
7321                     regionRhs = maybeLhs + maybeOverlapping[2];
7322                     hunkIndex++;
7323                 }
7324
7325                 copyCommon(regionLhs);
7326                 if (firstHunkIndex == hunkIndex) {
7327             // The "overlap" was only one hunk long, meaning that
7328             // there's no conflict here. Either a and o were the
7329             // same, or b and o were the same.
7330                     if (hunk[4] > 0) {
7331                         result.push([hunk[1], hunk[3], hunk[4]]);
7332                     }
7333                 } else {
7334             // A proper conflict. Determine the extents of the
7335             // regions involved from a, o and b. Effectively merge
7336             // all the hunks on the left into one giant hunk, and
7337             // do the same for the right; then, correct for skew
7338             // in the regions of o that each side changed, and
7339             // report appropriate spans for the three sides.
7340             var regions = {
7341                 0: [a.length, -1, o.length, -1],
7342                 2: [b.length, -1, o.length, -1]
7343             };
7344                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7345                 hunk = hunks[i];
7346                         var side = hunk[1];
7347                 var r = regions[side];
7348                 var oLhs = hunk[0];
7349                 var oRhs = oLhs + hunk[2];
7350                         var abLhs = hunk[3];
7351                         var abRhs = abLhs + hunk[4];
7352                 r[0] = Math.min(abLhs, r[0]);
7353                 r[1] = Math.max(abRhs, r[1]);
7354                 r[2] = Math.min(oLhs, r[2]);
7355                 r[3] = Math.max(oRhs, r[3]);
7356                     }
7357             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7358             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7359             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7360             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7361                     result.push([-1,
7362                      aLhs,      aRhs      - aLhs,
7363                      regionLhs, regionRhs - regionLhs,
7364                      bLhs,      bRhs      - bLhs]);
7365                 }
7366                 commonOffset = regionRhs;
7367             }
7368
7369             copyCommon(o.length);
7370             return result;
7371         },
7372
7373         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7374             // Applies the output of Diff3.diff3_merge_indices to actually
7375             // construct the merged file; the returned result alternates
7376             // between "ok" and "conflict" blocks.
7377
7378             var result = [];
7379             var files = [a, o, b];
7380             var indices = Diff3.diff3_merge_indices(a, o, b);
7381
7382             var okLines = [];
7383             function flushOk() {
7384                 if (okLines.length) {
7385                     result.push({ok: okLines});
7386                 }
7387                 okLines = [];
7388             }
7389             function pushOk(xs) {
7390                 for (var j = 0; j < xs.length; j++) {
7391                     okLines.push(xs[j]);
7392                 }
7393             }
7394
7395             function isTrueConflict(rec) {
7396                 if (rec[2] != rec[6]) return true;
7397                 var aoff = rec[1];
7398                 var boff = rec[5];
7399                 for (var j = 0; j < rec[2]; j++) {
7400                     if (a[j + aoff] != b[j + boff]) return true;
7401                 }
7402                 return false;
7403             }
7404
7405             for (var i = 0; i < indices.length; i++) {
7406                 var x = indices[i];
7407                 var side = x[0];
7408                 if (side == -1) {
7409                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7410                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7411                     } else {
7412                         flushOk();
7413                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7414                                                 aIndex: x[1],
7415                                                 o: o.slice(x[3], x[3] + x[4]),
7416                                                 oIndex: x[3],
7417                                                 b: b.slice(x[5], x[5] + x[6]),
7418                                                 bIndex: x[5]}});
7419                     }
7420                 } else {
7421                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7422                 }
7423             }
7424
7425             flushOk();
7426             return result;
7427         }
7428     };
7429     return diff3;
7430 })();
7431
7432 if (typeof module !== 'undefined') module.exports = Diff3;
7433 var JXON = new (function () {
7434   var
7435     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7436     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7437
7438   function parseText (sValue) {
7439     if (rIsNull.test(sValue)) { return null; }
7440     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7441     if (isFinite(sValue)) { return parseFloat(sValue); }
7442     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7443     return sValue;
7444   }
7445
7446   function EmptyTree () { }
7447   EmptyTree.prototype.toString = function () { return "null"; };
7448   EmptyTree.prototype.valueOf = function () { return null; };
7449
7450   function objectify (vValue) {
7451     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7452   }
7453
7454   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7455     var
7456       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7457       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7458
7459     var
7460       sProp, vContent, nLength = 0, sCollectedTxt = "",
7461       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7462
7463     if (bChildren) {
7464       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7465         oNode = oParentNode.childNodes.item(nItem);
7466         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7467         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7468         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7469       }
7470     }
7471
7472     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7473
7474     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7475
7476     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7477       sProp = aCache[nElId].nodeName.toLowerCase();
7478       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7479       if (vResult.hasOwnProperty(sProp)) {
7480         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7481         vResult[sProp].push(vContent);
7482       } else {
7483         vResult[sProp] = vContent;
7484         nLength++;
7485       }
7486     }
7487
7488     if (bAttributes) {
7489       var
7490         nAttrLen = oParentNode.attributes.length,
7491         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7492
7493       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7494         oAttrib = oParentNode.attributes.item(nAttrib);
7495         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7496       }
7497
7498       if (bNesteAttr) {
7499         if (bFreeze) { Object.freeze(oAttrParent); }
7500         vResult[sAttributesProp] = oAttrParent;
7501         nLength -= nAttrLen - 1;
7502       }
7503     }
7504
7505     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7506       vResult[sValueProp] = vBuiltVal;
7507     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7508       vResult = vBuiltVal;
7509     }
7510
7511     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7512
7513     aCache.length = nLevelStart;
7514
7515     return vResult;
7516   }
7517
7518   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7519     var vValue, oChild;
7520
7521     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7522       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7523     } else if (oParentObj.constructor === Date) {
7524       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7525     }
7526
7527     for (var sName in oParentObj) {
7528       vValue = oParentObj[sName];
7529       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7530       if (sName === sValueProp) {
7531         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7532       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7533         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7534       } else if (sName.charAt(0) === sAttrPref) {
7535         oParentEl.setAttribute(sName.slice(1), vValue);
7536       } else if (vValue.constructor === Array) {
7537         for (var nItem = 0; nItem < vValue.length; nItem++) {
7538           oChild = oXMLDoc.createElement(sName);
7539           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7540           oParentEl.appendChild(oChild);
7541         }
7542       } else {
7543         oChild = oXMLDoc.createElement(sName);
7544         if (vValue instanceof Object) {
7545           loadObjTree(oXMLDoc, oChild, vValue);
7546         } else if (vValue !== null && vValue !== true) {
7547           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7548         }
7549         oParentEl.appendChild(oChild);
7550      }
7551    }
7552   }
7553
7554   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7555     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7556     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7557   };
7558
7559   this.unbuild = function (oObjTree) {    
7560     var oNewDoc = document.implementation.createDocument("", "", null);
7561     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7562     return oNewDoc;
7563   };
7564
7565   this.stringify = function (oObjTree) {
7566     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7567   };
7568 })();
7569 // var myObject = JXON.build(doc);
7570 // we got our javascript object! try: alert(JSON.stringify(myObject));
7571
7572 // var newDoc = JXON.unbuild(myObject);
7573 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7574 /**
7575  * @license
7576  * lodash 3.9.3 (Custom Build) <https://lodash.com/>
7577  * Build: `lodash --development --output js/lib/lodash.js include="any,assign,bind,chunk,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,forOwn,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"`
7578  * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
7579  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7580  * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7581  * Available under MIT license <https://lodash.com/license>
7582  */
7583 ;(function() {
7584
7585   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7586   var undefined;
7587
7588   /** Used as the semantic version number. */
7589   var VERSION = '3.9.3';
7590
7591   /** Used to compose bitmasks for wrapper metadata. */
7592   var BIND_FLAG = 1,
7593       BIND_KEY_FLAG = 2,
7594       CURRY_BOUND_FLAG = 4,
7595       CURRY_FLAG = 8,
7596       CURRY_RIGHT_FLAG = 16,
7597       PARTIAL_FLAG = 32,
7598       PARTIAL_RIGHT_FLAG = 64,
7599       ARY_FLAG = 128,
7600       REARG_FLAG = 256;
7601
7602   /** Used to detect when a function becomes hot. */
7603   var HOT_COUNT = 150,
7604       HOT_SPAN = 16;
7605
7606   /** Used to indicate the type of lazy iteratees. */
7607   var LAZY_DROP_WHILE_FLAG = 0,
7608       LAZY_FILTER_FLAG = 1,
7609       LAZY_MAP_FLAG = 2;
7610
7611   /** Used as the `TypeError` message for "Functions" methods. */
7612   var FUNC_ERROR_TEXT = 'Expected a function';
7613
7614   /** Used as the internal argument placeholder. */
7615   var PLACEHOLDER = '__lodash_placeholder__';
7616
7617   /** `Object#toString` result references. */
7618   var argsTag = '[object Arguments]',
7619       arrayTag = '[object Array]',
7620       boolTag = '[object Boolean]',
7621       dateTag = '[object Date]',
7622       errorTag = '[object Error]',
7623       funcTag = '[object Function]',
7624       mapTag = '[object Map]',
7625       numberTag = '[object Number]',
7626       objectTag = '[object Object]',
7627       regexpTag = '[object RegExp]',
7628       setTag = '[object Set]',
7629       stringTag = '[object String]',
7630       weakMapTag = '[object WeakMap]';
7631
7632   var arrayBufferTag = '[object ArrayBuffer]',
7633       float32Tag = '[object Float32Array]',
7634       float64Tag = '[object Float64Array]',
7635       int8Tag = '[object Int8Array]',
7636       int16Tag = '[object Int16Array]',
7637       int32Tag = '[object Int32Array]',
7638       uint8Tag = '[object Uint8Array]',
7639       uint8ClampedTag = '[object Uint8ClampedArray]',
7640       uint16Tag = '[object Uint16Array]',
7641       uint32Tag = '[object Uint32Array]';
7642
7643   /** Used to match property names within property paths. */
7644   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
7645       reIsPlainProp = /^\w*$/,
7646       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
7647
7648   /**
7649    * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
7650    * In addition to special characters the forward slash is escaped to allow for
7651    * easier `eval` use and `Function` compilation.
7652    */
7653   var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
7654       reHasRegExpChars = RegExp(reRegExpChars.source);
7655
7656   /** Used to match backslashes in property paths. */
7657   var reEscapeChar = /\\(\\)?/g;
7658
7659   /** Used to match `RegExp` flags from their coerced string values. */
7660   var reFlags = /\w*$/;
7661
7662   /** Used to detect host constructors (Safari > 5). */
7663   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7664
7665   /** Used to detect unsigned integer values. */
7666   var reIsUint = /^\d+$/;
7667
7668   /** Used to fix the JScript `[[DontEnum]]` bug. */
7669   var shadowProps = [
7670     'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
7671     'toLocaleString', 'toString', 'valueOf'
7672   ];
7673
7674   /** Used to identify `toStringTag` values of typed arrays. */
7675   var typedArrayTags = {};
7676   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7677   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7678   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7679   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7680   typedArrayTags[uint32Tag] = true;
7681   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7682   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7683   typedArrayTags[dateTag] = typedArrayTags[errorTag] =
7684   typedArrayTags[funcTag] = typedArrayTags[mapTag] =
7685   typedArrayTags[numberTag] = typedArrayTags[objectTag] =
7686   typedArrayTags[regexpTag] = typedArrayTags[setTag] =
7687   typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
7688
7689   /** Used to identify `toStringTag` values supported by `_.clone`. */
7690   var cloneableTags = {};
7691   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7692   cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
7693   cloneableTags[dateTag] = cloneableTags[float32Tag] =
7694   cloneableTags[float64Tag] = cloneableTags[int8Tag] =
7695   cloneableTags[int16Tag] = cloneableTags[int32Tag] =
7696   cloneableTags[numberTag] = cloneableTags[objectTag] =
7697   cloneableTags[regexpTag] = cloneableTags[stringTag] =
7698   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7699   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7700   cloneableTags[errorTag] = cloneableTags[funcTag] =
7701   cloneableTags[mapTag] = cloneableTags[setTag] =
7702   cloneableTags[weakMapTag] = false;
7703
7704   /** Used as an internal `_.debounce` options object by `_.throttle`. */
7705   var debounceOptions = {
7706     'leading': false,
7707     'maxWait': 0,
7708     'trailing': false
7709   };
7710
7711   /** Used to determine if values are of the language type `Object`. */
7712   var objectTypes = {
7713     'function': true,
7714     'object': true
7715   };
7716
7717   /** Detect free variable `exports`. */
7718   var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
7719
7720   /** Detect free variable `module`. */
7721   var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
7722
7723   /** Detect free variable `global` from Node.js. */
7724   var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
7725
7726   /** Detect free variable `self`. */
7727   var freeSelf = objectTypes[typeof self] && self && self.Object && self;
7728
7729   /** Detect free variable `window`. */
7730   var freeWindow = objectTypes[typeof window] && window && window.Object && window;
7731
7732   /** Detect the popular CommonJS extension `module.exports`. */
7733   var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
7734
7735   /**
7736    * Used as a reference to the global object.
7737    *
7738    * The `this` value is used if it's the global object to avoid Greasemonkey's
7739    * restricted `window` object, otherwise the `window` object is used.
7740    */
7741   var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
7742
7743   /*--------------------------------------------------------------------------*/
7744
7745   /**
7746    * The base implementation of `_.findIndex` and `_.findLastIndex` without
7747    * support for callback shorthands and `this` binding.
7748    *
7749    * @private
7750    * @param {Array} array The array to search.
7751    * @param {Function} predicate The function invoked per iteration.
7752    * @param {boolean} [fromRight] Specify iterating from right to left.
7753    * @returns {number} Returns the index of the matched value, else `-1`.
7754    */
7755   function baseFindIndex(array, predicate, fromRight) {
7756     var length = array.length,
7757         index = fromRight ? length : -1;
7758
7759     while ((fromRight ? index-- : ++index < length)) {
7760       if (predicate(array[index], index, array)) {
7761         return index;
7762       }
7763     }
7764     return -1;
7765   }
7766
7767   /**
7768    * The base implementation of `_.indexOf` without support for binary searches.
7769    *
7770    * @private
7771    * @param {Array} array The array to search.
7772    * @param {*} value The value to search for.
7773    * @param {number} fromIndex The index to search from.
7774    * @returns {number} Returns the index of the matched value, else `-1`.
7775    */
7776   function baseIndexOf(array, value, fromIndex) {
7777     if (value !== value) {
7778       return indexOfNaN(array, fromIndex);
7779     }
7780     var index = fromIndex - 1,
7781         length = array.length;
7782
7783     while (++index < length) {
7784       if (array[index] === value) {
7785         return index;
7786       }
7787     }
7788     return -1;
7789   }
7790
7791   /**
7792    * The base implementation of `_.isFunction` without support for environments
7793    * with incorrect `typeof` results.
7794    *
7795    * @private
7796    * @param {*} value The value to check.
7797    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
7798    */
7799   function baseIsFunction(value) {
7800     // Avoid a Chakra JIT bug in compatibility modes of IE 11.
7801     // See https://github.com/jashkenas/underscore/issues/1621 for more details.
7802     return typeof value == 'function' || false;
7803   }
7804
7805   /**
7806    * Converts `value` to a string if it's not one. An empty string is returned
7807    * for `null` or `undefined` values.
7808    *
7809    * @private
7810    * @param {*} value The value to process.
7811    * @returns {string} Returns the string.
7812    */
7813   function baseToString(value) {
7814     if (typeof value == 'string') {
7815       return value;
7816     }
7817     return value == null ? '' : (value + '');
7818   }
7819
7820   /**
7821    * Gets the index at which the first occurrence of `NaN` is found in `array`.
7822    *
7823    * @private
7824    * @param {Array} array The array to search.
7825    * @param {number} fromIndex The index to search from.
7826    * @param {boolean} [fromRight] Specify iterating from right to left.
7827    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
7828    */
7829   function indexOfNaN(array, fromIndex, fromRight) {
7830     var length = array.length,
7831         index = fromIndex + (fromRight ? 0 : -1);
7832
7833     while ((fromRight ? index-- : ++index < length)) {
7834       var other = array[index];
7835       if (other !== other) {
7836         return index;
7837       }
7838     }
7839     return -1;
7840   }
7841
7842   /**
7843    * Checks if `value` is a host object in IE < 9.
7844    *
7845    * @private
7846    * @param {*} value The value to check.
7847    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
7848    */
7849   var isHostObject = (function() {
7850     try {
7851       Object({ 'toString': 0 } + '');
7852     } catch(e) {
7853       return function() { return false; };
7854     }
7855     return function(value) {
7856       // IE < 9 presents many host objects as `Object` objects that can coerce
7857       // to strings despite having improperly defined `toString` methods.
7858       return typeof value.toString != 'function' && typeof (value + '') == 'string';
7859     };
7860   }());
7861
7862   /**
7863    * Checks if `value` is object-like.
7864    *
7865    * @private
7866    * @param {*} value The value to check.
7867    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
7868    */
7869   function isObjectLike(value) {
7870     return !!value && typeof value == 'object';
7871   }
7872
7873   /**
7874    * Replaces all `placeholder` elements in `array` with an internal placeholder
7875    * and returns an array of their indexes.
7876    *
7877    * @private
7878    * @param {Array} array The array to modify.
7879    * @param {*} placeholder The placeholder to replace.
7880    * @returns {Array} Returns the new array of placeholder indexes.
7881    */
7882   function replaceHolders(array, placeholder) {
7883     var index = -1,
7884         length = array.length,
7885         resIndex = -1,
7886         result = [];
7887
7888     while (++index < length) {
7889       if (array[index] === placeholder) {
7890         array[index] = PLACEHOLDER;
7891         result[++resIndex] = index;
7892       }
7893     }
7894     return result;
7895   }
7896
7897   /**
7898    * An implementation of `_.uniq` optimized for sorted arrays without support
7899    * for callback shorthands and `this` binding.
7900    *
7901    * @private
7902    * @param {Array} array The array to inspect.
7903    * @param {Function} [iteratee] The function invoked per iteration.
7904    * @returns {Array} Returns the new duplicate-value-free array.
7905    */
7906   function sortedUniq(array, iteratee) {
7907     var seen,
7908         index = -1,
7909         length = array.length,
7910         resIndex = -1,
7911         result = [];
7912
7913     while (++index < length) {
7914       var value = array[index],
7915           computed = iteratee ? iteratee(value, index, array) : value;
7916
7917       if (!index || seen !== computed) {
7918         seen = computed;
7919         result[++resIndex] = value;
7920       }
7921     }
7922     return result;
7923   }
7924
7925   /*--------------------------------------------------------------------------*/
7926
7927   /** Used for native method references. */
7928   var arrayProto = Array.prototype,
7929       errorProto = Error.prototype,
7930       objectProto = Object.prototype,
7931       stringProto = String.prototype;
7932
7933   /** Used to resolve the decompiled source of functions. */
7934   var fnToString = Function.prototype.toString;
7935
7936   /** Used to check objects for own properties. */
7937   var hasOwnProperty = objectProto.hasOwnProperty;
7938
7939   /**
7940    * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
7941    * of values.
7942    */
7943   var objToString = objectProto.toString;
7944
7945   /** Used to detect if a method is native. */
7946   var reIsNative = RegExp('^' +
7947     escapeRegExp(fnToString.call(hasOwnProperty))
7948     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
7949   );
7950
7951   /** Native method references. */
7952   var ArrayBuffer = getNative(root, 'ArrayBuffer'),
7953       bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'),
7954       ceil = Math.ceil,
7955       floor = Math.floor,
7956       getPrototypeOf = getNative(Object, 'getPrototypeOf'),
7957       push = arrayProto.push,
7958       propertyIsEnumerable = objectProto.propertyIsEnumerable,
7959       Set = getNative(root, 'Set'),
7960       splice = arrayProto.splice,
7961       Uint8Array = getNative(root, 'Uint8Array'),
7962       WeakMap = getNative(root, 'WeakMap');
7963
7964   /** Used to clone array buffers. */
7965   var Float64Array = (function() {
7966     // Safari 5 errors when using an array buffer to initialize a typed array
7967     // where the array buffer's `byteLength` is not a multiple of the typed
7968     // array's `BYTES_PER_ELEMENT`.
7969     try {
7970       var func = getNative(root, 'Float64Array'),
7971           result = new func(new ArrayBuffer(10), 0, 1) && func;
7972     } catch(e) {}
7973     return result || null;
7974   }());
7975
7976   /* Native method references for those with the same name as other `lodash` methods. */
7977   var nativeCreate = getNative(Object, 'create'),
7978       nativeIsArray = getNative(Array, 'isArray'),
7979       nativeKeys = getNative(Object, 'keys'),
7980       nativeMax = Math.max,
7981       nativeMin = Math.min,
7982       nativeNow = getNative(Date, 'now');
7983
7984   /** Used as references for `-Infinity` and `Infinity`. */
7985   var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
7986
7987   /** Used as references for the maximum length and index of an array. */
7988   var MAX_ARRAY_LENGTH = 4294967295,
7989       MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
7990       HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
7991
7992   /** Used as the size, in bytes, of each `Float64Array` element. */
7993   var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
7994
7995   /**
7996    * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
7997    * of an array-like value.
7998    */
7999   var MAX_SAFE_INTEGER = 9007199254740991;
8000
8001   /** Used to store function metadata. */
8002   var metaMap = WeakMap && new WeakMap;
8003
8004   /** Used to lookup unminified function names. */
8005   var realNames = {};
8006
8007   /** Used to lookup a type array constructors by `toStringTag`. */
8008   var ctorByTag = {};
8009   ctorByTag[float32Tag] = root.Float32Array;
8010   ctorByTag[float64Tag] = root.Float64Array;
8011   ctorByTag[int8Tag] = root.Int8Array;
8012   ctorByTag[int16Tag] = root.Int16Array;
8013   ctorByTag[int32Tag] = root.Int32Array;
8014   ctorByTag[uint8Tag] = root.Uint8Array;
8015   ctorByTag[uint8ClampedTag] = root.Uint8ClampedArray;
8016   ctorByTag[uint16Tag] = root.Uint16Array;
8017   ctorByTag[uint32Tag] = root.Uint32Array;
8018
8019   /** Used to avoid iterating over non-enumerable properties in IE < 9. */
8020   var nonEnumProps = {};
8021   nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
8022   nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
8023   nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
8024   nonEnumProps[objectTag] = { 'constructor': true };
8025
8026   arrayEach(shadowProps, function(key) {
8027     for (var tag in nonEnumProps) {
8028       if (hasOwnProperty.call(nonEnumProps, tag)) {
8029         var props = nonEnumProps[tag];
8030         props[key] = hasOwnProperty.call(props, key);
8031       }
8032     }
8033   });
8034
8035   /*------------------------------------------------------------------------*/
8036
8037   /**
8038    * Creates a `lodash` object which wraps `value` to enable implicit chaining.
8039    * Methods that operate on and return arrays, collections, and functions can
8040    * be chained together. Methods that return a boolean or single value will
8041    * automatically end the chain returning the unwrapped value. Explicit chaining
8042    * may be enabled using `_.chain`. The execution of chained methods is lazy,
8043    * that is, execution is deferred until `_#value` is implicitly or explicitly
8044    * called.
8045    *
8046    * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
8047    * fusion is an optimization that merges iteratees to avoid creating intermediate
8048    * arrays and reduce the number of iteratee executions.
8049    *
8050    * Chaining is supported in custom builds as long as the `_#value` method is
8051    * directly or indirectly included in the build.
8052    *
8053    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8054    *
8055    * The wrapper `Array` methods are:
8056    * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
8057    * `splice`, and `unshift`
8058    *
8059    * The wrapper `String` methods are:
8060    * `replace` and `split`
8061    *
8062    * The wrapper methods that support shortcut fusion are:
8063    * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
8064    * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
8065    * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
8066    * and `where`
8067    *
8068    * The chainable wrapper methods are:
8069    * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
8070    * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
8071    * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
8072    * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
8073    * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
8074    * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
8075    * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
8076    * `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8077    * `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`,
8078    * `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`,
8079    * `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`,
8080    * `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`,
8081    * `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`,
8082    * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`,
8083    * `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`,
8084    * `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`,
8085    * `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
8086    *
8087    * The wrapper methods that are **not** chainable by default are:
8088    * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
8089    * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
8090    * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`,
8091    * `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`,
8092    * `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
8093    * `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
8094    * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
8095    * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`,
8096    * `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`,
8097    * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`,
8098    * `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8099    * `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`,
8100    * `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
8101    *
8102    * The wrapper method `sample` will return a wrapped value when `n` is provided,
8103    * otherwise an unwrapped value is returned.
8104    *
8105    * @name _
8106    * @constructor
8107    * @category Chain
8108    * @param {*} value The value to wrap in a `lodash` instance.
8109    * @returns {Object} Returns the new `lodash` wrapper instance.
8110    * @example
8111    *
8112    * var wrapped = _([1, 2, 3]);
8113    *
8114    * // returns an unwrapped value
8115    * wrapped.reduce(function(total, n) {
8116    *   return total + n;
8117    * });
8118    * // => 6
8119    *
8120    * // returns a wrapped value
8121    * var squares = wrapped.map(function(n) {
8122    *   return n * n;
8123    * });
8124    *
8125    * _.isArray(squares);
8126    * // => false
8127    *
8128    * _.isArray(squares.value());
8129    * // => true
8130    */
8131   function lodash(value) {
8132     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8133       if (value instanceof LodashWrapper) {
8134         return value;
8135       }
8136       if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
8137         return wrapperClone(value);
8138       }
8139     }
8140     return new LodashWrapper(value);
8141   }
8142
8143   /**
8144    * The function whose prototype all chaining wrappers inherit from.
8145    *
8146    * @private
8147    */
8148   function baseLodash() {
8149     // No operation performed.
8150   }
8151
8152   /**
8153    * The base constructor for creating `lodash` wrapper objects.
8154    *
8155    * @private
8156    * @param {*} value The value to wrap.
8157    * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
8158    * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
8159    */
8160   function LodashWrapper(value, chainAll, actions) {
8161     this.__wrapped__ = value;
8162     this.__actions__ = actions || [];
8163     this.__chain__ = !!chainAll;
8164   }
8165
8166   /**
8167    * An object environment feature flags.
8168    *
8169    * @static
8170    * @memberOf _
8171    * @type Object
8172    */
8173   var support = lodash.support = {};
8174
8175   (function(x) {
8176     var Ctor = function() { this.x = x; },
8177         object = { '0': x, 'length': x },
8178         props = [];
8179
8180     Ctor.prototype = { 'valueOf': x, 'y': x };
8181     for (var key in new Ctor) { props.push(key); }
8182
8183     /**
8184      * Detect if the `toStringTag` of `arguments` objects is resolvable
8185      * (all but Firefox < 4, IE < 9).
8186      *
8187      * @memberOf _.support
8188      * @type boolean
8189      */
8190     support.argsTag = objToString.call(arguments) == argsTag;
8191
8192     /**
8193      * Detect if `name` or `message` properties of `Error.prototype` are
8194      * enumerable by default (IE < 9, Safari < 5.1).
8195      *
8196      * @memberOf _.support
8197      * @type boolean
8198      */
8199     support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
8200       propertyIsEnumerable.call(errorProto, 'name');
8201
8202     /**
8203      * Detect if `prototype` properties are enumerable by default.
8204      *
8205      * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
8206      * (if the prototype or a property on the prototype has been set)
8207      * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
8208      * property to `true`.
8209      *
8210      * @memberOf _.support
8211      * @type boolean
8212      */
8213     support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
8214
8215     /**
8216      * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
8217      *
8218      * In IE < 9 an object's own properties, shadowing non-enumerable ones,
8219      * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
8220      *
8221      * @memberOf _.support
8222      * @type boolean
8223      */
8224     support.nonEnumShadows = !/valueOf/.test(props);
8225
8226     /**
8227      * Detect if own properties are iterated after inherited properties (IE < 9).
8228      *
8229      * @memberOf _.support
8230      * @type boolean
8231      */
8232     support.ownLast = props[0] != 'x';
8233
8234     /**
8235      * Detect if `Array#shift` and `Array#splice` augment array-like objects
8236      * correctly.
8237      *
8238      * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
8239      * `shift()` and `splice()` functions that fail to remove the last element,
8240      * `value[0]`, of array-like objects even though the "length" property is
8241      * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
8242      * while `splice()` is buggy regardless of mode in IE < 9.
8243      *
8244      * @memberOf _.support
8245      * @type boolean
8246      */
8247     support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
8248
8249     /**
8250      * Detect lack of support for accessing string characters by index.
8251      *
8252      * IE < 8 can't access characters by index. IE 8 can only access characters
8253      * by index on string literals, not string objects.
8254      *
8255      * @memberOf _.support
8256      * @type boolean
8257      */
8258     support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
8259   }(1, 0));
8260
8261   /*------------------------------------------------------------------------*/
8262
8263   /**
8264    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8265    *
8266    * @private
8267    * @param {*} value The value to wrap.
8268    */
8269   function LazyWrapper(value) {
8270     this.__wrapped__ = value;
8271     this.__actions__ = null;
8272     this.__dir__ = 1;
8273     this.__dropCount__ = 0;
8274     this.__filtered__ = false;
8275     this.__iteratees__ = null;
8276     this.__takeCount__ = POSITIVE_INFINITY;
8277     this.__views__ = null;
8278   }
8279
8280   /**
8281    * Creates a clone of the lazy wrapper object.
8282    *
8283    * @private
8284    * @name clone
8285    * @memberOf LazyWrapper
8286    * @returns {Object} Returns the cloned `LazyWrapper` object.
8287    */
8288   function lazyClone() {
8289     var actions = this.__actions__,
8290         iteratees = this.__iteratees__,
8291         views = this.__views__,
8292         result = new LazyWrapper(this.__wrapped__);
8293
8294     result.__actions__ = actions ? arrayCopy(actions) : null;
8295     result.__dir__ = this.__dir__;
8296     result.__filtered__ = this.__filtered__;
8297     result.__iteratees__ = iteratees ? arrayCopy(iteratees) : null;
8298     result.__takeCount__ = this.__takeCount__;
8299     result.__views__ = views ? arrayCopy(views) : null;
8300     return result;
8301   }
8302
8303   /**
8304    * Reverses the direction of lazy iteration.
8305    *
8306    * @private
8307    * @name reverse
8308    * @memberOf LazyWrapper
8309    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8310    */
8311   function lazyReverse() {
8312     if (this.__filtered__) {
8313       var result = new LazyWrapper(this);
8314       result.__dir__ = -1;
8315       result.__filtered__ = true;
8316     } else {
8317       result = this.clone();
8318       result.__dir__ *= -1;
8319     }
8320     return result;
8321   }
8322
8323   /**
8324    * Extracts the unwrapped value from its lazy wrapper.
8325    *
8326    * @private
8327    * @name value
8328    * @memberOf LazyWrapper
8329    * @returns {*} Returns the unwrapped value.
8330    */
8331   function lazyValue() {
8332     var array = this.__wrapped__.value();
8333     if (!isArray(array)) {
8334       return baseWrapperValue(array, this.__actions__);
8335     }
8336     var dir = this.__dir__,
8337         isRight = dir < 0,
8338         view = getView(0, array.length, this.__views__),
8339         start = view.start,
8340         end = view.end,
8341         length = end - start,
8342         index = isRight ? end : (start - 1),
8343         takeCount = nativeMin(length, this.__takeCount__),
8344         iteratees = this.__iteratees__,
8345         iterLength = iteratees ? iteratees.length : 0,
8346         resIndex = 0,
8347         result = [];
8348
8349     outer:
8350     while (length-- && resIndex < takeCount) {
8351       index += dir;
8352
8353       var iterIndex = -1,
8354           value = array[index];
8355
8356       while (++iterIndex < iterLength) {
8357         var data = iteratees[iterIndex],
8358             iteratee = data.iteratee,
8359             type = data.type;
8360
8361         if (type == LAZY_DROP_WHILE_FLAG) {
8362           if (data.done && (isRight ? (index > data.index) : (index < data.index))) {
8363             data.count = 0;
8364             data.done = false;
8365           }
8366           data.index = index;
8367           if (!data.done) {
8368             var limit = data.limit;
8369             if (!(data.done = limit > -1 ? (data.count++ >= limit) : !iteratee(value))) {
8370               continue outer;
8371             }
8372           }
8373         } else {
8374           var computed = iteratee(value);
8375           if (type == LAZY_MAP_FLAG) {
8376             value = computed;
8377           } else if (!computed) {
8378             if (type == LAZY_FILTER_FLAG) {
8379               continue outer;
8380             } else {
8381               break outer;
8382             }
8383           }
8384         }
8385       }
8386       result[resIndex++] = value;
8387     }
8388     return result;
8389   }
8390
8391   /*------------------------------------------------------------------------*/
8392
8393   /**
8394    *
8395    * Creates a cache object to store unique values.
8396    *
8397    * @private
8398    * @param {Array} [values] The values to cache.
8399    */
8400   function SetCache(values) {
8401     var length = values ? values.length : 0;
8402
8403     this.data = { 'hash': nativeCreate(null), 'set': new Set };
8404     while (length--) {
8405       this.push(values[length]);
8406     }
8407   }
8408
8409   /**
8410    * Checks if `value` is in `cache` mimicking the return signature of
8411    * `_.indexOf` by returning `0` if the value is found, else `-1`.
8412    *
8413    * @private
8414    * @param {Object} cache The cache to search.
8415    * @param {*} value The value to search for.
8416    * @returns {number} Returns `0` if `value` is found, else `-1`.
8417    */
8418   function cacheIndexOf(cache, value) {
8419     var data = cache.data,
8420         result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
8421
8422     return result ? 0 : -1;
8423   }
8424
8425   /**
8426    * Adds `value` to the cache.
8427    *
8428    * @private
8429    * @name push
8430    * @memberOf SetCache
8431    * @param {*} value The value to cache.
8432    */
8433   function cachePush(value) {
8434     var data = this.data;
8435     if (typeof value == 'string' || isObject(value)) {
8436       data.set.add(value);
8437     } else {
8438       data.hash[value] = true;
8439     }
8440   }
8441
8442   /*------------------------------------------------------------------------*/
8443
8444   /**
8445    * Copies the values of `source` to `array`.
8446    *
8447    * @private
8448    * @param {Array} source The array to copy values from.
8449    * @param {Array} [array=[]] The array to copy values to.
8450    * @returns {Array} Returns `array`.
8451    */
8452   function arrayCopy(source, array) {
8453     var index = -1,
8454         length = source.length;
8455
8456     array || (array = Array(length));
8457     while (++index < length) {
8458       array[index] = source[index];
8459     }
8460     return array;
8461   }
8462
8463   /**
8464    * A specialized version of `_.forEach` for arrays without support for callback
8465    * shorthands and `this` binding.
8466    *
8467    * @private
8468    * @param {Array} array The array to iterate over.
8469    * @param {Function} iteratee The function invoked per iteration.
8470    * @returns {Array} Returns `array`.
8471    */
8472   function arrayEach(array, iteratee) {
8473     var index = -1,
8474         length = array.length;
8475
8476     while (++index < length) {
8477       if (iteratee(array[index], index, array) === false) {
8478         break;
8479       }
8480     }
8481     return array;
8482   }
8483
8484   /**
8485    * A specialized version of `_.every` for arrays without support for callback
8486    * shorthands and `this` binding.
8487    *
8488    * @private
8489    * @param {Array} array The array to iterate over.
8490    * @param {Function} predicate The function invoked per iteration.
8491    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8492    *  else `false`.
8493    */
8494   function arrayEvery(array, predicate) {
8495     var index = -1,
8496         length = array.length;
8497
8498     while (++index < length) {
8499       if (!predicate(array[index], index, array)) {
8500         return false;
8501       }
8502     }
8503     return true;
8504   }
8505
8506   /**
8507    * A specialized version of `_.filter` for arrays without support for callback
8508    * shorthands and `this` binding.
8509    *
8510    * @private
8511    * @param {Array} array The array to iterate over.
8512    * @param {Function} predicate The function invoked per iteration.
8513    * @returns {Array} Returns the new filtered array.
8514    */
8515   function arrayFilter(array, predicate) {
8516     var index = -1,
8517         length = array.length,
8518         resIndex = -1,
8519         result = [];
8520
8521     while (++index < length) {
8522       var value = array[index];
8523       if (predicate(value, index, array)) {
8524         result[++resIndex] = value;
8525       }
8526     }
8527     return result;
8528   }
8529
8530   /**
8531    * A specialized version of `_.map` for arrays without support for callback
8532    * shorthands and `this` binding.
8533    *
8534    * @private
8535    * @param {Array} array The array to iterate over.
8536    * @param {Function} iteratee The function invoked per iteration.
8537    * @returns {Array} Returns the new mapped array.
8538    */
8539   function arrayMap(array, iteratee) {
8540     var index = -1,
8541         length = array.length,
8542         result = Array(length);
8543
8544     while (++index < length) {
8545       result[index] = iteratee(array[index], index, array);
8546     }
8547     return result;
8548   }
8549
8550   /**
8551    * A specialized version of `_.reduce` for arrays without support for callback
8552    * shorthands and `this` binding.
8553    *
8554    * @private
8555    * @param {Array} array The array to iterate over.
8556    * @param {Function} iteratee The function invoked per iteration.
8557    * @param {*} [accumulator] The initial value.
8558    * @param {boolean} [initFromArray] Specify using the first element of `array`
8559    *  as the initial value.
8560    * @returns {*} Returns the accumulated value.
8561    */
8562   function arrayReduce(array, iteratee, accumulator, initFromArray) {
8563     var index = -1,
8564         length = array.length;
8565
8566     if (initFromArray && length) {
8567       accumulator = array[++index];
8568     }
8569     while (++index < length) {
8570       accumulator = iteratee(accumulator, array[index], index, array);
8571     }
8572     return accumulator;
8573   }
8574
8575   /**
8576    * A specialized version of `_.some` for arrays without support for callback
8577    * shorthands and `this` binding.
8578    *
8579    * @private
8580    * @param {Array} array The array to iterate over.
8581    * @param {Function} predicate The function invoked per iteration.
8582    * @returns {boolean} Returns `true` if any element passes the predicate check,
8583    *  else `false`.
8584    */
8585   function arraySome(array, predicate) {
8586     var index = -1,
8587         length = array.length;
8588
8589     while (++index < length) {
8590       if (predicate(array[index], index, array)) {
8591         return true;
8592       }
8593     }
8594     return false;
8595   }
8596
8597   /**
8598    * A specialized version of `_.assign` for customizing assigned values without
8599    * support for argument juggling, multiple sources, and `this` binding `customizer`
8600    * functions.
8601    *
8602    * @private
8603    * @param {Object} object The destination object.
8604    * @param {Object} source The source object.
8605    * @param {Function} customizer The function to customize assigned values.
8606    * @returns {Object} Returns `object`.
8607    */
8608   function assignWith(object, source, customizer) {
8609     var index = -1,
8610         props = keys(source),
8611         length = props.length;
8612
8613     while (++index < length) {
8614       var key = props[index],
8615           value = object[key],
8616           result = customizer(value, source[key], key, object, source);
8617
8618       if ((result === result ? (result !== value) : (value === value)) ||
8619           (value === undefined && !(key in object))) {
8620         object[key] = result;
8621       }
8622     }
8623     return object;
8624   }
8625
8626   /**
8627    * The base implementation of `_.assign` without support for argument juggling,
8628    * multiple sources, and `customizer` functions.
8629    *
8630    * @private
8631    * @param {Object} object The destination object.
8632    * @param {Object} source The source object.
8633    * @returns {Object} Returns `object`.
8634    */
8635   function baseAssign(object, source) {
8636     return source == null
8637       ? object
8638       : baseCopy(source, keys(source), object);
8639   }
8640
8641   /**
8642    * Copies properties of `source` to `object`.
8643    *
8644    * @private
8645    * @param {Object} source The object to copy properties from.
8646    * @param {Array} props The property names to copy.
8647    * @param {Object} [object={}] The object to copy properties to.
8648    * @returns {Object} Returns `object`.
8649    */
8650   function baseCopy(source, props, object) {
8651     object || (object = {});
8652
8653     var index = -1,
8654         length = props.length;
8655
8656     while (++index < length) {
8657       var key = props[index];
8658       object[key] = source[key];
8659     }
8660     return object;
8661   }
8662
8663   /**
8664    * The base implementation of `_.callback` which supports specifying the
8665    * number of arguments to provide to `func`.
8666    *
8667    * @private
8668    * @param {*} [func=_.identity] The value to convert to a callback.
8669    * @param {*} [thisArg] The `this` binding of `func`.
8670    * @param {number} [argCount] The number of arguments to provide to `func`.
8671    * @returns {Function} Returns the callback.
8672    */
8673   function baseCallback(func, thisArg, argCount) {
8674     var type = typeof func;
8675     if (type == 'function') {
8676       return thisArg === undefined
8677         ? func
8678         : bindCallback(func, thisArg, argCount);
8679     }
8680     if (func == null) {
8681       return identity;
8682     }
8683     if (type == 'object') {
8684       return baseMatches(func);
8685     }
8686     return thisArg === undefined
8687       ? property(func)
8688       : baseMatchesProperty(func, thisArg);
8689   }
8690
8691   /**
8692    * The base implementation of `_.clone` without support for argument juggling
8693    * and `this` binding `customizer` functions.
8694    *
8695    * @private
8696    * @param {*} value The value to clone.
8697    * @param {boolean} [isDeep] Specify a deep clone.
8698    * @param {Function} [customizer] The function to customize cloning values.
8699    * @param {string} [key] The key of `value`.
8700    * @param {Object} [object] The object `value` belongs to.
8701    * @param {Array} [stackA=[]] Tracks traversed source objects.
8702    * @param {Array} [stackB=[]] Associates clones with source counterparts.
8703    * @returns {*} Returns the cloned value.
8704    */
8705   function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
8706     var result;
8707     if (customizer) {
8708       result = object ? customizer(value, key, object) : customizer(value);
8709     }
8710     if (result !== undefined) {
8711       return result;
8712     }
8713     if (!isObject(value)) {
8714       return value;
8715     }
8716     var isArr = isArray(value);
8717     if (isArr) {
8718       result = initCloneArray(value);
8719       if (!isDeep) {
8720         return arrayCopy(value, result);
8721       }
8722     } else {
8723       var tag = objToString.call(value),
8724           isFunc = tag == funcTag;
8725
8726       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
8727         if (isHostObject(value)) {
8728           return object ? value : {};
8729         }
8730         result = initCloneObject(isFunc ? {} : value);
8731         if (!isDeep) {
8732           return baseAssign(result, value);
8733         }
8734       } else {
8735         return cloneableTags[tag]
8736           ? initCloneByTag(value, tag, isDeep)
8737           : (object ? value : {});
8738       }
8739     }
8740     // Check for circular references and return corresponding clone.
8741     stackA || (stackA = []);
8742     stackB || (stackB = []);
8743
8744     var length = stackA.length;
8745     while (length--) {
8746       if (stackA[length] == value) {
8747         return stackB[length];
8748       }
8749     }
8750     // Add the source value to the stack of traversed objects and associate it with its clone.
8751     stackA.push(value);
8752     stackB.push(result);
8753
8754     // Recursively populate clone (susceptible to call stack limits).
8755     (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
8756       result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
8757     });
8758     return result;
8759   }
8760
8761   /**
8762    * The base implementation of `_.create` without support for assigning
8763    * properties to the created object.
8764    *
8765    * @private
8766    * @param {Object} prototype The object to inherit from.
8767    * @returns {Object} Returns the new object.
8768    */
8769   var baseCreate = (function() {
8770     function object() {}
8771     return function(prototype) {
8772       if (isObject(prototype)) {
8773         object.prototype = prototype;
8774         var result = new object;
8775         object.prototype = null;
8776       }
8777       return result || {};
8778     };
8779   }());
8780
8781   /**
8782    * The base implementation of `_.difference` which accepts a single array
8783    * of values to exclude.
8784    *
8785    * @private
8786    * @param {Array} array The array to inspect.
8787    * @param {Array} values The values to exclude.
8788    * @returns {Array} Returns the new array of filtered values.
8789    */
8790   function baseDifference(array, values) {
8791     var length = array ? array.length : 0,
8792         result = [];
8793
8794     if (!length) {
8795       return result;
8796     }
8797     var index = -1,
8798         indexOf = getIndexOf(),
8799         isCommon = indexOf == baseIndexOf,
8800         cache = (isCommon && values.length >= 200) ? createCache(values) : null,
8801         valuesLength = values.length;
8802
8803     if (cache) {
8804       indexOf = cacheIndexOf;
8805       isCommon = false;
8806       values = cache;
8807     }
8808     outer:
8809     while (++index < length) {
8810       var value = array[index];
8811
8812       if (isCommon && value === value) {
8813         var valuesIndex = valuesLength;
8814         while (valuesIndex--) {
8815           if (values[valuesIndex] === value) {
8816             continue outer;
8817           }
8818         }
8819         result.push(value);
8820       }
8821       else if (indexOf(values, value, 0) < 0) {
8822         result.push(value);
8823       }
8824     }
8825     return result;
8826   }
8827
8828   /**
8829    * The base implementation of `_.forEach` without support for callback
8830    * shorthands and `this` binding.
8831    *
8832    * @private
8833    * @param {Array|Object|string} collection The collection to iterate over.
8834    * @param {Function} iteratee The function invoked per iteration.
8835    * @returns {Array|Object|string} Returns `collection`.
8836    */
8837   var baseEach = createBaseEach(baseForOwn);
8838
8839   /**
8840    * The base implementation of `_.every` without support for callback
8841    * shorthands and `this` binding.
8842    *
8843    * @private
8844    * @param {Array|Object|string} collection The collection to iterate over.
8845    * @param {Function} predicate The function invoked per iteration.
8846    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8847    *  else `false`
8848    */
8849   function baseEvery(collection, predicate) {
8850     var result = true;
8851     baseEach(collection, function(value, index, collection) {
8852       result = !!predicate(value, index, collection);
8853       return result;
8854     });
8855     return result;
8856   }
8857
8858   /**
8859    * The base implementation of `_.filter` without support for callback
8860    * shorthands and `this` binding.
8861    *
8862    * @private
8863    * @param {Array|Object|string} collection The collection to iterate over.
8864    * @param {Function} predicate The function invoked per iteration.
8865    * @returns {Array} Returns the new filtered array.
8866    */
8867   function baseFilter(collection, predicate) {
8868     var result = [];
8869     baseEach(collection, function(value, index, collection) {
8870       if (predicate(value, index, collection)) {
8871         result.push(value);
8872       }
8873     });
8874     return result;
8875   }
8876
8877   /**
8878    * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
8879    * without support for callback shorthands and `this` binding, which iterates
8880    * over `collection` using the provided `eachFunc`.
8881    *
8882    * @private
8883    * @param {Array|Object|string} collection The collection to search.
8884    * @param {Function} predicate The function invoked per iteration.
8885    * @param {Function} eachFunc The function to iterate over `collection`.
8886    * @param {boolean} [retKey] Specify returning the key of the found element
8887    *  instead of the element itself.
8888    * @returns {*} Returns the found element or its key, else `undefined`.
8889    */
8890   function baseFind(collection, predicate, eachFunc, retKey) {
8891     var result;
8892     eachFunc(collection, function(value, key, collection) {
8893       if (predicate(value, key, collection)) {
8894         result = retKey ? key : value;
8895         return false;
8896       }
8897     });
8898     return result;
8899   }
8900
8901   /**
8902    * The base implementation of `_.flatten` with added support for restricting
8903    * flattening and specifying the start index.
8904    *
8905    * @private
8906    * @param {Array} array The array to flatten.
8907    * @param {boolean} [isDeep] Specify a deep flatten.
8908    * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
8909    * @returns {Array} Returns the new flattened array.
8910    */
8911   function baseFlatten(array, isDeep, isStrict) {
8912     var index = -1,
8913         length = array.length,
8914         resIndex = -1,
8915         result = [];
8916
8917     while (++index < length) {
8918       var value = array[index];
8919       if (isObjectLike(value) && isArrayLike(value) &&
8920           (isStrict || isArray(value) || isArguments(value))) {
8921         if (isDeep) {
8922           // Recursively flatten arrays (susceptible to call stack limits).
8923           value = baseFlatten(value, isDeep, isStrict);
8924         }
8925         var valIndex = -1,
8926             valLength = value.length;
8927
8928         while (++valIndex < valLength) {
8929           result[++resIndex] = value[valIndex];
8930         }
8931       } else if (!isStrict) {
8932         result[++resIndex] = value;
8933       }
8934     }
8935     return result;
8936   }
8937
8938   /**
8939    * The base implementation of `baseForIn` and `baseForOwn` which iterates
8940    * over `object` properties returned by `keysFunc` invoking `iteratee` for
8941    * each property. Iteratee functions may exit iteration early by explicitly
8942    * returning `false`.
8943    *
8944    * @private
8945    * @param {Object} object The object to iterate over.
8946    * @param {Function} iteratee The function invoked per iteration.
8947    * @param {Function} keysFunc The function to get the keys of `object`.
8948    * @returns {Object} Returns `object`.
8949    */
8950   var baseFor = createBaseFor();
8951
8952   /**
8953    * The base implementation of `_.forIn` without support for callback
8954    * shorthands and `this` binding.
8955    *
8956    * @private
8957    * @param {Object} object The object to iterate over.
8958    * @param {Function} iteratee The function invoked per iteration.
8959    * @returns {Object} Returns `object`.
8960    */
8961   function baseForIn(object, iteratee) {
8962     return baseFor(object, iteratee, keysIn);
8963   }
8964
8965   /**
8966    * The base implementation of `_.forOwn` without support for callback
8967    * shorthands and `this` binding.
8968    *
8969    * @private
8970    * @param {Object} object The object to iterate over.
8971    * @param {Function} iteratee The function invoked per iteration.
8972    * @returns {Object} Returns `object`.
8973    */
8974   function baseForOwn(object, iteratee) {
8975     return baseFor(object, iteratee, keys);
8976   }
8977
8978   /**
8979    * The base implementation of `_.functions` which creates an array of
8980    * `object` function property names filtered from those provided.
8981    *
8982    * @private
8983    * @param {Object} object The object to inspect.
8984    * @param {Array} props The property names to filter.
8985    * @returns {Array} Returns the new array of filtered property names.
8986    */
8987   function baseFunctions(object, props) {
8988     var index = -1,
8989         length = props.length,
8990         resIndex = -1,
8991         result = [];
8992
8993     while (++index < length) {
8994       var key = props[index];
8995       if (isFunction(object[key])) {
8996         result[++resIndex] = key;
8997       }
8998     }
8999     return result;
9000   }
9001
9002   /**
9003    * The base implementation of `get` without support for string paths
9004    * and default values.
9005    *
9006    * @private
9007    * @param {Object} object The object to query.
9008    * @param {Array} path The path of the property to get.
9009    * @param {string} [pathKey] The key representation of path.
9010    * @returns {*} Returns the resolved value.
9011    */
9012   function baseGet(object, path, pathKey) {
9013     if (object == null) {
9014       return;
9015     }
9016     object = toObject(object);
9017     if (pathKey !== undefined && pathKey in object) {
9018       path = [pathKey];
9019     }
9020     var index = 0,
9021         length = path.length;
9022
9023     while (object != null && index < length) {
9024       object = toObject(object)[path[index++]];
9025     }
9026     return (index && index == length) ? object : undefined;
9027   }
9028
9029   /**
9030    * The base implementation of `_.isEqual` without support for `this` binding
9031    * `customizer` functions.
9032    *
9033    * @private
9034    * @param {*} value The value to compare.
9035    * @param {*} other The other value to compare.
9036    * @param {Function} [customizer] The function to customize comparing values.
9037    * @param {boolean} [isLoose] Specify performing partial comparisons.
9038    * @param {Array} [stackA] Tracks traversed `value` objects.
9039    * @param {Array} [stackB] Tracks traversed `other` objects.
9040    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9041    */
9042   function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
9043     if (value === other) {
9044       return true;
9045     }
9046     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9047       return value !== value && other !== other;
9048     }
9049     return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
9050   }
9051
9052   /**
9053    * A specialized version of `baseIsEqual` for arrays and objects which performs
9054    * deep comparisons and tracks traversed objects enabling objects with circular
9055    * references to be compared.
9056    *
9057    * @private
9058    * @param {Object} object The object to compare.
9059    * @param {Object} other The other object to compare.
9060    * @param {Function} equalFunc The function to determine equivalents of values.
9061    * @param {Function} [customizer] The function to customize comparing objects.
9062    * @param {boolean} [isLoose] Specify performing partial comparisons.
9063    * @param {Array} [stackA=[]] Tracks traversed `value` objects.
9064    * @param {Array} [stackB=[]] Tracks traversed `other` objects.
9065    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9066    */
9067   function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
9068     var objIsArr = isArray(object),
9069         othIsArr = isArray(other),
9070         objTag = arrayTag,
9071         othTag = arrayTag;
9072
9073     if (!objIsArr) {
9074       objTag = objToString.call(object);
9075       if (objTag == argsTag) {
9076         objTag = objectTag;
9077       } else if (objTag != objectTag) {
9078         objIsArr = isTypedArray(object);
9079       }
9080     }
9081     if (!othIsArr) {
9082       othTag = objToString.call(other);
9083       if (othTag == argsTag) {
9084         othTag = objectTag;
9085       } else if (othTag != objectTag) {
9086         othIsArr = isTypedArray(other);
9087       }
9088     }
9089     var objIsObj = objTag == objectTag && !isHostObject(object),
9090         othIsObj = othTag == objectTag && !isHostObject(other),
9091         isSameTag = objTag == othTag;
9092
9093     if (isSameTag && !(objIsArr || objIsObj)) {
9094       return equalByTag(object, other, objTag);
9095     }
9096     if (!isLoose) {
9097       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9098           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9099
9100       if (objIsWrapped || othIsWrapped) {
9101         return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
9102       }
9103     }
9104     if (!isSameTag) {
9105       return false;
9106     }
9107     // Assume cyclic values are equal.
9108     // For more information on detecting circular references see https://es5.github.io/#JO.
9109     stackA || (stackA = []);
9110     stackB || (stackB = []);
9111
9112     var length = stackA.length;
9113     while (length--) {
9114       if (stackA[length] == object) {
9115         return stackB[length] == other;
9116       }
9117     }
9118     // Add `object` and `other` to the stack of traversed objects.
9119     stackA.push(object);
9120     stackB.push(other);
9121
9122     var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
9123
9124     stackA.pop();
9125     stackB.pop();
9126
9127     return result;
9128   }
9129
9130   /**
9131    * The base implementation of `_.isMatch` without support for callback
9132    * shorthands and `this` binding.
9133    *
9134    * @private
9135    * @param {Object} object The object to inspect.
9136    * @param {Array} matchData The propery names, values, and compare flags to match.
9137    * @param {Function} [customizer] The function to customize comparing objects.
9138    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9139    */
9140   function baseIsMatch(object, matchData, customizer) {
9141     var index = matchData.length,
9142         length = index,
9143         noCustomizer = !customizer;
9144
9145     if (object == null) {
9146       return !length;
9147     }
9148     object = toObject(object);
9149     while (index--) {
9150       var data = matchData[index];
9151       if ((noCustomizer && data[2])
9152             ? data[1] !== object[data[0]]
9153             : !(data[0] in object)
9154           ) {
9155         return false;
9156       }
9157     }
9158     while (++index < length) {
9159       data = matchData[index];
9160       var key = data[0],
9161           objValue = object[key],
9162           srcValue = data[1];
9163
9164       if (noCustomizer && data[2]) {
9165         if (objValue === undefined && !(key in object)) {
9166           return false;
9167         }
9168       } else {
9169         var result = customizer ? customizer(objValue, srcValue, key) : undefined;
9170         if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
9171           return false;
9172         }
9173       }
9174     }
9175     return true;
9176   }
9177
9178   /**
9179    * The base implementation of `_.map` without support for callback shorthands
9180    * and `this` binding.
9181    *
9182    * @private
9183    * @param {Array|Object|string} collection The collection to iterate over.
9184    * @param {Function} iteratee The function invoked per iteration.
9185    * @returns {Array} Returns the new mapped array.
9186    */
9187   function baseMap(collection, iteratee) {
9188     var index = -1,
9189         result = isArrayLike(collection) ? Array(collection.length) : [];
9190
9191     baseEach(collection, function(value, key, collection) {
9192       result[++index] = iteratee(value, key, collection);
9193     });
9194     return result;
9195   }
9196
9197   /**
9198    * The base implementation of `_.matches` which does not clone `source`.
9199    *
9200    * @private
9201    * @param {Object} source The object of property values to match.
9202    * @returns {Function} Returns the new function.
9203    */
9204   function baseMatches(source) {
9205     var matchData = getMatchData(source);
9206     if (matchData.length == 1 && matchData[0][2]) {
9207       var key = matchData[0][0],
9208           value = matchData[0][1];
9209
9210       return function(object) {
9211         if (object == null) {
9212           return false;
9213         }
9214         object = toObject(object);
9215         return object[key] === value && (value !== undefined || (key in object));
9216       };
9217     }
9218     return function(object) {
9219       return baseIsMatch(object, matchData);
9220     };
9221   }
9222
9223   /**
9224    * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
9225    *
9226    * @private
9227    * @param {string} path The path of the property to get.
9228    * @param {*} srcValue The value to compare.
9229    * @returns {Function} Returns the new function.
9230    */
9231   function baseMatchesProperty(path, srcValue) {
9232     var isArr = isArray(path),
9233         isCommon = isKey(path) && isStrictComparable(srcValue),
9234         pathKey = (path + '');
9235
9236     path = toPath(path);
9237     return function(object) {
9238       if (object == null) {
9239         return false;
9240       }
9241       var key = pathKey;
9242       object = toObject(object);
9243       if ((isArr || !isCommon) && !(key in object)) {
9244         object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
9245         if (object == null) {
9246           return false;
9247         }
9248         key = last(path);
9249         object = toObject(object);
9250       }
9251       return object[key] === srcValue
9252         ? (srcValue !== undefined || (key in object))
9253         : baseIsEqual(srcValue, object[key], undefined, true);
9254     };
9255   }
9256
9257   /**
9258    * The base implementation of `_.merge` without support for argument juggling,
9259    * multiple sources, and `this` binding `customizer` functions.
9260    *
9261    * @private
9262    * @param {Object} object The destination object.
9263    * @param {Object} source The source object.
9264    * @param {Function} [customizer] The function to customize merging properties.
9265    * @param {Array} [stackA=[]] Tracks traversed source objects.
9266    * @param {Array} [stackB=[]] Associates values with source counterparts.
9267    * @returns {Object} Returns `object`.
9268    */
9269   function baseMerge(object, source, customizer, stackA, stackB) {
9270     if (!isObject(object)) {
9271       return object;
9272     }
9273     var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
9274         props = isSrcArr ? null : keys(source);
9275
9276     arrayEach(props || source, function(srcValue, key) {
9277       if (props) {
9278         key = srcValue;
9279         srcValue = source[key];
9280       }
9281       if (isObjectLike(srcValue)) {
9282         stackA || (stackA = []);
9283         stackB || (stackB = []);
9284         baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
9285       }
9286       else {
9287         var value = object[key],
9288             result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9289             isCommon = result === undefined;
9290
9291         if (isCommon) {
9292           result = srcValue;
9293         }
9294         if ((result !== undefined || (isSrcArr && !(key in object))) &&
9295             (isCommon || (result === result ? (result !== value) : (value === value)))) {
9296           object[key] = result;
9297         }
9298       }
9299     });
9300     return object;
9301   }
9302
9303   /**
9304    * A specialized version of `baseMerge` for arrays and objects which performs
9305    * deep merges and tracks traversed objects enabling objects with circular
9306    * references to be merged.
9307    *
9308    * @private
9309    * @param {Object} object The destination object.
9310    * @param {Object} source The source object.
9311    * @param {string} key The key of the value to merge.
9312    * @param {Function} mergeFunc The function to merge values.
9313    * @param {Function} [customizer] The function to customize merging properties.
9314    * @param {Array} [stackA=[]] Tracks traversed source objects.
9315    * @param {Array} [stackB=[]] Associates values with source counterparts.
9316    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9317    */
9318   function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
9319     var length = stackA.length,
9320         srcValue = source[key];
9321
9322     while (length--) {
9323       if (stackA[length] == srcValue) {
9324         object[key] = stackB[length];
9325         return;
9326       }
9327     }
9328     var value = object[key],
9329         result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9330         isCommon = result === undefined;
9331
9332     if (isCommon) {
9333       result = srcValue;
9334       if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
9335         result = isArray(value)
9336           ? value
9337           : (isArrayLike(value) ? arrayCopy(value) : []);
9338       }
9339       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
9340         result = isArguments(value)
9341           ? toPlainObject(value)
9342           : (isPlainObject(value) ? value : {});
9343       }
9344       else {
9345         isCommon = false;
9346       }
9347     }
9348     // Add the source value to the stack of traversed objects and associate
9349     // it with its merged value.
9350     stackA.push(srcValue);
9351     stackB.push(result);
9352
9353     if (isCommon) {
9354       // Recursively merge objects and arrays (susceptible to call stack limits).
9355       object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
9356     } else if (result === result ? (result !== value) : (value === value)) {
9357       object[key] = result;
9358     }
9359   }
9360
9361   /**
9362    * The base implementation of `_.property` without support for deep paths.
9363    *
9364    * @private
9365    * @param {string} key The key of the property to get.
9366    * @returns {Function} Returns the new function.
9367    */
9368   function baseProperty(key) {
9369     return function(object) {
9370       return object == null ? undefined : toObject(object)[key];
9371     };
9372   }
9373
9374   /**
9375    * A specialized version of `baseProperty` which supports deep paths.
9376    *
9377    * @private
9378    * @param {Array|string} path The path of the property to get.
9379    * @returns {Function} Returns the new function.
9380    */
9381   function basePropertyDeep(path) {
9382     var pathKey = (path + '');
9383     path = toPath(path);
9384     return function(object) {
9385       return baseGet(object, path, pathKey);
9386     };
9387   }
9388
9389   /**
9390    * The base implementation of `_.reduce` and `_.reduceRight` without support
9391    * for callback shorthands and `this` binding, which iterates over `collection`
9392    * using the provided `eachFunc`.
9393    *
9394    * @private
9395    * @param {Array|Object|string} collection The collection to iterate over.
9396    * @param {Function} iteratee The function invoked per iteration.
9397    * @param {*} accumulator The initial value.
9398    * @param {boolean} initFromCollection Specify using the first or last element
9399    *  of `collection` as the initial value.
9400    * @param {Function} eachFunc The function to iterate over `collection`.
9401    * @returns {*} Returns the accumulated value.
9402    */
9403   function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
9404     eachFunc(collection, function(value, index, collection) {
9405       accumulator = initFromCollection
9406         ? (initFromCollection = false, value)
9407         : iteratee(accumulator, value, index, collection);
9408     });
9409     return accumulator;
9410   }
9411
9412   /**
9413    * The base implementation of `setData` without support for hot loop detection.
9414    *
9415    * @private
9416    * @param {Function} func The function to associate metadata with.
9417    * @param {*} data The metadata.
9418    * @returns {Function} Returns `func`.
9419    */
9420   var baseSetData = !metaMap ? identity : function(func, data) {
9421     metaMap.set(func, data);
9422     return func;
9423   };
9424
9425   /**
9426    * The base implementation of `_.slice` without an iteratee call guard.
9427    *
9428    * @private
9429    * @param {Array} array The array to slice.
9430    * @param {number} [start=0] The start position.
9431    * @param {number} [end=array.length] The end position.
9432    * @returns {Array} Returns the slice of `array`.
9433    */
9434   function baseSlice(array, start, end) {
9435     var index = -1,
9436         length = array.length;
9437
9438     start = start == null ? 0 : (+start || 0);
9439     if (start < 0) {
9440       start = -start > length ? 0 : (length + start);
9441     }
9442     end = (end === undefined || end > length) ? length : (+end || 0);
9443     if (end < 0) {
9444       end += length;
9445     }
9446     length = start > end ? 0 : ((end - start) >>> 0);
9447     start >>>= 0;
9448
9449     var result = Array(length);
9450     while (++index < length) {
9451       result[index] = array[index + start];
9452     }
9453     return result;
9454   }
9455
9456   /**
9457    * The base implementation of `_.some` without support for callback shorthands
9458    * and `this` binding.
9459    *
9460    * @private
9461    * @param {Array|Object|string} collection The collection to iterate over.
9462    * @param {Function} predicate The function invoked per iteration.
9463    * @returns {boolean} Returns `true` if any element passes the predicate check,
9464    *  else `false`.
9465    */
9466   function baseSome(collection, predicate) {
9467     var result;
9468
9469     baseEach(collection, function(value, index, collection) {
9470       result = predicate(value, index, collection);
9471       return !result;
9472     });
9473     return !!result;
9474   }
9475
9476   /**
9477    * The base implementation of `_.uniq` without support for callback shorthands
9478    * and `this` binding.
9479    *
9480    * @private
9481    * @param {Array} array The array to inspect.
9482    * @param {Function} [iteratee] The function invoked per iteration.
9483    * @returns {Array} Returns the new duplicate-value-free array.
9484    */
9485   function baseUniq(array, iteratee) {
9486     var index = -1,
9487         indexOf = getIndexOf(),
9488         length = array.length,
9489         isCommon = indexOf == baseIndexOf,
9490         isLarge = isCommon && length >= 200,
9491         seen = isLarge ? createCache() : null,
9492         result = [];
9493
9494     if (seen) {
9495       indexOf = cacheIndexOf;
9496       isCommon = false;
9497     } else {
9498       isLarge = false;
9499       seen = iteratee ? [] : result;
9500     }
9501     outer:
9502     while (++index < length) {
9503       var value = array[index],
9504           computed = iteratee ? iteratee(value, index, array) : value;
9505
9506       if (isCommon && value === value) {
9507         var seenIndex = seen.length;
9508         while (seenIndex--) {
9509           if (seen[seenIndex] === computed) {
9510             continue outer;
9511           }
9512         }
9513         if (iteratee) {
9514           seen.push(computed);
9515         }
9516         result.push(value);
9517       }
9518       else if (indexOf(seen, computed, 0) < 0) {
9519         if (iteratee || isLarge) {
9520           seen.push(computed);
9521         }
9522         result.push(value);
9523       }
9524     }
9525     return result;
9526   }
9527
9528   /**
9529    * The base implementation of `_.values` and `_.valuesIn` which creates an
9530    * array of `object` property values corresponding to the property names
9531    * of `props`.
9532    *
9533    * @private
9534    * @param {Object} object The object to query.
9535    * @param {Array} props The property names to get values for.
9536    * @returns {Object} Returns the array of property values.
9537    */
9538   function baseValues(object, props) {
9539     var index = -1,
9540         length = props.length,
9541         result = Array(length);
9542
9543     while (++index < length) {
9544       result[index] = object[props[index]];
9545     }
9546     return result;
9547   }
9548
9549   /**
9550    * The base implementation of `wrapperValue` which returns the result of
9551    * performing a sequence of actions on the unwrapped `value`, where each
9552    * successive action is supplied the return value of the previous.
9553    *
9554    * @private
9555    * @param {*} value The unwrapped value.
9556    * @param {Array} actions Actions to peform to resolve the unwrapped value.
9557    * @returns {*} Returns the resolved value.
9558    */
9559   function baseWrapperValue(value, actions) {
9560     var result = value;
9561     if (result instanceof LazyWrapper) {
9562       result = result.value();
9563     }
9564     var index = -1,
9565         length = actions.length;
9566
9567     while (++index < length) {
9568       var args = [result],
9569           action = actions[index];
9570
9571       push.apply(args, action.args);
9572       result = action.func.apply(action.thisArg, args);
9573     }
9574     return result;
9575   }
9576
9577   /**
9578    * Performs a binary search of `array` to determine the index at which `value`
9579    * should be inserted into `array` in order to maintain its sort order.
9580    *
9581    * @private
9582    * @param {Array} array The sorted array to inspect.
9583    * @param {*} value The value to evaluate.
9584    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9585    * @returns {number} Returns the index at which `value` should be inserted
9586    *  into `array`.
9587    */
9588   function binaryIndex(array, value, retHighest) {
9589     var low = 0,
9590         high = array ? array.length : low;
9591
9592     if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
9593       while (low < high) {
9594         var mid = (low + high) >>> 1,
9595             computed = array[mid];
9596
9597         if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
9598           low = mid + 1;
9599         } else {
9600           high = mid;
9601         }
9602       }
9603       return high;
9604     }
9605     return binaryIndexBy(array, value, identity, retHighest);
9606   }
9607
9608   /**
9609    * This function is like `binaryIndex` except that it invokes `iteratee` for
9610    * `value` and each element of `array` to compute their sort ranking. The
9611    * iteratee is invoked with one argument; (value).
9612    *
9613    * @private
9614    * @param {Array} array The sorted array to inspect.
9615    * @param {*} value The value to evaluate.
9616    * @param {Function} iteratee The function invoked per iteration.
9617    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9618    * @returns {number} Returns the index at which `value` should be inserted
9619    *  into `array`.
9620    */
9621   function binaryIndexBy(array, value, iteratee, retHighest) {
9622     value = iteratee(value);
9623
9624     var low = 0,
9625         high = array ? array.length : 0,
9626         valIsNaN = value !== value,
9627         valIsNull = value === null,
9628         valIsUndef = value === undefined;
9629
9630     while (low < high) {
9631       var mid = floor((low + high) / 2),
9632           computed = iteratee(array[mid]),
9633           isDef = computed !== undefined,
9634           isReflexive = computed === computed;
9635
9636       if (valIsNaN) {
9637         var setLow = isReflexive || retHighest;
9638       } else if (valIsNull) {
9639         setLow = isReflexive && isDef && (retHighest || computed != null);
9640       } else if (valIsUndef) {
9641         setLow = isReflexive && (retHighest || isDef);
9642       } else if (computed == null) {
9643         setLow = false;
9644       } else {
9645         setLow = retHighest ? (computed <= value) : (computed < value);
9646       }
9647       if (setLow) {
9648         low = mid + 1;
9649       } else {
9650         high = mid;
9651       }
9652     }
9653     return nativeMin(high, MAX_ARRAY_INDEX);
9654   }
9655
9656   /**
9657    * A specialized version of `baseCallback` which only supports `this` binding
9658    * and specifying the number of arguments to provide to `func`.
9659    *
9660    * @private
9661    * @param {Function} func The function to bind.
9662    * @param {*} thisArg The `this` binding of `func`.
9663    * @param {number} [argCount] The number of arguments to provide to `func`.
9664    * @returns {Function} Returns the callback.
9665    */
9666   function bindCallback(func, thisArg, argCount) {
9667     if (typeof func != 'function') {
9668       return identity;
9669     }
9670     if (thisArg === undefined) {
9671       return func;
9672     }
9673     switch (argCount) {
9674       case 1: return function(value) {
9675         return func.call(thisArg, value);
9676       };
9677       case 3: return function(value, index, collection) {
9678         return func.call(thisArg, value, index, collection);
9679       };
9680       case 4: return function(accumulator, value, index, collection) {
9681         return func.call(thisArg, accumulator, value, index, collection);
9682       };
9683       case 5: return function(value, other, key, object, source) {
9684         return func.call(thisArg, value, other, key, object, source);
9685       };
9686     }
9687     return function() {
9688       return func.apply(thisArg, arguments);
9689     };
9690   }
9691
9692   /**
9693    * Creates a clone of the given array buffer.
9694    *
9695    * @private
9696    * @param {ArrayBuffer} buffer The array buffer to clone.
9697    * @returns {ArrayBuffer} Returns the cloned array buffer.
9698    */
9699   function bufferClone(buffer) {
9700     return bufferSlice.call(buffer, 0);
9701   }
9702   if (!bufferSlice) {
9703     // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
9704     bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
9705       var byteLength = buffer.byteLength,
9706           floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
9707           offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
9708           result = new ArrayBuffer(byteLength);
9709
9710       if (floatLength) {
9711         var view = new Float64Array(result, 0, floatLength);
9712         view.set(new Float64Array(buffer, 0, floatLength));
9713       }
9714       if (byteLength != offset) {
9715         view = new Uint8Array(result, offset);
9716         view.set(new Uint8Array(buffer, offset));
9717       }
9718       return result;
9719     };
9720   }
9721
9722   /**
9723    * Creates an array that is the composition of partially applied arguments,
9724    * placeholders, and provided arguments into a single array of arguments.
9725    *
9726    * @private
9727    * @param {Array|Object} args The provided arguments.
9728    * @param {Array} partials The arguments to prepend to those provided.
9729    * @param {Array} holders The `partials` placeholder indexes.
9730    * @returns {Array} Returns the new array of composed arguments.
9731    */
9732   function composeArgs(args, partials, holders) {
9733     var holdersLength = holders.length,
9734         argsIndex = -1,
9735         argsLength = nativeMax(args.length - holdersLength, 0),
9736         leftIndex = -1,
9737         leftLength = partials.length,
9738         result = Array(argsLength + leftLength);
9739
9740     while (++leftIndex < leftLength) {
9741       result[leftIndex] = partials[leftIndex];
9742     }
9743     while (++argsIndex < holdersLength) {
9744       result[holders[argsIndex]] = args[argsIndex];
9745     }
9746     while (argsLength--) {
9747       result[leftIndex++] = args[argsIndex++];
9748     }
9749     return result;
9750   }
9751
9752   /**
9753    * This function is like `composeArgs` except that the arguments composition
9754    * is tailored for `_.partialRight`.
9755    *
9756    * @private
9757    * @param {Array|Object} args The provided arguments.
9758    * @param {Array} partials The arguments to append to those provided.
9759    * @param {Array} holders The `partials` placeholder indexes.
9760    * @returns {Array} Returns the new array of composed arguments.
9761    */
9762   function composeArgsRight(args, partials, holders) {
9763     var holdersIndex = -1,
9764         holdersLength = holders.length,
9765         argsIndex = -1,
9766         argsLength = nativeMax(args.length - holdersLength, 0),
9767         rightIndex = -1,
9768         rightLength = partials.length,
9769         result = Array(argsLength + rightLength);
9770
9771     while (++argsIndex < argsLength) {
9772       result[argsIndex] = args[argsIndex];
9773     }
9774     var offset = argsIndex;
9775     while (++rightIndex < rightLength) {
9776       result[offset + rightIndex] = partials[rightIndex];
9777     }
9778     while (++holdersIndex < holdersLength) {
9779       result[offset + holders[holdersIndex]] = args[argsIndex++];
9780     }
9781     return result;
9782   }
9783
9784   /**
9785    * Creates a function that aggregates a collection, creating an accumulator
9786    * object composed from the results of running each element in the collection
9787    * through an iteratee.
9788    *
9789    * **Note:** This function is used to create `_.countBy`, `_.groupBy`, `_.indexBy`,
9790    * and `_.partition`.
9791    *
9792    * @private
9793    * @param {Function} setter The function to set keys and values of the accumulator object.
9794    * @param {Function} [initializer] The function to initialize the accumulator object.
9795    * @returns {Function} Returns the new aggregator function.
9796    */
9797   function createAggregator(setter, initializer) {
9798     return function(collection, iteratee, thisArg) {
9799       var result = initializer ? initializer() : {};
9800       iteratee = getCallback(iteratee, thisArg, 3);
9801
9802       if (isArray(collection)) {
9803         var index = -1,
9804             length = collection.length;
9805
9806         while (++index < length) {
9807           var value = collection[index];
9808           setter(result, value, iteratee(value, index, collection), collection);
9809         }
9810       } else {
9811         baseEach(collection, function(value, key, collection) {
9812           setter(result, value, iteratee(value, key, collection), collection);
9813         });
9814       }
9815       return result;
9816     };
9817   }
9818
9819   /**
9820    * Creates a function that assigns properties of source object(s) to a given
9821    * destination object.
9822    *
9823    * **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`.
9824    *
9825    * @private
9826    * @param {Function} assigner The function to assign values.
9827    * @returns {Function} Returns the new assigner function.
9828    */
9829   function createAssigner(assigner) {
9830     return restParam(function(object, sources) {
9831       var index = -1,
9832           length = object == null ? 0 : sources.length,
9833           customizer = length > 2 ? sources[length - 2] : undefined,
9834           guard = length > 2 ? sources[2] : undefined,
9835           thisArg = length > 1 ? sources[length - 1] : undefined;
9836
9837       if (typeof customizer == 'function') {
9838         customizer = bindCallback(customizer, thisArg, 5);
9839         length -= 2;
9840       } else {
9841         customizer = typeof thisArg == 'function' ? thisArg : undefined;
9842         length -= (customizer ? 1 : 0);
9843       }
9844       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
9845         customizer = length < 3 ? undefined : customizer;
9846         length = 1;
9847       }
9848       while (++index < length) {
9849         var source = sources[index];
9850         if (source) {
9851           assigner(object, source, customizer);
9852         }
9853       }
9854       return object;
9855     });
9856   }
9857
9858   /**
9859    * Creates a `baseEach` or `baseEachRight` function.
9860    *
9861    * @private
9862    * @param {Function} eachFunc The function to iterate over a collection.
9863    * @param {boolean} [fromRight] Specify iterating from right to left.
9864    * @returns {Function} Returns the new base function.
9865    */
9866   function createBaseEach(eachFunc, fromRight) {
9867     return function(collection, iteratee) {
9868       var length = collection ? getLength(collection) : 0;
9869       if (!isLength(length)) {
9870         return eachFunc(collection, iteratee);
9871       }
9872       var index = fromRight ? length : -1,
9873           iterable = toObject(collection);
9874
9875       while ((fromRight ? index-- : ++index < length)) {
9876         if (iteratee(iterable[index], index, iterable) === false) {
9877           break;
9878         }
9879       }
9880       return collection;
9881     };
9882   }
9883
9884   /**
9885    * Creates a base function for `_.forIn` or `_.forInRight`.
9886    *
9887    * @private
9888    * @param {boolean} [fromRight] Specify iterating from right to left.
9889    * @returns {Function} Returns the new base function.
9890    */
9891   function createBaseFor(fromRight) {
9892     return function(object, iteratee, keysFunc) {
9893       var iterable = toObject(object),
9894           props = keysFunc(object),
9895           length = props.length,
9896           index = fromRight ? length : -1;
9897
9898       while ((fromRight ? index-- : ++index < length)) {
9899         var key = props[index];
9900         if (iteratee(iterable[key], key, iterable) === false) {
9901           break;
9902         }
9903       }
9904       return object;
9905     };
9906   }
9907
9908   /**
9909    * Creates a function that wraps `func` and invokes it with the `this`
9910    * binding of `thisArg`.
9911    *
9912    * @private
9913    * @param {Function} func The function to bind.
9914    * @param {*} [thisArg] The `this` binding of `func`.
9915    * @returns {Function} Returns the new bound function.
9916    */
9917   function createBindWrapper(func, thisArg) {
9918     var Ctor = createCtorWrapper(func);
9919
9920     function wrapper() {
9921       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
9922       return fn.apply(thisArg, arguments);
9923     }
9924     return wrapper;
9925   }
9926
9927   /**
9928    * Creates a `Set` cache object to optimize linear searches of large arrays.
9929    *
9930    * @private
9931    * @param {Array} [values] The values to cache.
9932    * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
9933    */
9934   var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
9935     return new SetCache(values);
9936   };
9937
9938   /**
9939    * Creates a function that produces an instance of `Ctor` regardless of
9940    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
9941    *
9942    * @private
9943    * @param {Function} Ctor The constructor to wrap.
9944    * @returns {Function} Returns the new wrapped function.
9945    */
9946   function createCtorWrapper(Ctor) {
9947     return function() {
9948       // Use a `switch` statement to work with class constructors.
9949       // See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
9950       // for more details.
9951       var args = arguments;
9952       switch (args.length) {
9953         case 0: return new Ctor;
9954         case 1: return new Ctor(args[0]);
9955         case 2: return new Ctor(args[0], args[1]);
9956         case 3: return new Ctor(args[0], args[1], args[2]);
9957         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
9958         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
9959       }
9960       var thisBinding = baseCreate(Ctor.prototype),
9961           result = Ctor.apply(thisBinding, args);
9962
9963       // Mimic the constructor's `return` behavior.
9964       // See https://es5.github.io/#x13.2.2 for more details.
9965       return isObject(result) ? result : thisBinding;
9966     };
9967   }
9968
9969   /**
9970    * Creates a `_.find` or `_.findLast` function.
9971    *
9972    * @private
9973    * @param {Function} eachFunc The function to iterate over a collection.
9974    * @param {boolean} [fromRight] Specify iterating from right to left.
9975    * @returns {Function} Returns the new find function.
9976    */
9977   function createFind(eachFunc, fromRight) {
9978     return function(collection, predicate, thisArg) {
9979       predicate = getCallback(predicate, thisArg, 3);
9980       if (isArray(collection)) {
9981         var index = baseFindIndex(collection, predicate, fromRight);
9982         return index > -1 ? collection[index] : undefined;
9983       }
9984       return baseFind(collection, predicate, eachFunc);
9985     };
9986   }
9987
9988   /**
9989    * Creates a function for `_.forEach` or `_.forEachRight`.
9990    *
9991    * @private
9992    * @param {Function} arrayFunc The function to iterate over an array.
9993    * @param {Function} eachFunc The function to iterate over a collection.
9994    * @returns {Function} Returns the new each function.
9995    */
9996   function createForEach(arrayFunc, eachFunc) {
9997     return function(collection, iteratee, thisArg) {
9998       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
9999         ? arrayFunc(collection, iteratee)
10000         : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
10001     };
10002   }
10003
10004   /**
10005    * Creates a function for `_.forOwn` or `_.forOwnRight`.
10006    *
10007    * @private
10008    * @param {Function} objectFunc The function to iterate over an object.
10009    * @returns {Function} Returns the new each function.
10010    */
10011   function createForOwn(objectFunc) {
10012     return function(object, iteratee, thisArg) {
10013       if (typeof iteratee != 'function' || thisArg !== undefined) {
10014         iteratee = bindCallback(iteratee, thisArg, 3);
10015       }
10016       return objectFunc(object, iteratee);
10017     };
10018   }
10019
10020   /**
10021    * Creates a function for `_.reduce` or `_.reduceRight`.
10022    *
10023    * @private
10024    * @param {Function} arrayFunc The function to iterate over an array.
10025    * @param {Function} eachFunc The function to iterate over a collection.
10026    * @returns {Function} Returns the new each function.
10027    */
10028   function createReduce(arrayFunc, eachFunc) {
10029     return function(collection, iteratee, accumulator, thisArg) {
10030       var initFromArray = arguments.length < 3;
10031       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10032         ? arrayFunc(collection, iteratee, accumulator, initFromArray)
10033         : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
10034     };
10035   }
10036
10037   /**
10038    * Creates a function that wraps `func` and invokes it with optional `this`
10039    * binding of, partial application, and currying.
10040    *
10041    * @private
10042    * @param {Function|string} func The function or method name to reference.
10043    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10044    * @param {*} [thisArg] The `this` binding of `func`.
10045    * @param {Array} [partials] The arguments to prepend to those provided to the new function.
10046    * @param {Array} [holders] The `partials` placeholder indexes.
10047    * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
10048    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10049    * @param {Array} [argPos] The argument positions of the new function.
10050    * @param {number} [ary] The arity cap of `func`.
10051    * @param {number} [arity] The arity of `func`.
10052    * @returns {Function} Returns the new wrapped function.
10053    */
10054   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10055     var isAry = bitmask & ARY_FLAG,
10056         isBind = bitmask & BIND_FLAG,
10057         isBindKey = bitmask & BIND_KEY_FLAG,
10058         isCurry = bitmask & CURRY_FLAG,
10059         isCurryBound = bitmask & CURRY_BOUND_FLAG,
10060         isCurryRight = bitmask & CURRY_RIGHT_FLAG,
10061         Ctor = isBindKey ? null : createCtorWrapper(func);
10062
10063     function wrapper() {
10064       // Avoid `arguments` object use disqualifying optimizations by
10065       // converting it to an array before providing it to other functions.
10066       var length = arguments.length,
10067           index = length,
10068           args = Array(length);
10069
10070       while (index--) {
10071         args[index] = arguments[index];
10072       }
10073       if (partials) {
10074         args = composeArgs(args, partials, holders);
10075       }
10076       if (partialsRight) {
10077         args = composeArgsRight(args, partialsRight, holdersRight);
10078       }
10079       if (isCurry || isCurryRight) {
10080         var placeholder = wrapper.placeholder,
10081             argsHolders = replaceHolders(args, placeholder);
10082
10083         length -= argsHolders.length;
10084         if (length < arity) {
10085           var newArgPos = argPos ? arrayCopy(argPos) : null,
10086               newArity = nativeMax(arity - length, 0),
10087               newsHolders = isCurry ? argsHolders : null,
10088               newHoldersRight = isCurry ? null : argsHolders,
10089               newPartials = isCurry ? args : null,
10090               newPartialsRight = isCurry ? null : args;
10091
10092           bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10093           bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10094
10095           if (!isCurryBound) {
10096             bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10097           }
10098           var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
10099               result = createHybridWrapper.apply(undefined, newData);
10100
10101           if (isLaziable(func)) {
10102             setData(result, newData);
10103           }
10104           result.placeholder = placeholder;
10105           return result;
10106         }
10107       }
10108       var thisBinding = isBind ? thisArg : this,
10109           fn = isBindKey ? thisBinding[func] : func;
10110
10111       if (argPos) {
10112         args = reorder(args, argPos);
10113       }
10114       if (isAry && ary < args.length) {
10115         args.length = ary;
10116       }
10117       if (this && this !== root && this instanceof wrapper) {
10118         fn = Ctor || createCtorWrapper(func);
10119       }
10120       return fn.apply(thisBinding, args);
10121     }
10122     return wrapper;
10123   }
10124
10125   /**
10126    * Creates a function that wraps `func` and invokes it with the optional `this`
10127    * binding of `thisArg` and the `partials` prepended to those provided to
10128    * the wrapper.
10129    *
10130    * @private
10131    * @param {Function} func The function to partially apply arguments to.
10132    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10133    * @param {*} thisArg The `this` binding of `func`.
10134    * @param {Array} partials The arguments to prepend to those provided to the new function.
10135    * @returns {Function} Returns the new bound function.
10136    */
10137   function createPartialWrapper(func, bitmask, thisArg, partials) {
10138     var isBind = bitmask & BIND_FLAG,
10139         Ctor = createCtorWrapper(func);
10140
10141     function wrapper() {
10142       // Avoid `arguments` object use disqualifying optimizations by
10143       // converting it to an array before providing it `func`.
10144       var argsIndex = -1,
10145           argsLength = arguments.length,
10146           leftIndex = -1,
10147           leftLength = partials.length,
10148           args = Array(argsLength + leftLength);
10149
10150       while (++leftIndex < leftLength) {
10151         args[leftIndex] = partials[leftIndex];
10152       }
10153       while (argsLength--) {
10154         args[leftIndex++] = arguments[++argsIndex];
10155       }
10156       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10157       return fn.apply(isBind ? thisArg : this, args);
10158     }
10159     return wrapper;
10160   }
10161
10162   /**
10163    * Creates a function that either curries or invokes `func` with optional
10164    * `this` binding and partially applied arguments.
10165    *
10166    * @private
10167    * @param {Function|string} func The function or method name to reference.
10168    * @param {number} bitmask The bitmask of flags.
10169    *  The bitmask may be composed of the following flags:
10170    *     1 - `_.bind`
10171    *     2 - `_.bindKey`
10172    *     4 - `_.curry` or `_.curryRight` of a bound function
10173    *     8 - `_.curry`
10174    *    16 - `_.curryRight`
10175    *    32 - `_.partial`
10176    *    64 - `_.partialRight`
10177    *   128 - `_.rearg`
10178    *   256 - `_.ary`
10179    * @param {*} [thisArg] The `this` binding of `func`.
10180    * @param {Array} [partials] The arguments to be partially applied.
10181    * @param {Array} [holders] The `partials` placeholder indexes.
10182    * @param {Array} [argPos] The argument positions of the new function.
10183    * @param {number} [ary] The arity cap of `func`.
10184    * @param {number} [arity] The arity of `func`.
10185    * @returns {Function} Returns the new wrapped function.
10186    */
10187   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10188     var isBindKey = bitmask & BIND_KEY_FLAG;
10189     if (!isBindKey && typeof func != 'function') {
10190       throw new TypeError(FUNC_ERROR_TEXT);
10191     }
10192     var length = partials ? partials.length : 0;
10193     if (!length) {
10194       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10195       partials = holders = null;
10196     }
10197     length -= (holders ? holders.length : 0);
10198     if (bitmask & PARTIAL_RIGHT_FLAG) {
10199       var partialsRight = partials,
10200           holdersRight = holders;
10201
10202       partials = holders = null;
10203     }
10204     var data = isBindKey ? null : getData(func),
10205         newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
10206
10207     if (data) {
10208       mergeData(newData, data);
10209       bitmask = newData[1];
10210       arity = newData[9];
10211     }
10212     newData[9] = arity == null
10213       ? (isBindKey ? 0 : func.length)
10214       : (nativeMax(arity - length, 0) || 0);
10215
10216     if (bitmask == BIND_FLAG) {
10217       var result = createBindWrapper(newData[0], newData[2]);
10218     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
10219       result = createPartialWrapper.apply(undefined, newData);
10220     } else {
10221       result = createHybridWrapper.apply(undefined, newData);
10222     }
10223     var setter = data ? baseSetData : setData;
10224     return setter(result, newData);
10225   }
10226
10227   /**
10228    * A specialized version of `baseIsEqualDeep` for arrays with support for
10229    * partial deep comparisons.
10230    *
10231    * @private
10232    * @param {Array} array The array to compare.
10233    * @param {Array} other The other array to compare.
10234    * @param {Function} equalFunc The function to determine equivalents of values.
10235    * @param {Function} [customizer] The function to customize comparing arrays.
10236    * @param {boolean} [isLoose] Specify performing partial comparisons.
10237    * @param {Array} [stackA] Tracks traversed `value` objects.
10238    * @param {Array} [stackB] Tracks traversed `other` objects.
10239    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
10240    */
10241   function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
10242     var index = -1,
10243         arrLength = array.length,
10244         othLength = other.length;
10245
10246     if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
10247       return false;
10248     }
10249     // Ignore non-index properties.
10250     while (++index < arrLength) {
10251       var arrValue = array[index],
10252           othValue = other[index],
10253           result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
10254
10255       if (result !== undefined) {
10256         if (result) {
10257           continue;
10258         }
10259         return false;
10260       }
10261       // Recursively compare arrays (susceptible to call stack limits).
10262       if (isLoose) {
10263         if (!arraySome(other, function(othValue) {
10264               return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
10265             })) {
10266           return false;
10267         }
10268       } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
10269         return false;
10270       }
10271     }
10272     return true;
10273   }
10274
10275   /**
10276    * A specialized version of `baseIsEqualDeep` for comparing objects of
10277    * the same `toStringTag`.
10278    *
10279    * **Note:** This function only supports comparing values with tags of
10280    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10281    *
10282    * @private
10283    * @param {Object} value The object to compare.
10284    * @param {Object} other The other object to compare.
10285    * @param {string} tag The `toStringTag` of the objects to compare.
10286    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10287    */
10288   function equalByTag(object, other, tag) {
10289     switch (tag) {
10290       case boolTag:
10291       case dateTag:
10292         // Coerce dates and booleans to numbers, dates to milliseconds and booleans
10293         // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
10294         return +object == +other;
10295
10296       case errorTag:
10297         return object.name == other.name && object.message == other.message;
10298
10299       case numberTag:
10300         // Treat `NaN` vs. `NaN` as equal.
10301         return (object != +object)
10302           ? other != +other
10303           : object == +other;
10304
10305       case regexpTag:
10306       case stringTag:
10307         // Coerce regexes to strings and treat strings primitives and string
10308         // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
10309         return object == (other + '');
10310     }
10311     return false;
10312   }
10313
10314   /**
10315    * A specialized version of `baseIsEqualDeep` for objects with support for
10316    * partial deep comparisons.
10317    *
10318    * @private
10319    * @param {Object} object The object to compare.
10320    * @param {Object} other The other object to compare.
10321    * @param {Function} equalFunc The function to determine equivalents of values.
10322    * @param {Function} [customizer] The function to customize comparing values.
10323    * @param {boolean} [isLoose] Specify performing partial comparisons.
10324    * @param {Array} [stackA] Tracks traversed `value` objects.
10325    * @param {Array} [stackB] Tracks traversed `other` objects.
10326    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10327    */
10328   function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
10329     var objProps = keys(object),
10330         objLength = objProps.length,
10331         othProps = keys(other),
10332         othLength = othProps.length;
10333
10334     if (objLength != othLength && !isLoose) {
10335       return false;
10336     }
10337     var index = objLength;
10338     while (index--) {
10339       var key = objProps[index];
10340       if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
10341         return false;
10342       }
10343     }
10344     var skipCtor = isLoose;
10345     while (++index < objLength) {
10346       key = objProps[index];
10347       var objValue = object[key],
10348           othValue = other[key],
10349           result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
10350
10351       // Recursively compare objects (susceptible to call stack limits).
10352       if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
10353         return false;
10354       }
10355       skipCtor || (skipCtor = key == 'constructor');
10356     }
10357     if (!skipCtor) {
10358       var objCtor = object.constructor,
10359           othCtor = other.constructor;
10360
10361       // Non `Object` object instances with different constructors are not equal.
10362       if (objCtor != othCtor &&
10363           ('constructor' in object && 'constructor' in other) &&
10364           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
10365             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
10366         return false;
10367       }
10368     }
10369     return true;
10370   }
10371
10372   /**
10373    * Gets the appropriate "callback" function. If the `_.callback` method is
10374    * customized this function returns the custom method, otherwise it returns
10375    * the `baseCallback` function. If arguments are provided the chosen function
10376    * is invoked with them and its result is returned.
10377    *
10378    * @private
10379    * @returns {Function} Returns the chosen function or its result.
10380    */
10381   function getCallback(func, thisArg, argCount) {
10382     var result = lodash.callback || callback;
10383     result = result === callback ? baseCallback : result;
10384     return argCount ? result(func, thisArg, argCount) : result;
10385   }
10386
10387   /**
10388    * Gets metadata for `func`.
10389    *
10390    * @private
10391    * @param {Function} func The function to query.
10392    * @returns {*} Returns the metadata for `func`.
10393    */
10394   var getData = !metaMap ? noop : function(func) {
10395     return metaMap.get(func);
10396   };
10397
10398   /**
10399    * Gets the name of `func`.
10400    *
10401    * @private
10402    * @param {Function} func The function to query.
10403    * @returns {string} Returns the function name.
10404    */
10405   function getFuncName(func) {
10406     var result = func.name,
10407         array = realNames[result],
10408         length = array ? array.length : 0;
10409
10410     while (length--) {
10411       var data = array[length],
10412           otherFunc = data.func;
10413       if (otherFunc == null || otherFunc == func) {
10414         return data.name;
10415       }
10416     }
10417     return result;
10418   }
10419
10420   /**
10421    * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
10422    * customized this function returns the custom method, otherwise it returns
10423    * the `baseIndexOf` function. If arguments are provided the chosen function
10424    * is invoked with them and its result is returned.
10425    *
10426    * @private
10427    * @returns {Function|number} Returns the chosen function or its result.
10428    */
10429   function getIndexOf(collection, target, fromIndex) {
10430     var result = lodash.indexOf || indexOf;
10431     result = result === indexOf ? baseIndexOf : result;
10432     return collection ? result(collection, target, fromIndex) : result;
10433   }
10434
10435   /**
10436    * Gets the "length" property value of `object`.
10437    *
10438    * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
10439    * that affects Safari on at least iOS 8.1-8.3 ARM64.
10440    *
10441    * @private
10442    * @param {Object} object The object to query.
10443    * @returns {*} Returns the "length" value.
10444    */
10445   var getLength = baseProperty('length');
10446
10447   /**
10448    * Gets the propery names, values, and compare flags of `object`.
10449    *
10450    * @private
10451    * @param {Object} object The object to query.
10452    * @returns {Array} Returns the match data of `object`.
10453    */
10454   function getMatchData(object) {
10455     var result = pairs(object),
10456         length = result.length;
10457
10458     while (length--) {
10459       result[length][2] = isStrictComparable(result[length][1]);
10460     }
10461     return result;
10462   }
10463
10464   /**
10465    * Gets the native function at `key` of `object`.
10466    *
10467    * @private
10468    * @param {Object} object The object to query.
10469    * @param {string} key The key of the method to get.
10470    * @returns {*} Returns the function if it's native, else `undefined`.
10471    */
10472   function getNative(object, key) {
10473     var value = object == null ? undefined : object[key];
10474     return isNative(value) ? value : undefined;
10475   }
10476
10477   /**
10478    * Gets the view, applying any `transforms` to the `start` and `end` positions.
10479    *
10480    * @private
10481    * @param {number} start The start of the view.
10482    * @param {number} end The end of the view.
10483    * @param {Array} [transforms] The transformations to apply to the view.
10484    * @returns {Object} Returns an object containing the `start` and `end`
10485    *  positions of the view.
10486    */
10487   function getView(start, end, transforms) {
10488     var index = -1,
10489         length = transforms ? transforms.length : 0;
10490
10491     while (++index < length) {
10492       var data = transforms[index],
10493           size = data.size;
10494
10495       switch (data.type) {
10496         case 'drop':      start += size; break;
10497         case 'dropRight': end -= size; break;
10498         case 'take':      end = nativeMin(end, start + size); break;
10499         case 'takeRight': start = nativeMax(start, end - size); break;
10500       }
10501     }
10502     return { 'start': start, 'end': end };
10503   }
10504
10505   /**
10506    * Initializes an array clone.
10507    *
10508    * @private
10509    * @param {Array} array The array to clone.
10510    * @returns {Array} Returns the initialized clone.
10511    */
10512   function initCloneArray(array) {
10513     var length = array.length,
10514         result = new array.constructor(length);
10515
10516     // Add array properties assigned by `RegExp#exec`.
10517     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
10518       result.index = array.index;
10519       result.input = array.input;
10520     }
10521     return result;
10522   }
10523
10524   /**
10525    * Initializes an object clone.
10526    *
10527    * @private
10528    * @param {Object} object The object to clone.
10529    * @returns {Object} Returns the initialized clone.
10530    */
10531   function initCloneObject(object) {
10532     var Ctor = object.constructor;
10533     if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
10534       Ctor = Object;
10535     }
10536     return new Ctor;
10537   }
10538
10539   /**
10540    * Initializes an object clone based on its `toStringTag`.
10541    *
10542    * **Note:** This function only supports cloning values with tags of
10543    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10544    *
10545    * @private
10546    * @param {Object} object The object to clone.
10547    * @param {string} tag The `toStringTag` of the object to clone.
10548    * @param {boolean} [isDeep] Specify a deep clone.
10549    * @returns {Object} Returns the initialized clone.
10550    */
10551   function initCloneByTag(object, tag, isDeep) {
10552     var Ctor = object.constructor;
10553     switch (tag) {
10554       case arrayBufferTag:
10555         return bufferClone(object);
10556
10557       case boolTag:
10558       case dateTag:
10559         return new Ctor(+object);
10560
10561       case float32Tag: case float64Tag:
10562       case int8Tag: case int16Tag: case int32Tag:
10563       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
10564         // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
10565         if (Ctor instanceof Ctor) {
10566           Ctor = ctorByTag[tag];
10567         }
10568         var buffer = object.buffer;
10569         return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
10570
10571       case numberTag:
10572       case stringTag:
10573         return new Ctor(object);
10574
10575       case regexpTag:
10576         var result = new Ctor(object.source, reFlags.exec(object));
10577         result.lastIndex = object.lastIndex;
10578     }
10579     return result;
10580   }
10581
10582   /**
10583    * Checks if `value` is array-like.
10584    *
10585    * @private
10586    * @param {*} value The value to check.
10587    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
10588    */
10589   function isArrayLike(value) {
10590     return value != null && isLength(getLength(value));
10591   }
10592
10593   /**
10594    * Checks if `value` is a valid array-like index.
10595    *
10596    * @private
10597    * @param {*} value The value to check.
10598    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
10599    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
10600    */
10601   function isIndex(value, length) {
10602     value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
10603     length = length == null ? MAX_SAFE_INTEGER : length;
10604     return value > -1 && value % 1 == 0 && value < length;
10605   }
10606
10607   /**
10608    * Checks if the provided arguments are from an iteratee call.
10609    *
10610    * @private
10611    * @param {*} value The potential iteratee value argument.
10612    * @param {*} index The potential iteratee index or key argument.
10613    * @param {*} object The potential iteratee object argument.
10614    * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
10615    */
10616   function isIterateeCall(value, index, object) {
10617     if (!isObject(object)) {
10618       return false;
10619     }
10620     var type = typeof index;
10621     if (type == 'number'
10622         ? (isArrayLike(object) && isIndex(index, object.length))
10623         : (type == 'string' && index in object)) {
10624       var other = object[index];
10625       return value === value ? (value === other) : (other !== other);
10626     }
10627     return false;
10628   }
10629
10630   /**
10631    * Checks if `value` is a property name and not a property path.
10632    *
10633    * @private
10634    * @param {*} value The value to check.
10635    * @param {Object} [object] The object to query keys on.
10636    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
10637    */
10638   function isKey(value, object) {
10639     var type = typeof value;
10640     if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
10641       return true;
10642     }
10643     if (isArray(value)) {
10644       return false;
10645     }
10646     var result = !reIsDeepProp.test(value);
10647     return result || (object != null && value in toObject(object));
10648   }
10649
10650   /**
10651    * Checks if `func` has a lazy counterpart.
10652    *
10653    * @private
10654    * @param {Function} func The function to check.
10655    * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
10656    */
10657   function isLaziable(func) {
10658     var funcName = getFuncName(func);
10659     if (!(funcName in LazyWrapper.prototype)) {
10660       return false;
10661     }
10662     var other = lodash[funcName];
10663     if (func === other) {
10664       return true;
10665     }
10666     var data = getData(other);
10667     return !!data && func === data[0];
10668   }
10669
10670   /**
10671    * Checks if `value` is a valid array-like length.
10672    *
10673    * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
10674    *
10675    * @private
10676    * @param {*} value The value to check.
10677    * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
10678    */
10679   function isLength(value) {
10680     return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
10681   }
10682
10683   /**
10684    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
10685    *
10686    * @private
10687    * @param {*} value The value to check.
10688    * @returns {boolean} Returns `true` if `value` if suitable for strict
10689    *  equality comparisons, else `false`.
10690    */
10691   function isStrictComparable(value) {
10692     return value === value && !isObject(value);
10693   }
10694
10695   /**
10696    * Merges the function metadata of `source` into `data`.
10697    *
10698    * Merging metadata reduces the number of wrappers required to invoke a function.
10699    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
10700    * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
10701    * augment function arguments, making the order in which they are executed important,
10702    * preventing the merging of metadata. However, we make an exception for a safe
10703    * common case where curried functions have `_.ary` and or `_.rearg` applied.
10704    *
10705    * @private
10706    * @param {Array} data The destination metadata.
10707    * @param {Array} source The source metadata.
10708    * @returns {Array} Returns `data`.
10709    */
10710   function mergeData(data, source) {
10711     var bitmask = data[1],
10712         srcBitmask = source[1],
10713         newBitmask = bitmask | srcBitmask,
10714         isCommon = newBitmask < ARY_FLAG;
10715
10716     var isCombo =
10717       (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
10718       (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
10719       (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
10720
10721     // Exit early if metadata can't be merged.
10722     if (!(isCommon || isCombo)) {
10723       return data;
10724     }
10725     // Use source `thisArg` if available.
10726     if (srcBitmask & BIND_FLAG) {
10727       data[2] = source[2];
10728       // Set when currying a bound function.
10729       newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
10730     }
10731     // Compose partial arguments.
10732     var value = source[3];
10733     if (value) {
10734       var partials = data[3];
10735       data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
10736       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
10737     }
10738     // Compose partial right arguments.
10739     value = source[5];
10740     if (value) {
10741       partials = data[5];
10742       data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
10743       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
10744     }
10745     // Use source `argPos` if available.
10746     value = source[7];
10747     if (value) {
10748       data[7] = arrayCopy(value);
10749     }
10750     // Use source `ary` if it's smaller.
10751     if (srcBitmask & ARY_FLAG) {
10752       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
10753     }
10754     // Use source `arity` if one is not provided.
10755     if (data[9] == null) {
10756       data[9] = source[9];
10757     }
10758     // Use source `func` and merge bitmasks.
10759     data[0] = source[0];
10760     data[1] = newBitmask;
10761
10762     return data;
10763   }
10764
10765   /**
10766    * A specialized version of `_.pick` which picks `object` properties specified
10767    * by `props`.
10768    *
10769    * @private
10770    * @param {Object} object The source object.
10771    * @param {string[]} props The property names to pick.
10772    * @returns {Object} Returns the new object.
10773    */
10774   function pickByArray(object, props) {
10775     object = toObject(object);
10776
10777     var index = -1,
10778         length = props.length,
10779         result = {};
10780
10781     while (++index < length) {
10782       var key = props[index];
10783       if (key in object) {
10784         result[key] = object[key];
10785       }
10786     }
10787     return result;
10788   }
10789
10790   /**
10791    * A specialized version of `_.pick` which picks `object` properties `predicate`
10792    * returns truthy for.
10793    *
10794    * @private
10795    * @param {Object} object The source object.
10796    * @param {Function} predicate The function invoked per iteration.
10797    * @returns {Object} Returns the new object.
10798    */
10799   function pickByCallback(object, predicate) {
10800     var result = {};
10801     baseForIn(object, function(value, key, object) {
10802       if (predicate(value, key, object)) {
10803         result[key] = value;
10804       }
10805     });
10806     return result;
10807   }
10808
10809   /**
10810    * Reorder `array` according to the specified indexes where the element at
10811    * the first index is assigned as the first element, the element at
10812    * the second index is assigned as the second element, and so on.
10813    *
10814    * @private
10815    * @param {Array} array The array to reorder.
10816    * @param {Array} indexes The arranged array indexes.
10817    * @returns {Array} Returns `array`.
10818    */
10819   function reorder(array, indexes) {
10820     var arrLength = array.length,
10821         length = nativeMin(indexes.length, arrLength),
10822         oldArray = arrayCopy(array);
10823
10824     while (length--) {
10825       var index = indexes[length];
10826       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
10827     }
10828     return array;
10829   }
10830
10831   /**
10832    * Sets metadata for `func`.
10833    *
10834    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
10835    * period of time, it will trip its breaker and transition to an identity function
10836    * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
10837    * for more details.
10838    *
10839    * @private
10840    * @param {Function} func The function to associate metadata with.
10841    * @param {*} data The metadata.
10842    * @returns {Function} Returns `func`.
10843    */
10844   var setData = (function() {
10845     var count = 0,
10846         lastCalled = 0;
10847
10848     return function(key, value) {
10849       var stamp = now(),
10850           remaining = HOT_SPAN - (stamp - lastCalled);
10851
10852       lastCalled = stamp;
10853       if (remaining > 0) {
10854         if (++count >= HOT_COUNT) {
10855           return key;
10856         }
10857       } else {
10858         count = 0;
10859       }
10860       return baseSetData(key, value);
10861     };
10862   }());
10863
10864   /**
10865    * A fallback implementation of `_.isPlainObject` which checks if `value`
10866    * is an object created by the `Object` constructor or has a `[[Prototype]]`
10867    * of `null`.
10868    *
10869    * @private
10870    * @param {*} value The value to check.
10871    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
10872    */
10873   function shimIsPlainObject(value) {
10874     var Ctor,
10875         support = lodash.support;
10876
10877     // Exit early for non `Object` objects.
10878     if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
10879         (!hasOwnProperty.call(value, 'constructor') &&
10880           (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
10881         (!support.argsTag && isArguments(value))) {
10882       return false;
10883     }
10884     // IE < 9 iterates inherited properties before own properties. If the first
10885     // iterated property is an object's own property then there are no inherited
10886     // enumerable properties.
10887     var result;
10888     if (support.ownLast) {
10889       baseForIn(value, function(subValue, key, object) {
10890         result = hasOwnProperty.call(object, key);
10891         return false;
10892       });
10893       return result !== false;
10894     }
10895     // In most environments an object's own properties are iterated before
10896     // its inherited properties. If the last iterated property is an object's
10897     // own property then there are no inherited enumerable properties.
10898     baseForIn(value, function(subValue, key) {
10899       result = key;
10900     });
10901     return result === undefined || hasOwnProperty.call(value, result);
10902   }
10903
10904   /**
10905    * A fallback implementation of `Object.keys` which creates an array of the
10906    * own enumerable property names of `object`.
10907    *
10908    * @private
10909    * @param {Object} object The object to query.
10910    * @returns {Array} Returns the array of property names.
10911    */
10912   function shimKeys(object) {
10913     var props = keysIn(object),
10914         propsLength = props.length,
10915         length = propsLength && object.length;
10916
10917     var allowIndexes = !!length && isLength(length) &&
10918       (isArray(object) || isArguments(object) || isString(object));
10919
10920     var index = -1,
10921         result = [];
10922
10923     while (++index < propsLength) {
10924       var key = props[index];
10925       if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
10926         result.push(key);
10927       }
10928     }
10929     return result;
10930   }
10931
10932   /**
10933    * Converts `value` to an object if it's not one.
10934    *
10935    * @private
10936    * @param {*} value The value to process.
10937    * @returns {Object} Returns the object.
10938    */
10939   function toObject(value) {
10940     if (lodash.support.unindexedChars && isString(value)) {
10941       var index = -1,
10942           length = value.length,
10943           result = Object(value);
10944
10945       while (++index < length) {
10946         result[index] = value.charAt(index);
10947       }
10948       return result;
10949     }
10950     return isObject(value) ? value : Object(value);
10951   }
10952
10953   /**
10954    * Converts `value` to property path array if it's not one.
10955    *
10956    * @private
10957    * @param {*} value The value to process.
10958    * @returns {Array} Returns the property path array.
10959    */
10960   function toPath(value) {
10961     if (isArray(value)) {
10962       return value;
10963     }
10964     var result = [];
10965     baseToString(value).replace(rePropName, function(match, number, quote, string) {
10966       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
10967     });
10968     return result;
10969   }
10970
10971   /**
10972    * Creates a clone of `wrapper`.
10973    *
10974    * @private
10975    * @param {Object} wrapper The wrapper to clone.
10976    * @returns {Object} Returns the cloned wrapper.
10977    */
10978   function wrapperClone(wrapper) {
10979     return wrapper instanceof LazyWrapper
10980       ? wrapper.clone()
10981       : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
10982   }
10983
10984   /*------------------------------------------------------------------------*/
10985
10986   /**
10987    * Creates an array of elements split into groups the length of `size`.
10988    * If `collection` can't be split evenly, the final chunk will be the remaining
10989    * elements.
10990    *
10991    * @static
10992    * @memberOf _
10993    * @category Array
10994    * @param {Array} array The array to process.
10995    * @param {number} [size=1] The length of each chunk.
10996    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
10997    * @returns {Array} Returns the new array containing chunks.
10998    * @example
10999    *
11000    * _.chunk(['a', 'b', 'c', 'd'], 2);
11001    * // => [['a', 'b'], ['c', 'd']]
11002    *
11003    * _.chunk(['a', 'b', 'c', 'd'], 3);
11004    * // => [['a', 'b', 'c'], ['d']]
11005    */
11006   function chunk(array, size, guard) {
11007     if (guard ? isIterateeCall(array, size, guard) : size == null) {
11008       size = 1;
11009     } else {
11010       size = nativeMax(+size || 1, 1);
11011     }
11012     var index = 0,
11013         length = array ? array.length : 0,
11014         resIndex = -1,
11015         result = Array(ceil(length / size));
11016
11017     while (index < length) {
11018       result[++resIndex] = baseSlice(array, index, (index += size));
11019     }
11020     return result;
11021   }
11022
11023   /**
11024    * Creates an array with all falsey values removed. The values `false`, `null`,
11025    * `0`, `""`, `undefined`, and `NaN` are falsey.
11026    *
11027    * @static
11028    * @memberOf _
11029    * @category Array
11030    * @param {Array} array The array to compact.
11031    * @returns {Array} Returns the new array of filtered values.
11032    * @example
11033    *
11034    * _.compact([0, 1, false, 2, '', 3]);
11035    * // => [1, 2, 3]
11036    */
11037   function compact(array) {
11038     var index = -1,
11039         length = array ? array.length : 0,
11040         resIndex = -1,
11041         result = [];
11042
11043     while (++index < length) {
11044       var value = array[index];
11045       if (value) {
11046         result[++resIndex] = value;
11047       }
11048     }
11049     return result;
11050   }
11051
11052   /**
11053    * Creates an array of unique `array` values not included in the other
11054    * provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11055    * for equality comparisons.
11056    *
11057    * @static
11058    * @memberOf _
11059    * @category Array
11060    * @param {Array} array The array to inspect.
11061    * @param {...Array} [values] The arrays of values to exclude.
11062    * @returns {Array} Returns the new array of filtered values.
11063    * @example
11064    *
11065    * _.difference([1, 2, 3], [4, 2]);
11066    * // => [1, 3]
11067    */
11068   var difference = restParam(function(array, values) {
11069     return isArrayLike(array)
11070       ? baseDifference(array, baseFlatten(values, false, true))
11071       : [];
11072   });
11073
11074   /**
11075    * Gets the first element of `array`.
11076    *
11077    * @static
11078    * @memberOf _
11079    * @alias head
11080    * @category Array
11081    * @param {Array} array The array to query.
11082    * @returns {*} Returns the first element of `array`.
11083    * @example
11084    *
11085    * _.first([1, 2, 3]);
11086    * // => 1
11087    *
11088    * _.first([]);
11089    * // => undefined
11090    */
11091   function first(array) {
11092     return array ? array[0] : undefined;
11093   }
11094
11095   /**
11096    * Flattens a nested array. If `isDeep` is `true` the array is recursively
11097    * flattened, otherwise it is only flattened a single level.
11098    *
11099    * @static
11100    * @memberOf _
11101    * @category Array
11102    * @param {Array} array The array to flatten.
11103    * @param {boolean} [isDeep] Specify a deep flatten.
11104    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11105    * @returns {Array} Returns the new flattened array.
11106    * @example
11107    *
11108    * _.flatten([1, [2, 3, [4]]]);
11109    * // => [1, 2, 3, [4]]
11110    *
11111    * // using `isDeep`
11112    * _.flatten([1, [2, 3, [4]]], true);
11113    * // => [1, 2, 3, 4]
11114    */
11115   function flatten(array, isDeep, guard) {
11116     var length = array ? array.length : 0;
11117     if (guard && isIterateeCall(array, isDeep, guard)) {
11118       isDeep = false;
11119     }
11120     return length ? baseFlatten(array, isDeep) : [];
11121   }
11122
11123   /**
11124    * Gets the index at which the first occurrence of `value` is found in `array`
11125    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11126    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11127    * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
11128    * performs a faster binary search.
11129    *
11130    * @static
11131    * @memberOf _
11132    * @category Array
11133    * @param {Array} array The array to search.
11134    * @param {*} value The value to search for.
11135    * @param {boolean|number} [fromIndex=0] The index to search from or `true`
11136    *  to perform a binary search on a sorted array.
11137    * @returns {number} Returns the index of the matched value, else `-1`.
11138    * @example
11139    *
11140    * _.indexOf([1, 2, 1, 2], 2);
11141    * // => 1
11142    *
11143    * // using `fromIndex`
11144    * _.indexOf([1, 2, 1, 2], 2, 2);
11145    * // => 3
11146    *
11147    * // performing a binary search
11148    * _.indexOf([1, 1, 2, 2], 2, true);
11149    * // => 2
11150    */
11151   function indexOf(array, value, fromIndex) {
11152     var length = array ? array.length : 0;
11153     if (!length) {
11154       return -1;
11155     }
11156     if (typeof fromIndex == 'number') {
11157       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
11158     } else if (fromIndex) {
11159       var index = binaryIndex(array, value),
11160           other = array[index];
11161
11162       if (value === value ? (value === other) : (other !== other)) {
11163         return index;
11164       }
11165       return -1;
11166     }
11167     return baseIndexOf(array, value, fromIndex || 0);
11168   }
11169
11170   /**
11171    * Creates an array of unique values that are included in all of the provided
11172    * arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11173    * for equality comparisons.
11174    *
11175    * @static
11176    * @memberOf _
11177    * @category Array
11178    * @param {...Array} [arrays] The arrays to inspect.
11179    * @returns {Array} Returns the new array of shared values.
11180    * @example
11181    * _.intersection([1, 2], [4, 2], [2, 1]);
11182    * // => [2]
11183    */
11184   var intersection = restParam(function(arrays) {
11185     var othLength = arrays.length,
11186         othIndex = othLength,
11187         caches = Array(length),
11188         indexOf = getIndexOf(),
11189         isCommon = indexOf == baseIndexOf,
11190         result = [];
11191
11192     while (othIndex--) {
11193       var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
11194       caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
11195     }
11196     var array = arrays[0],
11197         index = -1,
11198         length = array ? array.length : 0,
11199         seen = caches[0];
11200
11201     outer:
11202     while (++index < length) {
11203       value = array[index];
11204       if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
11205         var othIndex = othLength;
11206         while (--othIndex) {
11207           var cache = caches[othIndex];
11208           if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
11209             continue outer;
11210           }
11211         }
11212         if (seen) {
11213           seen.push(value);
11214         }
11215         result.push(value);
11216       }
11217     }
11218     return result;
11219   });
11220
11221   /**
11222    * Gets the last element of `array`.
11223    *
11224    * @static
11225    * @memberOf _
11226    * @category Array
11227    * @param {Array} array The array to query.
11228    * @returns {*} Returns the last element of `array`.
11229    * @example
11230    *
11231    * _.last([1, 2, 3]);
11232    * // => 3
11233    */
11234   function last(array) {
11235     var length = array ? array.length : 0;
11236     return length ? array[length - 1] : undefined;
11237   }
11238
11239   /**
11240    * Creates an array of unique values, in order, from all of the provided arrays
11241    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11242    * for equality comparisons.
11243    *
11244    * @static
11245    * @memberOf _
11246    * @category Array
11247    * @param {...Array} [arrays] The arrays to inspect.
11248    * @returns {Array} Returns the new array of combined values.
11249    * @example
11250    *
11251    * _.union([1, 2], [4, 2], [2, 1]);
11252    * // => [1, 2, 4]
11253    */
11254   var union = restParam(function(arrays) {
11255     return baseUniq(baseFlatten(arrays, false, true));
11256   });
11257
11258   /**
11259    * Creates a duplicate-free version of an array, using
11260    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11261    * for equality comparisons, in which only the first occurence of each element
11262    * is kept. Providing `true` for `isSorted` performs a faster search algorithm
11263    * for sorted arrays. If an iteratee function is provided it is invoked for
11264    * each element in the array to generate the criterion by which uniqueness
11265    * is computed. The `iteratee` is bound to `thisArg` and invoked with three
11266    * arguments: (value, index, array).
11267    *
11268    * If a property name is provided for `iteratee` the created `_.property`
11269    * style callback returns the property value of the given element.
11270    *
11271    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11272    * style callback returns `true` for elements that have a matching property
11273    * value, else `false`.
11274    *
11275    * If an object is provided for `iteratee` the created `_.matches` style
11276    * callback returns `true` for elements that have the properties of the given
11277    * object, else `false`.
11278    *
11279    * @static
11280    * @memberOf _
11281    * @alias unique
11282    * @category Array
11283    * @param {Array} array The array to inspect.
11284    * @param {boolean} [isSorted] Specify the array is sorted.
11285    * @param {Function|Object|string} [iteratee] The function invoked per iteration.
11286    * @param {*} [thisArg] The `this` binding of `iteratee`.
11287    * @returns {Array} Returns the new duplicate-value-free array.
11288    * @example
11289    *
11290    * _.uniq([2, 1, 2]);
11291    * // => [2, 1]
11292    *
11293    * // using `isSorted`
11294    * _.uniq([1, 1, 2], true);
11295    * // => [1, 2]
11296    *
11297    * // using an iteratee function
11298    * _.uniq([1, 2.5, 1.5, 2], function(n) {
11299    *   return this.floor(n);
11300    * }, Math);
11301    * // => [1, 2.5]
11302    *
11303    * // using the `_.property` callback shorthand
11304    * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
11305    * // => [{ 'x': 1 }, { 'x': 2 }]
11306    */
11307   function uniq(array, isSorted, iteratee, thisArg) {
11308     var length = array ? array.length : 0;
11309     if (!length) {
11310       return [];
11311     }
11312     if (isSorted != null && typeof isSorted != 'boolean') {
11313       thisArg = iteratee;
11314       iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
11315       isSorted = false;
11316     }
11317     var callback = getCallback();
11318     if (!(iteratee == null && callback === baseCallback)) {
11319       iteratee = callback(iteratee, thisArg, 3);
11320     }
11321     return (isSorted && getIndexOf() == baseIndexOf)
11322       ? sortedUniq(array, iteratee)
11323       : baseUniq(array, iteratee);
11324   }
11325
11326   /**
11327    * Creates an array excluding all provided values using
11328    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11329    * for equality comparisons.
11330    *
11331    * @static
11332    * @memberOf _
11333    * @category Array
11334    * @param {Array} array The array to filter.
11335    * @param {...*} [values] The values to exclude.
11336    * @returns {Array} Returns the new array of filtered values.
11337    * @example
11338    *
11339    * _.without([1, 2, 1, 3], 1, 2);
11340    * // => [3]
11341    */
11342   var without = restParam(function(array, values) {
11343     return isArrayLike(array)
11344       ? baseDifference(array, values)
11345       : [];
11346   });
11347
11348   /*------------------------------------------------------------------------*/
11349
11350   /**
11351    * Creates a `lodash` object that wraps `value` with explicit method
11352    * chaining enabled.
11353    *
11354    * @static
11355    * @memberOf _
11356    * @category Chain
11357    * @param {*} value The value to wrap.
11358    * @returns {Object} Returns the new `lodash` wrapper instance.
11359    * @example
11360    *
11361    * var users = [
11362    *   { 'user': 'barney',  'age': 36 },
11363    *   { 'user': 'fred',    'age': 40 },
11364    *   { 'user': 'pebbles', 'age': 1 }
11365    * ];
11366    *
11367    * var youngest = _.chain(users)
11368    *   .sortBy('age')
11369    *   .map(function(chr) {
11370    *     return chr.user + ' is ' + chr.age;
11371    *   })
11372    *   .first()
11373    *   .value();
11374    * // => 'pebbles is 1'
11375    */
11376   function chain(value) {
11377     var result = lodash(value);
11378     result.__chain__ = true;
11379     return result;
11380   }
11381
11382   /**
11383    * This method invokes `interceptor` and returns `value`. The interceptor is
11384    * bound to `thisArg` and invoked with one argument; (value). The purpose of
11385    * this method is to "tap into" a method chain in order to perform operations
11386    * on intermediate results within the chain.
11387    *
11388    * @static
11389    * @memberOf _
11390    * @category Chain
11391    * @param {*} value The value to provide to `interceptor`.
11392    * @param {Function} interceptor The function to invoke.
11393    * @param {*} [thisArg] The `this` binding of `interceptor`.
11394    * @returns {*} Returns `value`.
11395    * @example
11396    *
11397    * _([1, 2, 3])
11398    *  .tap(function(array) {
11399    *    array.pop();
11400    *  })
11401    *  .reverse()
11402    *  .value();
11403    * // => [2, 1]
11404    */
11405   function tap(value, interceptor, thisArg) {
11406     interceptor.call(thisArg, value);
11407     return value;
11408   }
11409
11410   /**
11411    * This method is like `_.tap` except that it returns the result of `interceptor`.
11412    *
11413    * @static
11414    * @memberOf _
11415    * @category Chain
11416    * @param {*} value The value to provide to `interceptor`.
11417    * @param {Function} interceptor The function to invoke.
11418    * @param {*} [thisArg] The `this` binding of `interceptor`.
11419    * @returns {*} Returns the result of `interceptor`.
11420    * @example
11421    *
11422    * _('  abc  ')
11423    *  .chain()
11424    *  .trim()
11425    *  .thru(function(value) {
11426    *    return [value];
11427    *  })
11428    *  .value();
11429    * // => ['abc']
11430    */
11431   function thru(value, interceptor, thisArg) {
11432     return interceptor.call(thisArg, value);
11433   }
11434
11435   /**
11436    * Enables explicit method chaining on the wrapper object.
11437    *
11438    * @name chain
11439    * @memberOf _
11440    * @category Chain
11441    * @returns {Object} Returns the new `lodash` wrapper instance.
11442    * @example
11443    *
11444    * var users = [
11445    *   { 'user': 'barney', 'age': 36 },
11446    *   { 'user': 'fred',   'age': 40 }
11447    * ];
11448    *
11449    * // without explicit chaining
11450    * _(users).first();
11451    * // => { 'user': 'barney', 'age': 36 }
11452    *
11453    * // with explicit chaining
11454    * _(users).chain()
11455    *   .first()
11456    *   .pick('user')
11457    *   .value();
11458    * // => { 'user': 'barney' }
11459    */
11460   function wrapperChain() {
11461     return chain(this);
11462   }
11463
11464   /**
11465    * Executes the chained sequence and returns the wrapped result.
11466    *
11467    * @name commit
11468    * @memberOf _
11469    * @category Chain
11470    * @returns {Object} Returns the new `lodash` wrapper instance.
11471    * @example
11472    *
11473    * var array = [1, 2];
11474    * var wrapper = _(array).push(3);
11475    *
11476    * console.log(array);
11477    * // => [1, 2]
11478    *
11479    * wrapper = wrapper.commit();
11480    * console.log(array);
11481    * // => [1, 2, 3]
11482    *
11483    * wrapper.last();
11484    * // => 3
11485    *
11486    * console.log(array);
11487    * // => [1, 2, 3]
11488    */
11489   function wrapperCommit() {
11490     return new LodashWrapper(this.value(), this.__chain__);
11491   }
11492
11493   /**
11494    * Creates a clone of the chained sequence planting `value` as the wrapped value.
11495    *
11496    * @name plant
11497    * @memberOf _
11498    * @category Chain
11499    * @returns {Object} Returns the new `lodash` wrapper instance.
11500    * @example
11501    *
11502    * var array = [1, 2];
11503    * var wrapper = _(array).map(function(value) {
11504    *   return Math.pow(value, 2);
11505    * });
11506    *
11507    * var other = [3, 4];
11508    * var otherWrapper = wrapper.plant(other);
11509    *
11510    * otherWrapper.value();
11511    * // => [9, 16]
11512    *
11513    * wrapper.value();
11514    * // => [1, 4]
11515    */
11516   function wrapperPlant(value) {
11517     var result,
11518         parent = this;
11519
11520     while (parent instanceof baseLodash) {
11521       var clone = wrapperClone(parent);
11522       if (result) {
11523         previous.__wrapped__ = clone;
11524       } else {
11525         result = clone;
11526       }
11527       var previous = clone;
11528       parent = parent.__wrapped__;
11529     }
11530     previous.__wrapped__ = value;
11531     return result;
11532   }
11533
11534   /**
11535    * Reverses the wrapped array so the first element becomes the last, the
11536    * second element becomes the second to last, and so on.
11537    *
11538    * **Note:** This method mutates the wrapped array.
11539    *
11540    * @name reverse
11541    * @memberOf _
11542    * @category Chain
11543    * @returns {Object} Returns the new reversed `lodash` wrapper instance.
11544    * @example
11545    *
11546    * var array = [1, 2, 3];
11547    *
11548    * _(array).reverse().value()
11549    * // => [3, 2, 1]
11550    *
11551    * console.log(array);
11552    * // => [3, 2, 1]
11553    */
11554   function wrapperReverse() {
11555     var value = this.__wrapped__;
11556     if (value instanceof LazyWrapper) {
11557       if (this.__actions__.length) {
11558         value = new LazyWrapper(this);
11559       }
11560       return new LodashWrapper(value.reverse(), this.__chain__);
11561     }
11562     return this.thru(function(value) {
11563       return value.reverse();
11564     });
11565   }
11566
11567   /**
11568    * Produces the result of coercing the unwrapped value to a string.
11569    *
11570    * @name toString
11571    * @memberOf _
11572    * @category Chain
11573    * @returns {string} Returns the coerced string value.
11574    * @example
11575    *
11576    * _([1, 2, 3]).toString();
11577    * // => '1,2,3'
11578    */
11579   function wrapperToString() {
11580     return (this.value() + '');
11581   }
11582
11583   /**
11584    * Executes the chained sequence to extract the unwrapped value.
11585    *
11586    * @name value
11587    * @memberOf _
11588    * @alias run, toJSON, valueOf
11589    * @category Chain
11590    * @returns {*} Returns the resolved unwrapped value.
11591    * @example
11592    *
11593    * _([1, 2, 3]).value();
11594    * // => [1, 2, 3]
11595    */
11596   function wrapperValue() {
11597     return baseWrapperValue(this.__wrapped__, this.__actions__);
11598   }
11599
11600   /*------------------------------------------------------------------------*/
11601
11602   /**
11603    * Checks if `predicate` returns truthy for **all** elements of `collection`.
11604    * The predicate is bound to `thisArg` and invoked with three arguments:
11605    * (value, index|key, collection).
11606    *
11607    * If a property name is provided for `predicate` the created `_.property`
11608    * style callback returns the property value of the given element.
11609    *
11610    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11611    * style callback returns `true` for elements that have a matching property
11612    * value, else `false`.
11613    *
11614    * If an object is provided for `predicate` the created `_.matches` style
11615    * callback returns `true` for elements that have the properties of the given
11616    * object, else `false`.
11617    *
11618    * @static
11619    * @memberOf _
11620    * @alias all
11621    * @category Collection
11622    * @param {Array|Object|string} collection The collection to iterate over.
11623    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11624    *  per iteration.
11625    * @param {*} [thisArg] The `this` binding of `predicate`.
11626    * @returns {boolean} Returns `true` if all elements pass the predicate check,
11627    *  else `false`.
11628    * @example
11629    *
11630    * _.every([true, 1, null, 'yes'], Boolean);
11631    * // => false
11632    *
11633    * var users = [
11634    *   { 'user': 'barney', 'active': false },
11635    *   { 'user': 'fred',   'active': false }
11636    * ];
11637    *
11638    * // using the `_.matches` callback shorthand
11639    * _.every(users, { 'user': 'barney', 'active': false });
11640    * // => false
11641    *
11642    * // using the `_.matchesProperty` callback shorthand
11643    * _.every(users, 'active', false);
11644    * // => true
11645    *
11646    * // using the `_.property` callback shorthand
11647    * _.every(users, 'active');
11648    * // => false
11649    */
11650   function every(collection, predicate, thisArg) {
11651     var func = isArray(collection) ? arrayEvery : baseEvery;
11652     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
11653       predicate = null;
11654     }
11655     if (typeof predicate != 'function' || thisArg !== undefined) {
11656       predicate = getCallback(predicate, thisArg, 3);
11657     }
11658     return func(collection, predicate);
11659   }
11660
11661   /**
11662    * Iterates over elements of `collection`, returning an array of all elements
11663    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11664    * invoked with three arguments: (value, index|key, collection).
11665    *
11666    * If a property name is provided for `predicate` the created `_.property`
11667    * style callback returns the property value of the given element.
11668    *
11669    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11670    * style callback returns `true` for elements that have a matching property
11671    * value, else `false`.
11672    *
11673    * If an object is provided for `predicate` the created `_.matches` style
11674    * callback returns `true` for elements that have the properties of the given
11675    * object, else `false`.
11676    *
11677    * @static
11678    * @memberOf _
11679    * @alias select
11680    * @category Collection
11681    * @param {Array|Object|string} collection The collection to iterate over.
11682    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11683    *  per iteration.
11684    * @param {*} [thisArg] The `this` binding of `predicate`.
11685    * @returns {Array} Returns the new filtered array.
11686    * @example
11687    *
11688    * _.filter([4, 5, 6], function(n) {
11689    *   return n % 2 == 0;
11690    * });
11691    * // => [4, 6]
11692    *
11693    * var users = [
11694    *   { 'user': 'barney', 'age': 36, 'active': true },
11695    *   { 'user': 'fred',   'age': 40, 'active': false }
11696    * ];
11697    *
11698    * // using the `_.matches` callback shorthand
11699    * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
11700    * // => ['barney']
11701    *
11702    * // using the `_.matchesProperty` callback shorthand
11703    * _.pluck(_.filter(users, 'active', false), 'user');
11704    * // => ['fred']
11705    *
11706    * // using the `_.property` callback shorthand
11707    * _.pluck(_.filter(users, 'active'), 'user');
11708    * // => ['barney']
11709    */
11710   function filter(collection, predicate, thisArg) {
11711     var func = isArray(collection) ? arrayFilter : baseFilter;
11712     predicate = getCallback(predicate, thisArg, 3);
11713     return func(collection, predicate);
11714   }
11715
11716   /**
11717    * Iterates over elements of `collection`, returning the first element
11718    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11719    * invoked with three arguments: (value, index|key, collection).
11720    *
11721    * If a property name is provided for `predicate` the created `_.property`
11722    * style callback returns the property value of the given element.
11723    *
11724    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11725    * style callback returns `true` for elements that have a matching property
11726    * value, else `false`.
11727    *
11728    * If an object is provided for `predicate` the created `_.matches` style
11729    * callback returns `true` for elements that have the properties of the given
11730    * object, else `false`.
11731    *
11732    * @static
11733    * @memberOf _
11734    * @alias detect
11735    * @category Collection
11736    * @param {Array|Object|string} collection The collection to search.
11737    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11738    *  per iteration.
11739    * @param {*} [thisArg] The `this` binding of `predicate`.
11740    * @returns {*} Returns the matched element, else `undefined`.
11741    * @example
11742    *
11743    * var users = [
11744    *   { 'user': 'barney',  'age': 36, 'active': true },
11745    *   { 'user': 'fred',    'age': 40, 'active': false },
11746    *   { 'user': 'pebbles', 'age': 1,  'active': true }
11747    * ];
11748    *
11749    * _.result(_.find(users, function(chr) {
11750    *   return chr.age < 40;
11751    * }), 'user');
11752    * // => 'barney'
11753    *
11754    * // using the `_.matches` callback shorthand
11755    * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
11756    * // => 'pebbles'
11757    *
11758    * // using the `_.matchesProperty` callback shorthand
11759    * _.result(_.find(users, 'active', false), 'user');
11760    * // => 'fred'
11761    *
11762    * // using the `_.property` callback shorthand
11763    * _.result(_.find(users, 'active'), 'user');
11764    * // => 'barney'
11765    */
11766   var find = createFind(baseEach);
11767
11768   /**
11769    * Iterates over elements of `collection` invoking `iteratee` for each element.
11770    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11771    * (value, index|key, collection). Iteratee functions may exit iteration early
11772    * by explicitly returning `false`.
11773    *
11774    * **Note:** As with other "Collections" methods, objects with a "length" property
11775    * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
11776    * may be used for object iteration.
11777    *
11778    * @static
11779    * @memberOf _
11780    * @alias each
11781    * @category Collection
11782    * @param {Array|Object|string} collection The collection to iterate over.
11783    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
11784    * @param {*} [thisArg] The `this` binding of `iteratee`.
11785    * @returns {Array|Object|string} Returns `collection`.
11786    * @example
11787    *
11788    * _([1, 2]).forEach(function(n) {
11789    *   console.log(n);
11790    * }).value();
11791    * // => logs each value from left to right and returns the array
11792    *
11793    * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
11794    *   console.log(n, key);
11795    * });
11796    * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
11797    */
11798   var forEach = createForEach(arrayEach, baseEach);
11799
11800   /**
11801    * Creates an object composed of keys generated from the results of running
11802    * each element of `collection` through `iteratee`. The corresponding value
11803    * of each key is an array of the elements responsible for generating the key.
11804    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11805    * (value, index|key, collection).
11806    *
11807    * If a property name is provided for `iteratee` the created `_.property`
11808    * style callback returns the property value of the given element.
11809    *
11810    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11811    * style callback returns `true` for elements that have a matching property
11812    * value, else `false`.
11813    *
11814    * If an object is provided for `iteratee` the created `_.matches` style
11815    * callback returns `true` for elements that have the properties of the given
11816    * object, else `false`.
11817    *
11818    * @static
11819    * @memberOf _
11820    * @category Collection
11821    * @param {Array|Object|string} collection The collection to iterate over.
11822    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11823    *  per iteration.
11824    * @param {*} [thisArg] The `this` binding of `iteratee`.
11825    * @returns {Object} Returns the composed aggregate object.
11826    * @example
11827    *
11828    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11829    *   return Math.floor(n);
11830    * });
11831    * // => { '4': [4.2], '6': [6.1, 6.4] }
11832    *
11833    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11834    *   return this.floor(n);
11835    * }, Math);
11836    * // => { '4': [4.2], '6': [6.1, 6.4] }
11837    *
11838    * // using the `_.property` callback shorthand
11839    * _.groupBy(['one', 'two', 'three'], 'length');
11840    * // => { '3': ['one', 'two'], '5': ['three'] }
11841    */
11842   var groupBy = createAggregator(function(result, value, key) {
11843     if (hasOwnProperty.call(result, key)) {
11844       result[key].push(value);
11845     } else {
11846       result[key] = [value];
11847     }
11848   });
11849
11850   /**
11851    * Checks if `value` is in `collection` using
11852    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11853    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11854    * from the end of `collection`.
11855    *
11856    * @static
11857    * @memberOf _
11858    * @alias contains, include
11859    * @category Collection
11860    * @param {Array|Object|string} collection The collection to search.
11861    * @param {*} target The value to search for.
11862    * @param {number} [fromIndex=0] The index to search from.
11863    * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
11864    * @returns {boolean} Returns `true` if a matching element is found, else `false`.
11865    * @example
11866    *
11867    * _.includes([1, 2, 3], 1);
11868    * // => true
11869    *
11870    * _.includes([1, 2, 3], 1, 2);
11871    * // => false
11872    *
11873    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
11874    * // => true
11875    *
11876    * _.includes('pebbles', 'eb');
11877    * // => true
11878    */
11879   function includes(collection, target, fromIndex, guard) {
11880     var length = collection ? getLength(collection) : 0;
11881     if (!isLength(length)) {
11882       collection = values(collection);
11883       length = collection.length;
11884     }
11885     if (!length) {
11886       return false;
11887     }
11888     if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
11889       fromIndex = 0;
11890     } else {
11891       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
11892     }
11893     return (typeof collection == 'string' || !isArray(collection) && isString(collection))
11894       ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
11895       : (getIndexOf(collection, target, fromIndex) > -1);
11896   }
11897
11898   /**
11899    * Creates an array of values by running each element in `collection` through
11900    * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
11901    * arguments: (value, index|key, collection).
11902    *
11903    * If a property name is provided for `iteratee` the created `_.property`
11904    * style callback returns the property value of the given element.
11905    *
11906    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11907    * style callback returns `true` for elements that have a matching property
11908    * value, else `false`.
11909    *
11910    * If an object is provided for `iteratee` the created `_.matches` style
11911    * callback returns `true` for elements that have the properties of the given
11912    * object, else `false`.
11913    *
11914    * Many lodash methods are guarded to work as iteratees for methods like
11915    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
11916    *
11917    * The guarded methods are:
11918    * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
11919    * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
11920    * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
11921    * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
11922    * `sum`, `uniq`, and `words`
11923    *
11924    * @static
11925    * @memberOf _
11926    * @alias collect
11927    * @category Collection
11928    * @param {Array|Object|string} collection The collection to iterate over.
11929    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11930    *  per iteration.
11931    * @param {*} [thisArg] The `this` binding of `iteratee`.
11932    * @returns {Array} Returns the new mapped array.
11933    * @example
11934    *
11935    * function timesThree(n) {
11936    *   return n * 3;
11937    * }
11938    *
11939    * _.map([1, 2], timesThree);
11940    * // => [3, 6]
11941    *
11942    * _.map({ 'a': 1, 'b': 2 }, timesThree);
11943    * // => [3, 6] (iteration order is not guaranteed)
11944    *
11945    * var users = [
11946    *   { 'user': 'barney' },
11947    *   { 'user': 'fred' }
11948    * ];
11949    *
11950    * // using the `_.property` callback shorthand
11951    * _.map(users, 'user');
11952    * // => ['barney', 'fred']
11953    */
11954   function map(collection, iteratee, thisArg) {
11955     var func = isArray(collection) ? arrayMap : baseMap;
11956     iteratee = getCallback(iteratee, thisArg, 3);
11957     return func(collection, iteratee);
11958   }
11959
11960   /**
11961    * Gets the property value of `path` from all elements in `collection`.
11962    *
11963    * @static
11964    * @memberOf _
11965    * @category Collection
11966    * @param {Array|Object|string} collection The collection to iterate over.
11967    * @param {Array|string} path The path of the property to pluck.
11968    * @returns {Array} Returns the property values.
11969    * @example
11970    *
11971    * var users = [
11972    *   { 'user': 'barney', 'age': 36 },
11973    *   { 'user': 'fred',   'age': 40 }
11974    * ];
11975    *
11976    * _.pluck(users, 'user');
11977    * // => ['barney', 'fred']
11978    *
11979    * var userIndex = _.indexBy(users, 'user');
11980    * _.pluck(userIndex, 'age');
11981    * // => [36, 40] (iteration order is not guaranteed)
11982    */
11983   function pluck(collection, path) {
11984     return map(collection, property(path));
11985   }
11986
11987   /**
11988    * Reduces `collection` to a value which is the accumulated result of running
11989    * each element in `collection` through `iteratee`, where each successive
11990    * invocation is supplied the return value of the previous. If `accumulator`
11991    * is not provided the first element of `collection` is used as the initial
11992    * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
11993    * (accumulator, value, index|key, collection).
11994    *
11995    * Many lodash methods are guarded to work as iteratees for methods like
11996    * `_.reduce`, `_.reduceRight`, and `_.transform`.
11997    *
11998    * The guarded methods are:
11999    * `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder`
12000    *
12001    * @static
12002    * @memberOf _
12003    * @alias foldl, inject
12004    * @category Collection
12005    * @param {Array|Object|string} collection The collection to iterate over.
12006    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12007    * @param {*} [accumulator] The initial value.
12008    * @param {*} [thisArg] The `this` binding of `iteratee`.
12009    * @returns {*} Returns the accumulated value.
12010    * @example
12011    *
12012    * _.reduce([1, 2], function(total, n) {
12013    *   return total + n;
12014    * });
12015    * // => 3
12016    *
12017    * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
12018    *   result[key] = n * 3;
12019    *   return result;
12020    * }, {});
12021    * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
12022    */
12023   var reduce = createReduce(arrayReduce, baseEach);
12024
12025   /**
12026    * The opposite of `_.filter`; this method returns the elements of `collection`
12027    * that `predicate` does **not** return truthy for.
12028    *
12029    * @static
12030    * @memberOf _
12031    * @category Collection
12032    * @param {Array|Object|string} collection The collection to iterate over.
12033    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12034    *  per iteration.
12035    * @param {*} [thisArg] The `this` binding of `predicate`.
12036    * @returns {Array} Returns the new filtered array.
12037    * @example
12038    *
12039    * _.reject([1, 2, 3, 4], function(n) {
12040    *   return n % 2 == 0;
12041    * });
12042    * // => [1, 3]
12043    *
12044    * var users = [
12045    *   { 'user': 'barney', 'age': 36, 'active': false },
12046    *   { 'user': 'fred',   'age': 40, 'active': true }
12047    * ];
12048    *
12049    * // using the `_.matches` callback shorthand
12050    * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
12051    * // => ['barney']
12052    *
12053    * // using the `_.matchesProperty` callback shorthand
12054    * _.pluck(_.reject(users, 'active', false), 'user');
12055    * // => ['fred']
12056    *
12057    * // using the `_.property` callback shorthand
12058    * _.pluck(_.reject(users, 'active'), 'user');
12059    * // => ['barney']
12060    */
12061   function reject(collection, predicate, thisArg) {
12062     var func = isArray(collection) ? arrayFilter : baseFilter;
12063     predicate = getCallback(predicate, thisArg, 3);
12064     return func(collection, function(value, index, collection) {
12065       return !predicate(value, index, collection);
12066     });
12067   }
12068
12069   /**
12070    * Checks if `predicate` returns truthy for **any** element of `collection`.
12071    * The function returns as soon as it finds a passing value and does not iterate
12072    * over the entire collection. The predicate is bound to `thisArg` and invoked
12073    * with three arguments: (value, index|key, collection).
12074    *
12075    * If a property name is provided for `predicate` the created `_.property`
12076    * style callback returns the property value of the given element.
12077    *
12078    * If a value is also provided for `thisArg` the created `_.matchesProperty`
12079    * style callback returns `true` for elements that have a matching property
12080    * value, else `false`.
12081    *
12082    * If an object is provided for `predicate` the created `_.matches` style
12083    * callback returns `true` for elements that have the properties of the given
12084    * object, else `false`.
12085    *
12086    * @static
12087    * @memberOf _
12088    * @alias any
12089    * @category Collection
12090    * @param {Array|Object|string} collection The collection to iterate over.
12091    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12092    *  per iteration.
12093    * @param {*} [thisArg] The `this` binding of `predicate`.
12094    * @returns {boolean} Returns `true` if any element passes the predicate check,
12095    *  else `false`.
12096    * @example
12097    *
12098    * _.some([null, 0, 'yes', false], Boolean);
12099    * // => true
12100    *
12101    * var users = [
12102    *   { 'user': 'barney', 'active': true },
12103    *   { 'user': 'fred',   'active': false }
12104    * ];
12105    *
12106    * // using the `_.matches` callback shorthand
12107    * _.some(users, { 'user': 'barney', 'active': false });
12108    * // => false
12109    *
12110    * // using the `_.matchesProperty` callback shorthand
12111    * _.some(users, 'active', false);
12112    * // => true
12113    *
12114    * // using the `_.property` callback shorthand
12115    * _.some(users, 'active');
12116    * // => true
12117    */
12118   function some(collection, predicate, thisArg) {
12119     var func = isArray(collection) ? arraySome : baseSome;
12120     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
12121       predicate = null;
12122     }
12123     if (typeof predicate != 'function' || thisArg !== undefined) {
12124       predicate = getCallback(predicate, thisArg, 3);
12125     }
12126     return func(collection, predicate);
12127   }
12128
12129   /*------------------------------------------------------------------------*/
12130
12131   /**
12132    * Gets the number of milliseconds that have elapsed since the Unix epoch
12133    * (1 January 1970 00:00:00 UTC).
12134    *
12135    * @static
12136    * @memberOf _
12137    * @category Date
12138    * @example
12139    *
12140    * _.defer(function(stamp) {
12141    *   console.log(_.now() - stamp);
12142    * }, _.now());
12143    * // => logs the number of milliseconds it took for the deferred function to be invoked
12144    */
12145   var now = nativeNow || function() {
12146     return new Date().getTime();
12147   };
12148
12149   /*------------------------------------------------------------------------*/
12150
12151   /**
12152    * Creates a function that invokes `func` with the `this` binding of `thisArg`
12153    * and prepends any additional `_.bind` arguments to those provided to the
12154    * bound function.
12155    *
12156    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
12157    * may be used as a placeholder for partially applied arguments.
12158    *
12159    * **Note:** Unlike native `Function#bind` this method does not set the "length"
12160    * property of bound functions.
12161    *
12162    * @static
12163    * @memberOf _
12164    * @category Function
12165    * @param {Function} func The function to bind.
12166    * @param {*} thisArg The `this` binding of `func`.
12167    * @param {...*} [partials] The arguments to be partially applied.
12168    * @returns {Function} Returns the new bound function.
12169    * @example
12170    *
12171    * var greet = function(greeting, punctuation) {
12172    *   return greeting + ' ' + this.user + punctuation;
12173    * };
12174    *
12175    * var object = { 'user': 'fred' };
12176    *
12177    * var bound = _.bind(greet, object, 'hi');
12178    * bound('!');
12179    * // => 'hi fred!'
12180    *
12181    * // using placeholders
12182    * var bound = _.bind(greet, object, _, '!');
12183    * bound('hi');
12184    * // => 'hi fred!'
12185    */
12186   var bind = restParam(function(func, thisArg, partials) {
12187     var bitmask = BIND_FLAG;
12188     if (partials.length) {
12189       var holders = replaceHolders(partials, bind.placeholder);
12190       bitmask |= PARTIAL_FLAG;
12191     }
12192     return createWrapper(func, bitmask, thisArg, partials, holders);
12193   });
12194
12195   /**
12196    * Creates a debounced function that delays invoking `func` until after `wait`
12197    * milliseconds have elapsed since the last time the debounced function was
12198    * invoked. The debounced function comes with a `cancel` method to cancel
12199    * delayed invocations. Provide an options object to indicate that `func`
12200    * should be invoked on the leading and/or trailing edge of the `wait` timeout.
12201    * Subsequent calls to the debounced function return the result of the last
12202    * `func` invocation.
12203    *
12204    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12205    * on the trailing edge of the timeout only if the the debounced function is
12206    * invoked more than once during the `wait` timeout.
12207    *
12208    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12209    * for details over the differences between `_.debounce` and `_.throttle`.
12210    *
12211    * @static
12212    * @memberOf _
12213    * @category Function
12214    * @param {Function} func The function to debounce.
12215    * @param {number} [wait=0] The number of milliseconds to delay.
12216    * @param {Object} [options] The options object.
12217    * @param {boolean} [options.leading=false] Specify invoking on the leading
12218    *  edge of the timeout.
12219    * @param {number} [options.maxWait] The maximum time `func` is allowed to be
12220    *  delayed before it is invoked.
12221    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12222    *  edge of the timeout.
12223    * @returns {Function} Returns the new debounced function.
12224    * @example
12225    *
12226    * // avoid costly calculations while the window size is in flux
12227    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
12228    *
12229    * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
12230    * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
12231    *   'leading': true,
12232    *   'trailing': false
12233    * }));
12234    *
12235    * // ensure `batchLog` is invoked once after 1 second of debounced calls
12236    * var source = new EventSource('/stream');
12237    * jQuery(source).on('message', _.debounce(batchLog, 250, {
12238    *   'maxWait': 1000
12239    * }));
12240    *
12241    * // cancel a debounced call
12242    * var todoChanges = _.debounce(batchLog, 1000);
12243    * Object.observe(models.todo, todoChanges);
12244    *
12245    * Object.observe(models, function(changes) {
12246    *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
12247    *     todoChanges.cancel();
12248    *   }
12249    * }, ['delete']);
12250    *
12251    * // ...at some point `models.todo` is changed
12252    * models.todo.completed = true;
12253    *
12254    * // ...before 1 second has passed `models.todo` is deleted
12255    * // which cancels the debounced `todoChanges` call
12256    * delete models.todo;
12257    */
12258   function debounce(func, wait, options) {
12259     var args,
12260         maxTimeoutId,
12261         result,
12262         stamp,
12263         thisArg,
12264         timeoutId,
12265         trailingCall,
12266         lastCalled = 0,
12267         maxWait = false,
12268         trailing = true;
12269
12270     if (typeof func != 'function') {
12271       throw new TypeError(FUNC_ERROR_TEXT);
12272     }
12273     wait = wait < 0 ? 0 : (+wait || 0);
12274     if (options === true) {
12275       var leading = true;
12276       trailing = false;
12277     } else if (isObject(options)) {
12278       leading = options.leading;
12279       maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
12280       trailing = 'trailing' in options ? options.trailing : trailing;
12281     }
12282
12283     function cancel() {
12284       if (timeoutId) {
12285         clearTimeout(timeoutId);
12286       }
12287       if (maxTimeoutId) {
12288         clearTimeout(maxTimeoutId);
12289       }
12290       maxTimeoutId = timeoutId = trailingCall = undefined;
12291     }
12292
12293     function delayed() {
12294       var remaining = wait - (now() - stamp);
12295       if (remaining <= 0 || remaining > wait) {
12296         if (maxTimeoutId) {
12297           clearTimeout(maxTimeoutId);
12298         }
12299         var isCalled = trailingCall;
12300         maxTimeoutId = timeoutId = trailingCall = undefined;
12301         if (isCalled) {
12302           lastCalled = now();
12303           result = func.apply(thisArg, args);
12304           if (!timeoutId && !maxTimeoutId) {
12305             args = thisArg = null;
12306           }
12307         }
12308       } else {
12309         timeoutId = setTimeout(delayed, remaining);
12310       }
12311     }
12312
12313     function maxDelayed() {
12314       if (timeoutId) {
12315         clearTimeout(timeoutId);
12316       }
12317       maxTimeoutId = timeoutId = trailingCall = undefined;
12318       if (trailing || (maxWait !== wait)) {
12319         lastCalled = now();
12320         result = func.apply(thisArg, args);
12321         if (!timeoutId && !maxTimeoutId) {
12322           args = thisArg = null;
12323         }
12324       }
12325     }
12326
12327     function debounced() {
12328       args = arguments;
12329       stamp = now();
12330       thisArg = this;
12331       trailingCall = trailing && (timeoutId || !leading);
12332
12333       if (maxWait === false) {
12334         var leadingCall = leading && !timeoutId;
12335       } else {
12336         if (!maxTimeoutId && !leading) {
12337           lastCalled = stamp;
12338         }
12339         var remaining = maxWait - (stamp - lastCalled),
12340             isCalled = remaining <= 0 || remaining > maxWait;
12341
12342         if (isCalled) {
12343           if (maxTimeoutId) {
12344             maxTimeoutId = clearTimeout(maxTimeoutId);
12345           }
12346           lastCalled = stamp;
12347           result = func.apply(thisArg, args);
12348         }
12349         else if (!maxTimeoutId) {
12350           maxTimeoutId = setTimeout(maxDelayed, remaining);
12351         }
12352       }
12353       if (isCalled && timeoutId) {
12354         timeoutId = clearTimeout(timeoutId);
12355       }
12356       else if (!timeoutId && wait !== maxWait) {
12357         timeoutId = setTimeout(delayed, wait);
12358       }
12359       if (leadingCall) {
12360         isCalled = true;
12361         result = func.apply(thisArg, args);
12362       }
12363       if (isCalled && !timeoutId && !maxTimeoutId) {
12364         args = thisArg = null;
12365       }
12366       return result;
12367     }
12368     debounced.cancel = cancel;
12369     return debounced;
12370   }
12371
12372   /**
12373    * Creates a function that invokes `func` with the `this` binding of the
12374    * created function and arguments from `start` and beyond provided as an array.
12375    *
12376    * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
12377    *
12378    * @static
12379    * @memberOf _
12380    * @category Function
12381    * @param {Function} func The function to apply a rest parameter to.
12382    * @param {number} [start=func.length-1] The start position of the rest parameter.
12383    * @returns {Function} Returns the new function.
12384    * @example
12385    *
12386    * var say = _.restParam(function(what, names) {
12387    *   return what + ' ' + _.initial(names).join(', ') +
12388    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
12389    * });
12390    *
12391    * say('hello', 'fred', 'barney', 'pebbles');
12392    * // => 'hello fred, barney, & pebbles'
12393    */
12394   function restParam(func, start) {
12395     if (typeof func != 'function') {
12396       throw new TypeError(FUNC_ERROR_TEXT);
12397     }
12398     start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
12399     return function() {
12400       var args = arguments,
12401           index = -1,
12402           length = nativeMax(args.length - start, 0),
12403           rest = Array(length);
12404
12405       while (++index < length) {
12406         rest[index] = args[start + index];
12407       }
12408       switch (start) {
12409         case 0: return func.call(this, rest);
12410         case 1: return func.call(this, args[0], rest);
12411         case 2: return func.call(this, args[0], args[1], rest);
12412       }
12413       var otherArgs = Array(start + 1);
12414       index = -1;
12415       while (++index < start) {
12416         otherArgs[index] = args[index];
12417       }
12418       otherArgs[start] = rest;
12419       return func.apply(this, otherArgs);
12420     };
12421   }
12422
12423   /**
12424    * Creates a throttled function that only invokes `func` at most once per
12425    * every `wait` milliseconds. The throttled function comes with a `cancel`
12426    * method to cancel delayed invocations. Provide an options object to indicate
12427    * that `func` should be invoked on the leading and/or trailing edge of the
12428    * `wait` timeout. Subsequent calls to the throttled function return the
12429    * result of the last `func` call.
12430    *
12431    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12432    * on the trailing edge of the timeout only if the the throttled function is
12433    * invoked more than once during the `wait` timeout.
12434    *
12435    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12436    * for details over the differences between `_.throttle` and `_.debounce`.
12437    *
12438    * @static
12439    * @memberOf _
12440    * @category Function
12441    * @param {Function} func The function to throttle.
12442    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
12443    * @param {Object} [options] The options object.
12444    * @param {boolean} [options.leading=true] Specify invoking on the leading
12445    *  edge of the timeout.
12446    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12447    *  edge of the timeout.
12448    * @returns {Function} Returns the new throttled function.
12449    * @example
12450    *
12451    * // avoid excessively updating the position while scrolling
12452    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
12453    *
12454    * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
12455    * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
12456    *   'trailing': false
12457    * }));
12458    *
12459    * // cancel a trailing throttled call
12460    * jQuery(window).on('popstate', throttled.cancel);
12461    */
12462   function throttle(func, wait, options) {
12463     var leading = true,
12464         trailing = true;
12465
12466     if (typeof func != 'function') {
12467       throw new TypeError(FUNC_ERROR_TEXT);
12468     }
12469     if (options === false) {
12470       leading = false;
12471     } else if (isObject(options)) {
12472       leading = 'leading' in options ? !!options.leading : leading;
12473       trailing = 'trailing' in options ? !!options.trailing : trailing;
12474     }
12475     debounceOptions.leading = leading;
12476     debounceOptions.maxWait = +wait;
12477     debounceOptions.trailing = trailing;
12478     return debounce(func, wait, debounceOptions);
12479   }
12480
12481   /*------------------------------------------------------------------------*/
12482
12483   /**
12484    * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
12485    * otherwise they are assigned by reference. If `customizer` is provided it is
12486    * invoked to produce the cloned values. If `customizer` returns `undefined`
12487    * cloning is handled by the method instead. The `customizer` is bound to
12488    * `thisArg` and invoked with two argument; (value [, index|key, object]).
12489    *
12490    * **Note:** This method is loosely based on the
12491    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12492    * The enumerable properties of `arguments` objects and objects created by
12493    * constructors other than `Object` are cloned to plain `Object` objects. An
12494    * empty object is returned for uncloneable values such as functions, DOM nodes,
12495    * Maps, Sets, and WeakMaps.
12496    *
12497    * @static
12498    * @memberOf _
12499    * @category Lang
12500    * @param {*} value The value to clone.
12501    * @param {boolean} [isDeep] Specify a deep clone.
12502    * @param {Function} [customizer] The function to customize cloning values.
12503    * @param {*} [thisArg] The `this` binding of `customizer`.
12504    * @returns {*} Returns the cloned value.
12505    * @example
12506    *
12507    * var users = [
12508    *   { 'user': 'barney' },
12509    *   { 'user': 'fred' }
12510    * ];
12511    *
12512    * var shallow = _.clone(users);
12513    * shallow[0] === users[0];
12514    * // => true
12515    *
12516    * var deep = _.clone(users, true);
12517    * deep[0] === users[0];
12518    * // => false
12519    *
12520    * // using a customizer callback
12521    * var el = _.clone(document.body, function(value) {
12522    *   if (_.isElement(value)) {
12523    *     return value.cloneNode(false);
12524    *   }
12525    * });
12526    *
12527    * el === document.body
12528    * // => false
12529    * el.nodeName
12530    * // => BODY
12531    * el.childNodes.length;
12532    * // => 0
12533    */
12534   function clone(value, isDeep, customizer, thisArg) {
12535     if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
12536       isDeep = false;
12537     }
12538     else if (typeof isDeep == 'function') {
12539       thisArg = customizer;
12540       customizer = isDeep;
12541       isDeep = false;
12542     }
12543     return typeof customizer == 'function'
12544       ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
12545       : baseClone(value, isDeep);
12546   }
12547
12548   /**
12549    * Creates a deep clone of `value`. If `customizer` is provided it is invoked
12550    * to produce the cloned values. If `customizer` returns `undefined` cloning
12551    * is handled by the method instead. The `customizer` is bound to `thisArg`
12552    * and invoked with two argument; (value [, index|key, object]).
12553    *
12554    * **Note:** This method is loosely based on the
12555    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12556    * The enumerable properties of `arguments` objects and objects created by
12557    * constructors other than `Object` are cloned to plain `Object` objects. An
12558    * empty object is returned for uncloneable values such as functions, DOM nodes,
12559    * Maps, Sets, and WeakMaps.
12560    *
12561    * @static
12562    * @memberOf _
12563    * @category Lang
12564    * @param {*} value The value to deep clone.
12565    * @param {Function} [customizer] The function to customize cloning values.
12566    * @param {*} [thisArg] The `this` binding of `customizer`.
12567    * @returns {*} Returns the deep cloned value.
12568    * @example
12569    *
12570    * var users = [
12571    *   { 'user': 'barney' },
12572    *   { 'user': 'fred' }
12573    * ];
12574    *
12575    * var deep = _.cloneDeep(users);
12576    * deep[0] === users[0];
12577    * // => false
12578    *
12579    * // using a customizer callback
12580    * var el = _.cloneDeep(document.body, function(value) {
12581    *   if (_.isElement(value)) {
12582    *     return value.cloneNode(true);
12583    *   }
12584    * });
12585    *
12586    * el === document.body
12587    * // => false
12588    * el.nodeName
12589    * // => BODY
12590    * el.childNodes.length;
12591    * // => 20
12592    */
12593   function cloneDeep(value, customizer, thisArg) {
12594     return typeof customizer == 'function'
12595       ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
12596       : baseClone(value, true);
12597   }
12598
12599   /**
12600    * Checks if `value` is classified as an `arguments` object.
12601    *
12602    * @static
12603    * @memberOf _
12604    * @category Lang
12605    * @param {*} value The value to check.
12606    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12607    * @example
12608    *
12609    * _.isArguments(function() { return arguments; }());
12610    * // => true
12611    *
12612    * _.isArguments([1, 2, 3]);
12613    * // => false
12614    */
12615   function isArguments(value) {
12616     return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
12617   }
12618   // Fallback for environments without a `toStringTag` for `arguments` objects.
12619   if (!support.argsTag) {
12620     isArguments = function(value) {
12621       return isObjectLike(value) && isArrayLike(value) &&
12622         hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
12623     };
12624   }
12625
12626   /**
12627    * Checks if `value` is classified as an `Array` object.
12628    *
12629    * @static
12630    * @memberOf _
12631    * @category Lang
12632    * @param {*} value The value to check.
12633    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12634    * @example
12635    *
12636    * _.isArray([1, 2, 3]);
12637    * // => true
12638    *
12639    * _.isArray(function() { return arguments; }());
12640    * // => false
12641    */
12642   var isArray = nativeIsArray || function(value) {
12643     return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
12644   };
12645
12646   /**
12647    * Checks if `value` is empty. A value is considered empty unless it is an
12648    * `arguments` object, array, string, or jQuery-like collection with a length
12649    * greater than `0` or an object with own enumerable properties.
12650    *
12651    * @static
12652    * @memberOf _
12653    * @category Lang
12654    * @param {Array|Object|string} value The value to inspect.
12655    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
12656    * @example
12657    *
12658    * _.isEmpty(null);
12659    * // => true
12660    *
12661    * _.isEmpty(true);
12662    * // => true
12663    *
12664    * _.isEmpty(1);
12665    * // => true
12666    *
12667    * _.isEmpty([1, 2, 3]);
12668    * // => false
12669    *
12670    * _.isEmpty({ 'a': 1 });
12671    * // => false
12672    */
12673   function isEmpty(value) {
12674     if (value == null) {
12675       return true;
12676     }
12677     if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
12678         (isObjectLike(value) && isFunction(value.splice)))) {
12679       return !value.length;
12680     }
12681     return !keys(value).length;
12682   }
12683
12684   /**
12685    * Performs a deep comparison between two values to determine if they are
12686    * equivalent. If `customizer` is provided it is invoked to compare values.
12687    * If `customizer` returns `undefined` comparisons are handled by the method
12688    * instead. The `customizer` is bound to `thisArg` and invoked with three
12689    * arguments: (value, other [, index|key]).
12690    *
12691    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
12692    * numbers, `Object` objects, regexes, and strings. Objects are compared by
12693    * their own, not inherited, enumerable properties. Functions and DOM nodes
12694    * are **not** supported. Provide a customizer function to extend support
12695    * for comparing other values.
12696    *
12697    * @static
12698    * @memberOf _
12699    * @alias eq
12700    * @category Lang
12701    * @param {*} value The value to compare.
12702    * @param {*} other The other value to compare.
12703    * @param {Function} [customizer] The function to customize value comparisons.
12704    * @param {*} [thisArg] The `this` binding of `customizer`.
12705    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
12706    * @example
12707    *
12708    * var object = { 'user': 'fred' };
12709    * var other = { 'user': 'fred' };
12710    *
12711    * object == other;
12712    * // => false
12713    *
12714    * _.isEqual(object, other);
12715    * // => true
12716    *
12717    * // using a customizer callback
12718    * var array = ['hello', 'goodbye'];
12719    * var other = ['hi', 'goodbye'];
12720    *
12721    * _.isEqual(array, other, function(value, other) {
12722    *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
12723    *     return true;
12724    *   }
12725    * });
12726    * // => true
12727    */
12728   function isEqual(value, other, customizer, thisArg) {
12729     customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
12730     var result = customizer ? customizer(value, other) : undefined;
12731     return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
12732   }
12733
12734   /**
12735    * Checks if `value` is classified as a `Function` object.
12736    *
12737    * @static
12738    * @memberOf _
12739    * @category Lang
12740    * @param {*} value The value to check.
12741    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12742    * @example
12743    *
12744    * _.isFunction(_);
12745    * // => true
12746    *
12747    * _.isFunction(/abc/);
12748    * // => false
12749    */
12750   var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
12751     // The use of `Object#toString` avoids issues with the `typeof` operator
12752     // in older versions of Chrome and Safari which return 'function' for regexes
12753     // and Safari 8 equivalents which return 'object' for typed array constructors.
12754     return objToString.call(value) == funcTag;
12755   };
12756
12757   /**
12758    * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
12759    * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
12760    *
12761    * @static
12762    * @memberOf _
12763    * @category Lang
12764    * @param {*} value The value to check.
12765    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
12766    * @example
12767    *
12768    * _.isObject({});
12769    * // => true
12770    *
12771    * _.isObject([1, 2, 3]);
12772    * // => true
12773    *
12774    * _.isObject(1);
12775    * // => false
12776    */
12777   function isObject(value) {
12778     // Avoid a V8 JIT bug in Chrome 19-20.
12779     // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
12780     var type = typeof value;
12781     return !!value && (type == 'object' || type == 'function');
12782   }
12783
12784   /**
12785    * Checks if `value` is a native function.
12786    *
12787    * @static
12788    * @memberOf _
12789    * @category Lang
12790    * @param {*} value The value to check.
12791    * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
12792    * @example
12793    *
12794    * _.isNative(Array.prototype.push);
12795    * // => true
12796    *
12797    * _.isNative(_);
12798    * // => false
12799    */
12800   function isNative(value) {
12801     if (value == null) {
12802       return false;
12803     }
12804     if (objToString.call(value) == funcTag) {
12805       return reIsNative.test(fnToString.call(value));
12806     }
12807     return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
12808   }
12809
12810   /**
12811    * Checks if `value` is a plain object, that is, an object created by the
12812    * `Object` constructor or one with a `[[Prototype]]` of `null`.
12813    *
12814    * **Note:** This method assumes objects created by the `Object` constructor
12815    * have no inherited enumerable properties.
12816    *
12817    * @static
12818    * @memberOf _
12819    * @category Lang
12820    * @param {*} value The value to check.
12821    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
12822    * @example
12823    *
12824    * function Foo() {
12825    *   this.a = 1;
12826    * }
12827    *
12828    * _.isPlainObject(new Foo);
12829    * // => false
12830    *
12831    * _.isPlainObject([1, 2, 3]);
12832    * // => false
12833    *
12834    * _.isPlainObject({ 'x': 0, 'y': 0 });
12835    * // => true
12836    *
12837    * _.isPlainObject(Object.create(null));
12838    * // => true
12839    */
12840   var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
12841     if (!(value && objToString.call(value) == objectTag) || (!lodash.support.argsTag && isArguments(value))) {
12842       return false;
12843     }
12844     var valueOf = getNative(value, 'valueOf'),
12845         objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
12846
12847     return objProto
12848       ? (value == objProto || getPrototypeOf(value) == objProto)
12849       : shimIsPlainObject(value);
12850   };
12851
12852   /**
12853    * Checks if `value` is classified as a `String` primitive or object.
12854    *
12855    * @static
12856    * @memberOf _
12857    * @category Lang
12858    * @param {*} value The value to check.
12859    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12860    * @example
12861    *
12862    * _.isString('abc');
12863    * // => true
12864    *
12865    * _.isString(1);
12866    * // => false
12867    */
12868   function isString(value) {
12869     return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
12870   }
12871
12872   /**
12873    * Checks if `value` is classified as a typed array.
12874    *
12875    * @static
12876    * @memberOf _
12877    * @category Lang
12878    * @param {*} value The value to check.
12879    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12880    * @example
12881    *
12882    * _.isTypedArray(new Uint8Array);
12883    * // => true
12884    *
12885    * _.isTypedArray([]);
12886    * // => false
12887    */
12888   function isTypedArray(value) {
12889     return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
12890   }
12891
12892   /**
12893    * Converts `value` to a plain object flattening inherited enumerable
12894    * properties of `value` to own properties of the plain object.
12895    *
12896    * @static
12897    * @memberOf _
12898    * @category Lang
12899    * @param {*} value The value to convert.
12900    * @returns {Object} Returns the converted plain object.
12901    * @example
12902    *
12903    * function Foo() {
12904    *   this.b = 2;
12905    * }
12906    *
12907    * Foo.prototype.c = 3;
12908    *
12909    * _.assign({ 'a': 1 }, new Foo);
12910    * // => { 'a': 1, 'b': 2 }
12911    *
12912    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
12913    * // => { 'a': 1, 'b': 2, 'c': 3 }
12914    */
12915   function toPlainObject(value) {
12916     return baseCopy(value, keysIn(value));
12917   }
12918
12919   /*------------------------------------------------------------------------*/
12920
12921   /**
12922    * Assigns own enumerable properties of source object(s) to the destination
12923    * object. Subsequent sources overwrite property assignments of previous sources.
12924    * If `customizer` is provided it is invoked to produce the assigned values.
12925    * The `customizer` is bound to `thisArg` and invoked with five arguments:
12926    * (objectValue, sourceValue, key, object, source).
12927    *
12928    * **Note:** This method mutates `object` and is based on
12929    * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign).
12930    *
12931    * @static
12932    * @memberOf _
12933    * @alias extend
12934    * @category Object
12935    * @param {Object} object The destination object.
12936    * @param {...Object} [sources] The source objects.
12937    * @param {Function} [customizer] The function to customize assigned values.
12938    * @param {*} [thisArg] The `this` binding of `customizer`.
12939    * @returns {Object} Returns `object`.
12940    * @example
12941    *
12942    * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
12943    * // => { 'user': 'fred', 'age': 40 }
12944    *
12945    * // using a customizer callback
12946    * var defaults = _.partialRight(_.assign, function(value, other) {
12947    *   return _.isUndefined(value) ? other : value;
12948    * });
12949    *
12950    * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
12951    * // => { 'user': 'barney', 'age': 36 }
12952    */
12953   var assign = createAssigner(function(object, source, customizer) {
12954     return customizer
12955       ? assignWith(object, source, customizer)
12956       : baseAssign(object, source);
12957   });
12958
12959   /**
12960    * Iterates over own enumerable properties of an object invoking `iteratee`
12961    * for each property. The `iteratee` is bound to `thisArg` and invoked with
12962    * three arguments: (value, key, object). Iteratee functions may exit iteration
12963    * early by explicitly returning `false`.
12964    *
12965    * @static
12966    * @memberOf _
12967    * @category Object
12968    * @param {Object} object The object to iterate over.
12969    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12970    * @param {*} [thisArg] The `this` binding of `iteratee`.
12971    * @returns {Object} Returns `object`.
12972    * @example
12973    *
12974    * function Foo() {
12975    *   this.a = 1;
12976    *   this.b = 2;
12977    * }
12978    *
12979    * Foo.prototype.c = 3;
12980    *
12981    * _.forOwn(new Foo, function(value, key) {
12982    *   console.log(key);
12983    * });
12984    * // => logs 'a' and 'b' (iteration order is not guaranteed)
12985    */
12986   var forOwn = createForOwn(baseForOwn);
12987
12988   /**
12989    * Creates an array of the own enumerable property names of `object`.
12990    *
12991    * **Note:** Non-object values are coerced to objects. See the
12992    * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
12993    * for more details.
12994    *
12995    * @static
12996    * @memberOf _
12997    * @category Object
12998    * @param {Object} object The object to query.
12999    * @returns {Array} Returns the array of property names.
13000    * @example
13001    *
13002    * function Foo() {
13003    *   this.a = 1;
13004    *   this.b = 2;
13005    * }
13006    *
13007    * Foo.prototype.c = 3;
13008    *
13009    * _.keys(new Foo);
13010    * // => ['a', 'b'] (iteration order is not guaranteed)
13011    *
13012    * _.keys('hi');
13013    * // => ['0', '1']
13014    */
13015   var keys = !nativeKeys ? shimKeys : function(object) {
13016     var Ctor = object == null ? null : object.constructor;
13017     if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
13018         (typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
13019       return shimKeys(object);
13020     }
13021     return isObject(object) ? nativeKeys(object) : [];
13022   };
13023
13024   /**
13025    * Creates an array of the own and inherited enumerable property names of `object`.
13026    *
13027    * **Note:** Non-object values are coerced to objects.
13028    *
13029    * @static
13030    * @memberOf _
13031    * @category Object
13032    * @param {Object} object The object to query.
13033    * @returns {Array} Returns the array of property names.
13034    * @example
13035    *
13036    * function Foo() {
13037    *   this.a = 1;
13038    *   this.b = 2;
13039    * }
13040    *
13041    * Foo.prototype.c = 3;
13042    *
13043    * _.keysIn(new Foo);
13044    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
13045    */
13046   function keysIn(object) {
13047     if (object == null) {
13048       return [];
13049     }
13050     if (!isObject(object)) {
13051       object = Object(object);
13052     }
13053     var length = object.length,
13054         support = lodash.support;
13055
13056     length = (length && isLength(length) &&
13057       (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
13058
13059     var Ctor = object.constructor,
13060         index = -1,
13061         proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
13062         isProto = proto === object,
13063         result = Array(length),
13064         skipIndexes = length > 0,
13065         skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
13066         skipProto = support.enumPrototypes && isFunction(object);
13067
13068     while (++index < length) {
13069       result[index] = (index + '');
13070     }
13071     // lodash skips the `constructor` property when it infers it is iterating
13072     // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
13073     // attribute of an existing property and the `constructor` property of a
13074     // prototype defaults to non-enumerable.
13075     for (var key in object) {
13076       if (!(skipProto && key == 'prototype') &&
13077           !(skipErrorProps && (key == 'message' || key == 'name')) &&
13078           !(skipIndexes && isIndex(key, length)) &&
13079           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
13080         result.push(key);
13081       }
13082     }
13083     if (support.nonEnumShadows && object !== objectProto) {
13084       var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
13085           nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
13086
13087       if (tag == objectTag) {
13088         proto = objectProto;
13089       }
13090       length = shadowProps.length;
13091       while (length--) {
13092         key = shadowProps[length];
13093         var nonEnum = nonEnums[key];
13094         if (!(isProto && nonEnum) &&
13095             (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
13096           result.push(key);
13097         }
13098       }
13099     }
13100     return result;
13101   }
13102
13103   /**
13104    * Recursively merges own enumerable properties of the source object(s), that
13105    * don't resolve to `undefined` into the destination object. Subsequent sources
13106    * overwrite property assignments of previous sources. If `customizer` is
13107    * provided it is invoked to produce the merged values of the destination and
13108    * source properties. If `customizer` returns `undefined` merging is handled
13109    * by the method instead. The `customizer` is bound to `thisArg` and invoked
13110    * with five arguments: (objectValue, sourceValue, key, object, source).
13111    *
13112    * @static
13113    * @memberOf _
13114    * @category Object
13115    * @param {Object} object The destination object.
13116    * @param {...Object} [sources] The source objects.
13117    * @param {Function} [customizer] The function to customize assigned values.
13118    * @param {*} [thisArg] The `this` binding of `customizer`.
13119    * @returns {Object} Returns `object`.
13120    * @example
13121    *
13122    * var users = {
13123    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
13124    * };
13125    *
13126    * var ages = {
13127    *   'data': [{ 'age': 36 }, { 'age': 40 }]
13128    * };
13129    *
13130    * _.merge(users, ages);
13131    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
13132    *
13133    * // using a customizer callback
13134    * var object = {
13135    *   'fruits': ['apple'],
13136    *   'vegetables': ['beet']
13137    * };
13138    *
13139    * var other = {
13140    *   'fruits': ['banana'],
13141    *   'vegetables': ['carrot']
13142    * };
13143    *
13144    * _.merge(object, other, function(a, b) {
13145    *   if (_.isArray(a)) {
13146    *     return a.concat(b);
13147    *   }
13148    * });
13149    * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
13150    */
13151   var merge = createAssigner(baseMerge);
13152
13153   /**
13154    * The opposite of `_.pick`; this method creates an object composed of the
13155    * own and inherited enumerable properties of `object` that are not omitted.
13156    *
13157    * @static
13158    * @memberOf _
13159    * @category Object
13160    * @param {Object} object The source object.
13161    * @param {Function|...(string|string[])} [predicate] The function invoked per
13162    *  iteration or property names to omit, specified as individual property
13163    *  names or arrays of property names.
13164    * @param {*} [thisArg] The `this` binding of `predicate`.
13165    * @returns {Object} Returns the new object.
13166    * @example
13167    *
13168    * var object = { 'user': 'fred', 'age': 40 };
13169    *
13170    * _.omit(object, 'age');
13171    * // => { 'user': 'fred' }
13172    *
13173    * _.omit(object, _.isNumber);
13174    * // => { 'user': 'fred' }
13175    */
13176   var omit = restParam(function(object, props) {
13177     if (object == null) {
13178       return {};
13179     }
13180     if (typeof props[0] != 'function') {
13181       var props = arrayMap(baseFlatten(props), String);
13182       return pickByArray(object, baseDifference(keysIn(object), props));
13183     }
13184     var predicate = bindCallback(props[0], props[1], 3);
13185     return pickByCallback(object, function(value, key, object) {
13186       return !predicate(value, key, object);
13187     });
13188   });
13189
13190   /**
13191    * Creates a two dimensional array of the key-value pairs for `object`,
13192    * e.g. `[[key1, value1], [key2, value2]]`.
13193    *
13194    * @static
13195    * @memberOf _
13196    * @category Object
13197    * @param {Object} object The object to query.
13198    * @returns {Array} Returns the new array of key-value pairs.
13199    * @example
13200    *
13201    * _.pairs({ 'barney': 36, 'fred': 40 });
13202    * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
13203    */
13204   function pairs(object) {
13205     object = toObject(object);
13206
13207     var index = -1,
13208         props = keys(object),
13209         length = props.length,
13210         result = Array(length);
13211
13212     while (++index < length) {
13213       var key = props[index];
13214       result[index] = [key, object[key]];
13215     }
13216     return result;
13217   }
13218
13219   /**
13220    * Creates an object composed of the picked `object` properties. Property
13221    * names may be specified as individual arguments or as arrays of property
13222    * names. If `predicate` is provided it is invoked for each property of `object`
13223    * picking the properties `predicate` returns truthy for. The predicate is
13224    * bound to `thisArg` and invoked with three arguments: (value, key, object).
13225    *
13226    * @static
13227    * @memberOf _
13228    * @category Object
13229    * @param {Object} object The source object.
13230    * @param {Function|...(string|string[])} [predicate] The function invoked per
13231    *  iteration or property names to pick, specified as individual property
13232    *  names or arrays of property names.
13233    * @param {*} [thisArg] The `this` binding of `predicate`.
13234    * @returns {Object} Returns the new object.
13235    * @example
13236    *
13237    * var object = { 'user': 'fred', 'age': 40 };
13238    *
13239    * _.pick(object, 'user');
13240    * // => { 'user': 'fred' }
13241    *
13242    * _.pick(object, _.isString);
13243    * // => { 'user': 'fred' }
13244    */
13245   var pick = restParam(function(object, props) {
13246     if (object == null) {
13247       return {};
13248     }
13249     return typeof props[0] == 'function'
13250       ? pickByCallback(object, bindCallback(props[0], props[1], 3))
13251       : pickByArray(object, baseFlatten(props));
13252   });
13253
13254   /**
13255    * Creates an array of the own enumerable property values of `object`.
13256    *
13257    * **Note:** Non-object values are coerced to objects.
13258    *
13259    * @static
13260    * @memberOf _
13261    * @category Object
13262    * @param {Object} object The object to query.
13263    * @returns {Array} Returns the array of property values.
13264    * @example
13265    *
13266    * function Foo() {
13267    *   this.a = 1;
13268    *   this.b = 2;
13269    * }
13270    *
13271    * Foo.prototype.c = 3;
13272    *
13273    * _.values(new Foo);
13274    * // => [1, 2] (iteration order is not guaranteed)
13275    *
13276    * _.values('hi');
13277    * // => ['h', 'i']
13278    */
13279   function values(object) {
13280     return baseValues(object, keys(object));
13281   }
13282
13283   /*------------------------------------------------------------------------*/
13284
13285   /**
13286    * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
13287    * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
13288    *
13289    * @static
13290    * @memberOf _
13291    * @category String
13292    * @param {string} [string=''] The string to escape.
13293    * @returns {string} Returns the escaped string.
13294    * @example
13295    *
13296    * _.escapeRegExp('[lodash](https://lodash.com/)');
13297    * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
13298    */
13299   function escapeRegExp(string) {
13300     string = baseToString(string);
13301     return (string && reHasRegExpChars.test(string))
13302       ? string.replace(reRegExpChars, '\\$&')
13303       : string;
13304   }
13305
13306   /*------------------------------------------------------------------------*/
13307
13308   /**
13309    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13310    * and arguments of the created function. If `func` is a property name the
13311    * created callback returns the property value for a given element. If `func`
13312    * is an object the created callback returns `true` for elements that contain
13313    * the equivalent object properties, otherwise it returns `false`.
13314    *
13315    * @static
13316    * @memberOf _
13317    * @alias iteratee
13318    * @category Utility
13319    * @param {*} [func=_.identity] The value to convert to a callback.
13320    * @param {*} [thisArg] The `this` binding of `func`.
13321    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
13322    * @returns {Function} Returns the callback.
13323    * @example
13324    *
13325    * var users = [
13326    *   { 'user': 'barney', 'age': 36 },
13327    *   { 'user': 'fred',   'age': 40 }
13328    * ];
13329    *
13330    * // wrap to create custom callback shorthands
13331    * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
13332    *   var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
13333    *   if (!match) {
13334    *     return callback(func, thisArg);
13335    *   }
13336    *   return function(object) {
13337    *     return match[2] == 'gt'
13338    *       ? object[match[1]] > match[3]
13339    *       : object[match[1]] < match[3];
13340    *   };
13341    * });
13342    *
13343    * _.filter(users, 'age__gt36');
13344    * // => [{ 'user': 'fred', 'age': 40 }]
13345    */
13346   function callback(func, thisArg, guard) {
13347     if (guard && isIterateeCall(func, thisArg, guard)) {
13348       thisArg = null;
13349     }
13350     return isObjectLike(func)
13351       ? matches(func)
13352       : baseCallback(func, thisArg);
13353   }
13354
13355   /**
13356    * Creates a function that returns `value`.
13357    *
13358    * @static
13359    * @memberOf _
13360    * @category Utility
13361    * @param {*} value The value to return from the new function.
13362    * @returns {Function} Returns the new function.
13363    * @example
13364    *
13365    * var object = { 'user': 'fred' };
13366    * var getter = _.constant(object);
13367    *
13368    * getter() === object;
13369    * // => true
13370    */
13371   function constant(value) {
13372     return function() {
13373       return value;
13374     };
13375   }
13376
13377   /**
13378    * This method returns the first argument provided to it.
13379    *
13380    * @static
13381    * @memberOf _
13382    * @category Utility
13383    * @param {*} value Any value.
13384    * @returns {*} Returns `value`.
13385    * @example
13386    *
13387    * var object = { 'user': 'fred' };
13388    *
13389    * _.identity(object) === object;
13390    * // => true
13391    */
13392   function identity(value) {
13393     return value;
13394   }
13395
13396   /**
13397    * Creates a function that performs a deep comparison between a given object
13398    * and `source`, returning `true` if the given object has equivalent property
13399    * values, else `false`.
13400    *
13401    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
13402    * numbers, `Object` objects, regexes, and strings. Objects are compared by
13403    * their own, not inherited, enumerable properties. For comparing a single
13404    * own or inherited property value see `_.matchesProperty`.
13405    *
13406    * @static
13407    * @memberOf _
13408    * @category Utility
13409    * @param {Object} source The object of property values to match.
13410    * @returns {Function} Returns the new function.
13411    * @example
13412    *
13413    * var users = [
13414    *   { 'user': 'barney', 'age': 36, 'active': true },
13415    *   { 'user': 'fred',   'age': 40, 'active': false }
13416    * ];
13417    *
13418    * _.filter(users, _.matches({ 'age': 40, 'active': false }));
13419    * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
13420    */
13421   function matches(source) {
13422     return baseMatches(baseClone(source, true));
13423   }
13424
13425   /**
13426    * Adds all own enumerable function properties of a source object to the
13427    * destination object. If `object` is a function then methods are added to
13428    * its prototype as well.
13429    *
13430    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
13431    * avoid conflicts caused by modifying the original.
13432    *
13433    * @static
13434    * @memberOf _
13435    * @category Utility
13436    * @param {Function|Object} [object=lodash] The destination object.
13437    * @param {Object} source The object of functions to add.
13438    * @param {Object} [options] The options object.
13439    * @param {boolean} [options.chain=true] Specify whether the functions added
13440    *  are chainable.
13441    * @returns {Function|Object} Returns `object`.
13442    * @example
13443    *
13444    * function vowels(string) {
13445    *   return _.filter(string, function(v) {
13446    *     return /[aeiou]/i.test(v);
13447    *   });
13448    * }
13449    *
13450    * _.mixin({ 'vowels': vowels });
13451    * _.vowels('fred');
13452    * // => ['e']
13453    *
13454    * _('fred').vowels().value();
13455    * // => ['e']
13456    *
13457    * _.mixin({ 'vowels': vowels }, { 'chain': false });
13458    * _('fred').vowels();
13459    * // => ['e']
13460    */
13461   function mixin(object, source, options) {
13462     if (options == null) {
13463       var isObj = isObject(source),
13464           props = isObj ? keys(source) : null,
13465           methodNames = (props && props.length) ? baseFunctions(source, props) : null;
13466
13467       if (!(methodNames ? methodNames.length : isObj)) {
13468         methodNames = false;
13469         options = source;
13470         source = object;
13471         object = this;
13472       }
13473     }
13474     if (!methodNames) {
13475       methodNames = baseFunctions(source, keys(source));
13476     }
13477     var chain = true,
13478         index = -1,
13479         isFunc = isFunction(object),
13480         length = methodNames.length;
13481
13482     if (options === false) {
13483       chain = false;
13484     } else if (isObject(options) && 'chain' in options) {
13485       chain = options.chain;
13486     }
13487     while (++index < length) {
13488       var methodName = methodNames[index],
13489           func = source[methodName];
13490
13491       object[methodName] = func;
13492       if (isFunc) {
13493         object.prototype[methodName] = (function(func) {
13494           return function() {
13495             var chainAll = this.__chain__;
13496             if (chain || chainAll) {
13497               var result = object(this.__wrapped__),
13498                   actions = result.__actions__ = arrayCopy(this.__actions__);
13499
13500               actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
13501               result.__chain__ = chainAll;
13502               return result;
13503             }
13504             var args = [this.value()];
13505             push.apply(args, arguments);
13506             return func.apply(object, args);
13507           };
13508         }(func));
13509       }
13510     }
13511     return object;
13512   }
13513
13514   /**
13515    * A no-operation function that returns `undefined` regardless of the
13516    * arguments it receives.
13517    *
13518    * @static
13519    * @memberOf _
13520    * @category Utility
13521    * @example
13522    *
13523    * var object = { 'user': 'fred' };
13524    *
13525    * _.noop(object) === undefined;
13526    * // => true
13527    */
13528   function noop() {
13529     // No operation performed.
13530   }
13531
13532   /**
13533    * Creates a function that returns the property value at `path` on a
13534    * given object.
13535    *
13536    * @static
13537    * @memberOf _
13538    * @category Utility
13539    * @param {Array|string} path The path of the property to get.
13540    * @returns {Function} Returns the new function.
13541    * @example
13542    *
13543    * var objects = [
13544    *   { 'a': { 'b': { 'c': 2 } } },
13545    *   { 'a': { 'b': { 'c': 1 } } }
13546    * ];
13547    *
13548    * _.map(objects, _.property('a.b.c'));
13549    * // => [2, 1]
13550    *
13551    * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
13552    * // => [1, 2]
13553    */
13554   function property(path) {
13555     return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
13556   }
13557
13558   /*------------------------------------------------------------------------*/
13559
13560   // Ensure wrappers are instances of `baseLodash`.
13561   lodash.prototype = baseLodash.prototype;
13562
13563   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
13564   LodashWrapper.prototype.constructor = LodashWrapper;
13565
13566   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
13567   LazyWrapper.prototype.constructor = LazyWrapper;
13568
13569   // Add functions to the `Set` cache.
13570   SetCache.prototype.push = cachePush;
13571
13572   // Add functions that return wrapped values when chaining.
13573   lodash.assign = assign;
13574   lodash.bind = bind;
13575   lodash.callback = callback;
13576   lodash.chain = chain;
13577   lodash.chunk = chunk;
13578   lodash.compact = compact;
13579   lodash.constant = constant;
13580   lodash.debounce = debounce;
13581   lodash.difference = difference;
13582   lodash.filter = filter;
13583   lodash.flatten = flatten;
13584   lodash.forEach = forEach;
13585   lodash.forOwn = forOwn;
13586   lodash.groupBy = groupBy;
13587   lodash.intersection = intersection;
13588   lodash.keys = keys;
13589   lodash.keysIn = keysIn;
13590   lodash.map = map;
13591   lodash.matches = matches;
13592   lodash.merge = merge;
13593   lodash.mixin = mixin;
13594   lodash.omit = omit;
13595   lodash.pairs = pairs;
13596   lodash.pick = pick;
13597   lodash.pluck = pluck;
13598   lodash.property = property;
13599   lodash.reject = reject;
13600   lodash.restParam = restParam;
13601   lodash.tap = tap;
13602   lodash.throttle = throttle;
13603   lodash.thru = thru;
13604   lodash.toPlainObject = toPlainObject;
13605   lodash.union = union;
13606   lodash.uniq = uniq;
13607   lodash.values = values;
13608   lodash.without = without;
13609
13610   // Add aliases.
13611   lodash.collect = map;
13612   lodash.each = forEach;
13613   lodash.extend = assign;
13614   lodash.iteratee = callback;
13615   lodash.select = filter;
13616   lodash.unique = uniq;
13617
13618   // Add functions to `lodash.prototype`.
13619   mixin(lodash, lodash);
13620
13621   /*------------------------------------------------------------------------*/
13622
13623   // Add functions that return unwrapped values when chaining.
13624   lodash.clone = clone;
13625   lodash.cloneDeep = cloneDeep;
13626   lodash.escapeRegExp = escapeRegExp;
13627   lodash.every = every;
13628   lodash.find = find;
13629   lodash.first = first;
13630   lodash.identity = identity;
13631   lodash.includes = includes;
13632   lodash.indexOf = indexOf;
13633   lodash.isArguments = isArguments;
13634   lodash.isArray = isArray;
13635   lodash.isEmpty = isEmpty;
13636   lodash.isEqual = isEqual;
13637   lodash.isFunction = isFunction;
13638   lodash.isNative = isNative;
13639   lodash.isObject = isObject;
13640   lodash.isPlainObject = isPlainObject;
13641   lodash.isString = isString;
13642   lodash.isTypedArray = isTypedArray;
13643   lodash.last = last;
13644   lodash.noop = noop;
13645   lodash.now = now;
13646   lodash.reduce = reduce;
13647   lodash.some = some;
13648
13649   // Add aliases.
13650   lodash.all = every;
13651   lodash.any = some;
13652   lodash.contains = includes;
13653   lodash.eq = isEqual;
13654   lodash.detect = find;
13655   lodash.foldl = reduce;
13656   lodash.head = first;
13657   lodash.include = includes;
13658   lodash.inject = reduce;
13659
13660   mixin(lodash, (function() {
13661     var source = {};
13662     baseForOwn(lodash, function(func, methodName) {
13663       if (!lodash.prototype[methodName]) {
13664         source[methodName] = func;
13665       }
13666     });
13667     return source;
13668   }()), false);
13669
13670   /*------------------------------------------------------------------------*/
13671
13672   lodash.prototype.sample = function(n) {
13673     if (!this.__chain__ && n == null) {
13674       return sample(this.value());
13675     }
13676     return this.thru(function(value) {
13677       return sample(value, n);
13678     });
13679   };
13680
13681   /*------------------------------------------------------------------------*/
13682
13683   /**
13684    * The semantic version number.
13685    *
13686    * @static
13687    * @memberOf _
13688    * @type string
13689    */
13690   lodash.VERSION = VERSION;
13691
13692   // Assign default placeholders.
13693   bind.placeholder = lodash;
13694
13695   // Add `LazyWrapper` methods that accept an `iteratee` value.
13696   arrayEach(['dropWhile', 'filter', 'map', 'takeWhile'], function(methodName, type) {
13697     var isFilter = type != LAZY_MAP_FLAG,
13698         isDropWhile = type == LAZY_DROP_WHILE_FLAG;
13699
13700     LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
13701       var filtered = this.__filtered__,
13702           result = (filtered && isDropWhile) ? new LazyWrapper(this) : this.clone(),
13703           iteratees = result.__iteratees__ || (result.__iteratees__ = []);
13704
13705       iteratees.push({
13706         'done': false,
13707         'count': 0,
13708         'index': 0,
13709         'iteratee': getCallback(iteratee, thisArg, 1),
13710         'limit': -1,
13711         'type': type
13712       });
13713
13714       result.__filtered__ = filtered || isFilter;
13715       return result;
13716     };
13717   });
13718
13719   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
13720   arrayEach(['drop', 'take'], function(methodName, index) {
13721     var whileName = methodName + 'While';
13722
13723     LazyWrapper.prototype[methodName] = function(n) {
13724       var filtered = this.__filtered__,
13725           result = (filtered && !index) ? this.dropWhile() : this.clone();
13726
13727       n = n == null ? 1 : nativeMax(floor(n) || 0, 0);
13728       if (filtered) {
13729         if (index) {
13730           result.__takeCount__ = nativeMin(result.__takeCount__, n);
13731         } else {
13732           last(result.__iteratees__).limit = n;
13733         }
13734       } else {
13735         var views = result.__views__ || (result.__views__ = []);
13736         views.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
13737       }
13738       return result;
13739     };
13740
13741     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
13742       return this.reverse()[methodName](n).reverse();
13743     };
13744
13745     LazyWrapper.prototype[methodName + 'RightWhile'] = function(predicate, thisArg) {
13746       return this.reverse()[whileName](predicate, thisArg).reverse();
13747     };
13748   });
13749
13750   // Add `LazyWrapper` methods for `_.first` and `_.last`.
13751   arrayEach(['first', 'last'], function(methodName, index) {
13752     var takeName = 'take' + (index ? 'Right' : '');
13753
13754     LazyWrapper.prototype[methodName] = function() {
13755       return this[takeName](1).value()[0];
13756     };
13757   });
13758
13759   // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
13760   arrayEach(['initial', 'rest'], function(methodName, index) {
13761     var dropName = 'drop' + (index ? '' : 'Right');
13762
13763     LazyWrapper.prototype[methodName] = function() {
13764       return this[dropName](1);
13765     };
13766   });
13767
13768   // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
13769   arrayEach(['pluck', 'where'], function(methodName, index) {
13770     var operationName = index ? 'filter' : 'map',
13771         createCallback = index ? baseMatches : property;
13772
13773     LazyWrapper.prototype[methodName] = function(value) {
13774       return this[operationName](createCallback(value));
13775     };
13776   });
13777
13778   LazyWrapper.prototype.compact = function() {
13779     return this.filter(identity);
13780   };
13781
13782   LazyWrapper.prototype.reject = function(predicate, thisArg) {
13783     predicate = getCallback(predicate, thisArg, 1);
13784     return this.filter(function(value) {
13785       return !predicate(value);
13786     });
13787   };
13788
13789   LazyWrapper.prototype.slice = function(start, end) {
13790     start = start == null ? 0 : (+start || 0);
13791
13792     var result = this;
13793     if (start < 0) {
13794       result = this.takeRight(-start);
13795     } else if (start) {
13796       result = this.drop(start);
13797     }
13798     if (end !== undefined) {
13799       end = (+end || 0);
13800       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
13801     }
13802     return result;
13803   };
13804
13805   LazyWrapper.prototype.toArray = function() {
13806     return this.drop(0);
13807   };
13808
13809   // Add `LazyWrapper` methods to `lodash.prototype`.
13810   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13811     var lodashFunc = lodash[methodName];
13812     if (!lodashFunc) {
13813       return;
13814     }
13815     var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
13816         retUnwrapped = /^(?:first|last)$/.test(methodName);
13817
13818     lodash.prototype[methodName] = function() {
13819       var args = arguments,
13820           chainAll = this.__chain__,
13821           value = this.__wrapped__,
13822           isHybrid = !!this.__actions__.length,
13823           isLazy = value instanceof LazyWrapper,
13824           iteratee = args[0],
13825           useLazy = isLazy || isArray(value);
13826
13827       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
13828         // avoid lazy use if the iteratee has a "length" value other than `1`
13829         isLazy = useLazy = false;
13830       }
13831       var onlyLazy = isLazy && !isHybrid;
13832       if (retUnwrapped && !chainAll) {
13833         return onlyLazy
13834           ? func.call(value)
13835           : lodashFunc.call(lodash, this.value());
13836       }
13837       var interceptor = function(value) {
13838         var otherArgs = [value];
13839         push.apply(otherArgs, args);
13840         return lodashFunc.apply(lodash, otherArgs);
13841       };
13842       if (useLazy) {
13843         var wrapper = onlyLazy ? value : new LazyWrapper(this),
13844             result = func.apply(wrapper, args);
13845
13846         if (!retUnwrapped && (isHybrid || result.__actions__)) {
13847           var actions = result.__actions__ || (result.__actions__ = []);
13848           actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash });
13849         }
13850         return new LodashWrapper(result, chainAll);
13851       }
13852       return this.thru(interceptor);
13853     };
13854   });
13855
13856   // Add `Array` and `String` methods to `lodash.prototype`.
13857   arrayEach(['concat', 'join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
13858     var protoFunc = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
13859         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
13860         fixObjects = !support.spliceObjects && /^(?:pop|shift|splice)$/.test(methodName),
13861         retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
13862
13863     // Avoid array-like object bugs with `Array#shift` and `Array#splice` in
13864     // IE < 9, Firefox < 10, and RingoJS.
13865     var func = !fixObjects ? protoFunc : function() {
13866       var result = protoFunc.apply(this, arguments);
13867       if (this.length === 0) {
13868         delete this[0];
13869       }
13870       return result;
13871     };
13872
13873     lodash.prototype[methodName] = function() {
13874       var args = arguments;
13875       if (retUnwrapped && !this.__chain__) {
13876         return func.apply(this.value(), args);
13877       }
13878       return this[chainName](function(value) {
13879         return func.apply(value, args);
13880       });
13881     };
13882   });
13883
13884   // Map minified function names to their real names.
13885   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13886     var lodashFunc = lodash[methodName];
13887     if (lodashFunc) {
13888       var key = lodashFunc.name,
13889           names = realNames[key] || (realNames[key] = []);
13890
13891       names.push({ 'name': methodName, 'func': lodashFunc });
13892     }
13893   });
13894
13895   realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': null }];
13896
13897   // Add functions to the lazy wrapper.
13898   LazyWrapper.prototype.clone = lazyClone;
13899   LazyWrapper.prototype.reverse = lazyReverse;
13900   LazyWrapper.prototype.value = lazyValue;
13901
13902   // Add chaining functions to the `lodash` wrapper.
13903   lodash.prototype.chain = wrapperChain;
13904   lodash.prototype.commit = wrapperCommit;
13905   lodash.prototype.plant = wrapperPlant;
13906   lodash.prototype.reverse = wrapperReverse;
13907   lodash.prototype.toString = wrapperToString;
13908   lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
13909
13910   // Add function aliases to the `lodash` wrapper.
13911   lodash.prototype.collect = lodash.prototype.map;
13912   lodash.prototype.head = lodash.prototype.first;
13913   lodash.prototype.select = lodash.prototype.filter;
13914   lodash.prototype.tail = lodash.prototype.rest;
13915
13916   /*--------------------------------------------------------------------------*/
13917
13918   if (freeExports && freeModule) {
13919     // Export for Node.js or RingoJS.
13920     if (moduleExports) {
13921       (freeModule.exports = lodash)._ = lodash;
13922     }
13923   }
13924   else {
13925     // Export for a browser or Rhino.
13926     root._ = lodash;
13927   }
13928 }.call(this));
13929 (function(e){if("function"==typeof bootstrap)bootstrap("osmauth",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeOsmAuth=e}else"undefined"!=typeof window?window.osmAuth=e():global.osmAuth=e()})(function(){var define,ses,bootstrap,module,exports;
13930 return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
13931 'use strict';
13932
13933 var ohauth = require('ohauth'),
13934     xtend = require('xtend'),
13935     store = require('store');
13936
13937 // # osm-auth
13938 //
13939 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
13940 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
13941 // does not support custom headers, which this uses everywhere.
13942 module.exports = function(o) {
13943
13944     var oauth = {};
13945
13946     // authenticated users will also have a request token secret, but it's
13947     // not used in transactions with the server
13948     oauth.authenticated = function() {
13949         return !!(token('oauth_token') && token('oauth_token_secret'));
13950     };
13951
13952     oauth.logout = function() {
13953         token('oauth_token', '');
13954         token('oauth_token_secret', '');
13955         token('oauth_request_token_secret', '');
13956         return oauth;
13957     };
13958
13959     // TODO: detect lack of click event
13960     oauth.authenticate = function(callback) {
13961         if (oauth.authenticated()) return callback();
13962
13963         oauth.logout();
13964
13965         // ## Getting a request token
13966         var params = timenonce(getAuth(o)),
13967             url = o.url + '/oauth/request_token',
13968             timer;
13969
13970         params.oauth_signature = ohauth.signature(
13971             o.oauth_secret, '',
13972             ohauth.baseString('POST', url, params));
13973
13974         if (!o.singlepage) {
13975             // Create a 600x550 popup window in the center of the screen
13976             var w = 600, h = 550,
13977                 settings = [
13978                     ['width', w], ['height', h],
13979                     ['left', screen.width / 2 - w / 2],
13980                     ['top', screen.height / 2 - h / 2]].map(function(x) {
13981                         return x.join('=');
13982                     }).join(','),
13983                 popup = window.open('about:blank', 'oauth_window', settings);
13984
13985
13986             timer = setInterval(function() {
13987                 if (popup.closed) {
13988                     o.done();
13989                     clearInterval(timer);
13990                     callback('not authenticated', null);
13991                 }
13992             }, 100);
13993         }
13994
13995         // Request a request token. When this is complete, the popup
13996         // window is redirected to OSM's authorization page.
13997         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
13998         o.loading();
13999
14000         function reqTokenDone(err, xhr) {
14001             o.done();
14002             if (err) {
14003                 if (timer) clearInterval(timer);
14004                 return callback(err);
14005             }
14006             var resp = ohauth.stringQs(xhr.response);
14007             token('oauth_request_token_secret', resp.oauth_token_secret);
14008             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
14009                 oauth_token: resp.oauth_token,
14010                 oauth_callback: location.href.replace('index.html', '')
14011                     .replace(/#.*/, '') + o.landing
14012             });
14013
14014             if (o.singlepage) {
14015                 location.href = authorize_url;
14016             } else {
14017                 popup.location = authorize_url;
14018             }
14019         }
14020
14021         // Called by a function in a landing page, in the popup window. The
14022         // window closes itself.
14023         window.authComplete = function(token) {
14024             if (timer) clearInterval(timer);
14025             var oauth_token = ohauth.stringQs(token.split('?')[1]);
14026             get_access_token(oauth_token.oauth_token);
14027             delete window.authComplete;
14028         };
14029
14030         // ## Getting an request token
14031         //
14032         // At this point we have an `oauth_token`, brought in from a function
14033         // call on a landing page popup.
14034         function get_access_token(oauth_token) {
14035             var url = o.url + '/oauth/access_token',
14036                 params = timenonce(getAuth(o)),
14037                 request_token_secret = token('oauth_request_token_secret');
14038             params.oauth_token = oauth_token;
14039             params.oauth_signature = ohauth.signature(
14040                 o.oauth_secret,
14041                 request_token_secret,
14042                 ohauth.baseString('POST', url, params));
14043
14044             // ## Getting an access token
14045             //
14046             // The final token required for authentication. At this point
14047             // we have a `request token secret`
14048             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14049             o.loading();
14050         }
14051
14052         function accessTokenDone(err, xhr) {
14053             o.done();
14054             if (timer) clearInterval(timer);
14055             if (err) return callback(err);
14056             var access_token = ohauth.stringQs(xhr.response);
14057             token('oauth_token', access_token.oauth_token);
14058             token('oauth_token_secret', access_token.oauth_token_secret);
14059             callback(null, oauth);
14060         }
14061     };
14062
14063     oauth.bootstrapToken = function(oauth_token, callback) {
14064         // ## Getting an request token
14065         // At this point we have an `oauth_token`, brought in from a function
14066         // call on a landing page popup.
14067         function get_access_token(oauth_token) {
14068             var url = o.url + '/oauth/access_token',
14069                 params = timenonce(getAuth(o)),
14070                 request_token_secret = token('oauth_request_token_secret');
14071             params.oauth_token = oauth_token;
14072             params.oauth_signature = ohauth.signature(
14073                 o.oauth_secret,
14074                 request_token_secret,
14075                 ohauth.baseString('POST', url, params));
14076
14077             // ## Getting an access token
14078             // The final token required for authentication. At this point
14079             // we have a `request token secret`
14080             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14081             o.loading();
14082         }
14083
14084         function accessTokenDone(err, xhr) {
14085             o.done();
14086             if (err) return callback(err);
14087             var access_token = ohauth.stringQs(xhr.response);
14088             token('oauth_token', access_token.oauth_token);
14089             token('oauth_token_secret', access_token.oauth_token_secret);
14090             callback(null, oauth);
14091         }
14092
14093         get_access_token(oauth_token);
14094     };
14095
14096     // # xhr
14097     //
14098     // A single XMLHttpRequest wrapper that does authenticated calls if the
14099     // user has logged in.
14100     oauth.xhr = function(options, callback) {
14101         if (!oauth.authenticated()) {
14102             if (o.auto) return oauth.authenticate(run);
14103             else return callback('not authenticated', null);
14104         } else return run();
14105
14106         function run() {
14107             var params = timenonce(getAuth(o)),
14108                 url = o.url + options.path,
14109                 oauth_token_secret = token('oauth_token_secret');
14110
14111             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
14112             if ((!options.options || !options.options.header ||
14113                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
14114                 options.content) {
14115                 params = xtend(params, ohauth.stringQs(options.content));
14116             }
14117
14118             params.oauth_token = token('oauth_token');
14119             params.oauth_signature = ohauth.signature(
14120                 o.oauth_secret,
14121                 oauth_token_secret,
14122                 ohauth.baseString(options.method, url, params));
14123
14124             ohauth.xhr(options.method,
14125                 url, params, options.content, options.options, done);
14126         }
14127
14128         function done(err, xhr) {
14129             if (err) return callback(err);
14130             else if (xhr.responseXML) return callback(err, xhr.responseXML);
14131             else return callback(err, xhr.response);
14132         }
14133     };
14134
14135     // pre-authorize this object, if we can just get a token and token_secret
14136     // from the start
14137     oauth.preauth = function(c) {
14138         if (!c) return;
14139         if (c.oauth_token) token('oauth_token', c.oauth_token);
14140         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
14141         return oauth;
14142     };
14143
14144     oauth.options = function(_) {
14145         if (!arguments.length) return o;
14146
14147         o = _;
14148
14149         o.url = o.url || 'http://www.openstreetmap.org';
14150         o.landing = o.landing || 'land.html';
14151
14152         o.singlepage = o.singlepage || false;
14153
14154         // Optional loading and loading-done functions for nice UI feedback.
14155         // by default, no-ops
14156         o.loading = o.loading || function() {};
14157         o.done = o.done || function() {};
14158
14159         return oauth.preauth(o);
14160     };
14161
14162     // 'stamp' an authentication object from `getAuth()`
14163     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
14164     // and timestamp
14165     function timenonce(o) {
14166         o.oauth_timestamp = ohauth.timestamp();
14167         o.oauth_nonce = ohauth.nonce();
14168         return o;
14169     }
14170
14171     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
14172     // can be used with multiple APIs and the keys in `localStorage`
14173     // will not clash
14174     var token;
14175
14176     if (store.enabled) {
14177         token = function (x, y) {
14178             if (arguments.length === 1) return store.get(o.url + x);
14179             else if (arguments.length === 2) return store.set(o.url + x, y);
14180         };
14181     } else {
14182         var storage = {};
14183         token = function (x, y) {
14184             if (arguments.length === 1) return storage[o.url + x];
14185             else if (arguments.length === 2) return storage[o.url + x] = y;
14186         };
14187     }
14188
14189     // Get an authentication object. If you just add and remove properties
14190     // from a single object, you'll need to use `delete` to make sure that
14191     // it doesn't contain undesired properties for authentication
14192     function getAuth(o) {
14193         return {
14194             oauth_consumer_key: o.oauth_consumer_key,
14195             oauth_signature_method: "HMAC-SHA1"
14196         };
14197     }
14198
14199     // potentially pre-authorize
14200     oauth.options(o);
14201
14202     return oauth;
14203 };
14204
14205 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
14206 (function(global){;(function(win){
14207         var store = {},
14208                 doc = win.document,
14209                 localStorageName = 'localStorage',
14210                 storage
14211
14212         store.disabled = false
14213         store.set = function(key, value) {}
14214         store.get = function(key) {}
14215         store.remove = function(key) {}
14216         store.clear = function() {}
14217         store.transact = function(key, defaultVal, transactionFn) {
14218                 var val = store.get(key)
14219                 if (transactionFn == null) {
14220                         transactionFn = defaultVal
14221                         defaultVal = null
14222                 }
14223                 if (typeof val == 'undefined') { val = defaultVal || {} }
14224                 transactionFn(val)
14225                 store.set(key, val)
14226         }
14227         store.getAll = function() {}
14228         store.forEach = function() {}
14229
14230         store.serialize = function(value) {
14231                 return JSON.stringify(value)
14232         }
14233         store.deserialize = function(value) {
14234                 if (typeof value != 'string') { return undefined }
14235                 try { return JSON.parse(value) }
14236                 catch(e) { return value || undefined }
14237         }
14238
14239         // Functions to encapsulate questionable FireFox 3.6.13 behavior
14240         // when about.config::dom.storage.enabled === false
14241         // See https://github.com/marcuswestin/store.js/issues#issue/13
14242         function isLocalStorageNameSupported() {
14243                 try { return (localStorageName in win && win[localStorageName]) }
14244                 catch(err) { return false }
14245         }
14246
14247         if (isLocalStorageNameSupported()) {
14248                 storage = win[localStorageName]
14249                 store.set = function(key, val) {
14250                         if (val === undefined) { return store.remove(key) }
14251                         storage.setItem(key, store.serialize(val))
14252                         return val
14253                 }
14254                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
14255                 store.remove = function(key) { storage.removeItem(key) }
14256                 store.clear = function() { storage.clear() }
14257                 store.getAll = function() {
14258                         var ret = {}
14259                         store.forEach(function(key, val) {
14260                                 ret[key] = val
14261                         })
14262                         return ret
14263                 }
14264                 store.forEach = function(callback) {
14265                         for (var i=0; i<storage.length; i++) {
14266                                 var key = storage.key(i)
14267                                 callback(key, store.get(key))
14268                         }
14269                 }
14270         } else if (doc.documentElement.addBehavior) {
14271                 var storageOwner,
14272                         storageContainer
14273                 // Since #userData storage applies only to specific paths, we need to
14274                 // somehow link our data to a specific path.  We choose /favicon.ico
14275                 // as a pretty safe option, since all browsers already make a request to
14276                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
14277                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
14278                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
14279                 // since the iframe access rules appear to allow direct access and
14280                 // manipulation of the document element, even for a 404 page.  This
14281                 // document can be used instead of the current document (which would
14282                 // have been limited to the current path) to perform #userData storage.
14283                 try {
14284                         storageContainer = new ActiveXObject('htmlfile')
14285                         storageContainer.open()
14286                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
14287                         storageContainer.close()
14288                         storageOwner = storageContainer.w.frames[0].document
14289                         storage = storageOwner.createElement('div')
14290                 } catch(e) {
14291                         // somehow ActiveXObject instantiation failed (perhaps some special
14292                         // security settings or otherwse), fall back to per-path storage
14293                         storage = doc.createElement('div')
14294                         storageOwner = doc.body
14295                 }
14296                 function withIEStorage(storeFunction) {
14297                         return function() {
14298                                 var args = Array.prototype.slice.call(arguments, 0)
14299                                 args.unshift(storage)
14300                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
14301                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
14302                                 storageOwner.appendChild(storage)
14303                                 storage.addBehavior('#default#userData')
14304                                 storage.load(localStorageName)
14305                                 var result = storeFunction.apply(store, args)
14306                                 storageOwner.removeChild(storage)
14307                                 return result
14308                         }
14309                 }
14310
14311                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
14312                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
14313                 function ieKeyFix(key) {
14314                         return key.replace(forbiddenCharsRegex, '___')
14315                 }
14316                 store.set = withIEStorage(function(storage, key, val) {
14317                         key = ieKeyFix(key)
14318                         if (val === undefined) { return store.remove(key) }
14319                         storage.setAttribute(key, store.serialize(val))
14320                         storage.save(localStorageName)
14321                         return val
14322                 })
14323                 store.get = withIEStorage(function(storage, key) {
14324                         key = ieKeyFix(key)
14325                         return store.deserialize(storage.getAttribute(key))
14326                 })
14327                 store.remove = withIEStorage(function(storage, key) {
14328                         key = ieKeyFix(key)
14329                         storage.removeAttribute(key)
14330                         storage.save(localStorageName)
14331                 })
14332                 store.clear = withIEStorage(function(storage) {
14333                         var attributes = storage.XMLDocument.documentElement.attributes
14334                         storage.load(localStorageName)
14335                         for (var i=0, attr; attr=attributes[i]; i++) {
14336                                 storage.removeAttribute(attr.name)
14337                         }
14338                         storage.save(localStorageName)
14339                 })
14340                 store.getAll = function(storage) {
14341                         var ret = {}
14342                         store.forEach(function(key, val) {
14343                                 ret[key] = val
14344                         })
14345                         return ret
14346                 }
14347                 store.forEach = withIEStorage(function(storage, callback) {
14348                         var attributes = storage.XMLDocument.documentElement.attributes
14349                         for (var i=0, attr; attr=attributes[i]; ++i) {
14350                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
14351                         }
14352                 })
14353         }
14354
14355         try {
14356                 var testKey = '__storejs__'
14357                 store.set(testKey, testKey)
14358                 if (store.get(testKey) != testKey) { store.disabled = true }
14359                 store.remove(testKey)
14360         } catch(e) {
14361                 store.disabled = true
14362         }
14363         store.enabled = !store.disabled
14364
14365         if (typeof module != 'undefined' && module.exports) { module.exports = store }
14366         else if (typeof define === 'function' && define.amd) { define(store) }
14367         else { win.store = store }
14368
14369 })(this.window || global);
14370
14371 })(window)
14372 },{}],5:[function(require,module,exports){
14373 module.exports = hasKeys
14374
14375 function hasKeys(source) {
14376     return source !== null &&
14377         (typeof source === "object" ||
14378         typeof source === "function")
14379 }
14380
14381 },{}],4:[function(require,module,exports){
14382 var Keys = require("object-keys")
14383 var hasKeys = require("./has-keys")
14384
14385 module.exports = extend
14386
14387 function extend() {
14388     var target = {}
14389
14390     for (var i = 0; i < arguments.length; i++) {
14391         var source = arguments[i]
14392
14393         if (!hasKeys(source)) {
14394             continue
14395         }
14396
14397         var keys = Keys(source)
14398
14399         for (var j = 0; j < keys.length; j++) {
14400             var name = keys[j]
14401             target[name] = source[name]
14402         }
14403     }
14404
14405     return target
14406 }
14407
14408 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
14409 (function(global){/**
14410  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
14411  *
14412  * @class Hashes
14413  * @author Tomas Aparicio <tomas@rijndael-project.com>
14414  * @license New BSD (see LICENSE file)
14415  * @version 1.0.4
14416  *
14417  * Algorithms specification:
14418  *
14419  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
14420  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
14421  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14422  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14423  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14424  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
14425  *
14426  */
14427 (function(){
14428   var Hashes;
14429
14430   // private helper methods
14431   function utf8Encode(str) {
14432     var  x, y, output = '', i = -1, l;
14433
14434     if (str && str.length) {
14435       l = str.length;
14436       while ((i+=1) < l) {
14437         /* Decode utf-16 surrogate pairs */
14438         x = str.charCodeAt(i);
14439         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
14440         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
14441             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
14442             i += 1;
14443         }
14444         /* Encode output as utf-8 */
14445         if (x <= 0x7F) {
14446             output += String.fromCharCode(x);
14447         } else if (x <= 0x7FF) {
14448             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
14449                         0x80 | ( x & 0x3F));
14450         } else if (x <= 0xFFFF) {
14451             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
14452                         0x80 | ((x >>> 6 ) & 0x3F),
14453                         0x80 | ( x & 0x3F));
14454         } else if (x <= 0x1FFFFF) {
14455             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
14456                         0x80 | ((x >>> 12) & 0x3F),
14457                         0x80 | ((x >>> 6 ) & 0x3F),
14458                         0x80 | ( x & 0x3F));
14459         }
14460       }
14461     }
14462     return output;
14463   }
14464
14465   function utf8Decode(str) {
14466     var i, ac, c1, c2, c3, arr = [], l;
14467     i = ac = c1 = c2 = c3 = 0;
14468
14469     if (str && str.length) {
14470       l = str.length;
14471       str += '';
14472
14473       while (i < l) {
14474           c1 = str.charCodeAt(i);
14475           ac += 1;
14476           if (c1 < 128) {
14477               arr[ac] = String.fromCharCode(c1);
14478               i+=1;
14479           } else if (c1 > 191 && c1 < 224) {
14480               c2 = str.charCodeAt(i + 1);
14481               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
14482               i += 2;
14483           } else {
14484               c2 = str.charCodeAt(i + 1);
14485               c3 = str.charCodeAt(i + 2);
14486               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
14487               i += 3;
14488           }
14489       }
14490     }
14491     return arr.join('');
14492   }
14493
14494   /**
14495    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
14496    * to work around bugs in some JS interpreters.
14497    */
14498   function safe_add(x, y) {
14499     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
14500         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
14501     return (msw << 16) | (lsw & 0xFFFF);
14502   }
14503
14504   /**
14505    * Bitwise rotate a 32-bit number to the left.
14506    */
14507   function bit_rol(num, cnt) {
14508     return (num << cnt) | (num >>> (32 - cnt));
14509   }
14510
14511   /**
14512    * Convert a raw string to a hex string
14513    */
14514   function rstr2hex(input, hexcase) {
14515     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
14516         output = '', x, i = 0, l = input.length;
14517     for (; i < l; i+=1) {
14518       x = input.charCodeAt(i);
14519       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
14520     }
14521     return output;
14522   }
14523
14524   /**
14525    * Encode a string as utf-16
14526    */
14527   function str2rstr_utf16le(input) {
14528     var i, l = input.length, output = '';
14529     for (i = 0; i < l; i+=1) {
14530       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
14531     }
14532     return output;
14533   }
14534
14535   function str2rstr_utf16be(input) {
14536     var i, l = input.length, output = '';
14537     for (i = 0; i < l; i+=1) {
14538       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
14539     }
14540     return output;
14541   }
14542
14543   /**
14544    * Convert an array of big-endian words to a string
14545    */
14546   function binb2rstr(input) {
14547     var i, l = input.length * 32, output = '';
14548     for (i = 0; i < l; i += 8) {
14549         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
14550     }
14551     return output;
14552   }
14553
14554   /**
14555    * Convert an array of little-endian words to a string
14556    */
14557   function binl2rstr(input) {
14558     var i, l = input.length * 32, output = '';
14559     for (i = 0;i < l; i += 8) {
14560       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
14561     }
14562     return output;
14563   }
14564
14565   /**
14566    * Convert a raw string to an array of little-endian words
14567    * Characters >255 have their high-byte silently ignored.
14568    */
14569   function rstr2binl(input) {
14570     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14571     for (i = 0; i < lo; i+=1) {
14572       output[i] = 0;
14573     }
14574     for (i = 0; i < l; i += 8) {
14575       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
14576     }
14577     return output;
14578   }
14579
14580   /**
14581    * Convert a raw string to an array of big-endian words
14582    * Characters >255 have their high-byte silently ignored.
14583    */
14584    function rstr2binb(input) {
14585       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14586       for (i = 0; i < lo; i+=1) {
14587             output[i] = 0;
14588         }
14589       for (i = 0; i < l; i += 8) {
14590             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
14591         }
14592       return output;
14593    }
14594
14595   /**
14596    * Convert a raw string to an arbitrary string encoding
14597    */
14598   function rstr2any(input, encoding) {
14599     var divisor = encoding.length,
14600         remainders = Array(),
14601         i, q, x, ld, quotient, dividend, output, full_length;
14602
14603     /* Convert to an array of 16-bit big-endian values, forming the dividend */
14604     dividend = Array(Math.ceil(input.length / 2));
14605     ld = dividend.length;
14606     for (i = 0; i < ld; i+=1) {
14607       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
14608     }
14609
14610     /**
14611      * Repeatedly perform a long division. The binary array forms the dividend,
14612      * the length of the encoding is the divisor. Once computed, the quotient
14613      * forms the dividend for the next step. We stop when the dividend is zerHashes.
14614      * All remainders are stored for later use.
14615      */
14616     while(dividend.length > 0) {
14617       quotient = Array();
14618       x = 0;
14619       for (i = 0; i < dividend.length; i+=1) {
14620         x = (x << 16) + dividend[i];
14621         q = Math.floor(x / divisor);
14622         x -= q * divisor;
14623         if (quotient.length > 0 || q > 0) {
14624           quotient[quotient.length] = q;
14625         }
14626       }
14627       remainders[remainders.length] = x;
14628       dividend = quotient;
14629     }
14630
14631     /* Convert the remainders to the output string */
14632     output = '';
14633     for (i = remainders.length - 1; i >= 0; i--) {
14634       output += encoding.charAt(remainders[i]);
14635     }
14636
14637     /* Append leading zero equivalents */
14638     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
14639     for (i = output.length; i < full_length; i+=1) {
14640       output = encoding[0] + output;
14641     }
14642     return output;
14643   }
14644
14645   /**
14646    * Convert a raw string to a base-64 string
14647    */
14648   function rstr2b64(input, b64pad) {
14649     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14650         output = '',
14651         len = input.length, i, j, triplet;
14652     b64pad= b64pad || '=';
14653     for (i = 0; i < len; i += 3) {
14654       triplet = (input.charCodeAt(i) << 16)
14655             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14656             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
14657       for (j = 0; j < 4; j+=1) {
14658         if (i * 8 + j * 6 > input.length * 8) {
14659           output += b64pad;
14660         } else {
14661           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14662         }
14663        }
14664     }
14665     return output;
14666   }
14667
14668   Hashes = {
14669   /**
14670    * @property {String} version
14671    * @readonly
14672    */
14673   VERSION : '1.0.3',
14674   /**
14675    * @member Hashes
14676    * @class Base64
14677    * @constructor
14678    */
14679   Base64 : function () {
14680     // private properties
14681     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14682         pad = '=', // default pad according with the RFC standard
14683         url = false, // URL encoding support @todo
14684         utf8 = true; // by default enable UTF-8 support encoding
14685
14686     // public method for encoding
14687     this.encode = function (input) {
14688       var i, j, triplet,
14689           output = '',
14690           len = input.length;
14691
14692       pad = pad || '=';
14693       input = (utf8) ? utf8Encode(input) : input;
14694
14695       for (i = 0; i < len; i += 3) {
14696         triplet = (input.charCodeAt(i) << 16)
14697               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14698               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14699         for (j = 0; j < 4; j+=1) {
14700           if (i * 8 + j * 6 > len * 8) {
14701               output += pad;
14702           } else {
14703               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14704           }
14705         }
14706       }
14707       return output;
14708     };
14709
14710     // public method for decoding
14711     this.decode = function (input) {
14712       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
14713       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
14714         dec = '',
14715         arr = [];
14716       if (!input) { return input; }
14717
14718       i = ac = 0;
14719       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
14720       //input += '';
14721
14722       do { // unpack four hexets into three octets using index points in b64
14723         h1 = tab.indexOf(input.charAt(i+=1));
14724         h2 = tab.indexOf(input.charAt(i+=1));
14725         h3 = tab.indexOf(input.charAt(i+=1));
14726         h4 = tab.indexOf(input.charAt(i+=1));
14727
14728         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
14729
14730         o1 = bits >> 16 & 0xff;
14731         o2 = bits >> 8 & 0xff;
14732         o3 = bits & 0xff;
14733         ac += 1;
14734
14735         if (h3 === 64) {
14736           arr[ac] = String.fromCharCode(o1);
14737         } else if (h4 === 64) {
14738           arr[ac] = String.fromCharCode(o1, o2);
14739         } else {
14740           arr[ac] = String.fromCharCode(o1, o2, o3);
14741         }
14742       } while (i < input.length);
14743
14744       dec = arr.join('');
14745       dec = (utf8) ? utf8Decode(dec) : dec;
14746
14747       return dec;
14748     };
14749
14750     // set custom pad string
14751     this.setPad = function (str) {
14752         pad = str || pad;
14753         return this;
14754     };
14755     // set custom tab string characters
14756     this.setTab = function (str) {
14757         tab = str || tab;
14758         return this;
14759     };
14760     this.setUTF8 = function (bool) {
14761         if (typeof bool === 'boolean') {
14762           utf8 = bool;
14763         }
14764         return this;
14765     };
14766   },
14767
14768   /**
14769    * CRC-32 calculation
14770    * @member Hashes
14771    * @method CRC32
14772    * @static
14773    * @param {String} str Input String
14774    * @return {String}
14775    */
14776   CRC32 : function (str) {
14777     var crc = 0, x = 0, y = 0, table, i, iTop;
14778     str = utf8Encode(str);
14779
14780     table = [
14781         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
14782         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
14783         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
14784         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
14785         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
14786         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
14787         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
14788         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
14789         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
14790         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
14791         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
14792         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
14793         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
14794         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
14795         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
14796         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
14797         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
14798         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
14799         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
14800         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
14801         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
14802         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
14803         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
14804         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
14805         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
14806         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
14807     ].join('');
14808
14809     crc = crc ^ (-1);
14810     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
14811         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
14812         x = '0x' + table.substr( y * 9, 8 );
14813         crc = ( crc >>> 8 ) ^ x;
14814     }
14815     // always return a positive number (that's what >>> 0 does)
14816     return (crc ^ (-1)) >>> 0;
14817   },
14818   /**
14819    * @member Hashes
14820    * @class MD5
14821    * @constructor
14822    * @param {Object} [config]
14823    *
14824    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
14825    * Digest Algorithm, as defined in RFC 1321.
14826    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14827    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
14828    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
14829    */
14830   MD5 : function (options) {
14831     /**
14832      * Private config properties. You may need to tweak these to be compatible with
14833      * the server-side, but the defaults work in most cases.
14834      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
14835      */
14836     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
14837         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
14838         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
14839
14840     // privileged (public) methods
14841     this.hex = function (s) {
14842       return rstr2hex(rstr(s, utf8), hexcase);
14843     };
14844     this.b64 = function (s) {
14845       return rstr2b64(rstr(s), b64pad);
14846     };
14847     this.any = function(s, e) {
14848       return rstr2any(rstr(s, utf8), e);
14849     };
14850     this.hex_hmac = function (k, d) {
14851       return rstr2hex(rstr_hmac(k, d), hexcase);
14852     };
14853     this.b64_hmac = function (k, d) {
14854       return rstr2b64(rstr_hmac(k,d), b64pad);
14855     };
14856     this.any_hmac = function (k, d, e) {
14857       return rstr2any(rstr_hmac(k, d), e);
14858     };
14859     /**
14860      * Perform a simple self-test to see if the VM is working
14861      * @return {String} Hexadecimal hash sample
14862      */
14863     this.vm_test = function () {
14864       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
14865     };
14866     /**
14867      * Enable/disable uppercase hexadecimal returned string
14868      * @param {Boolean}
14869      * @return {Object} this
14870      */
14871     this.setUpperCase = function (a) {
14872       if (typeof a === 'boolean' ) {
14873         hexcase = a;
14874       }
14875       return this;
14876     };
14877     /**
14878      * Defines a base64 pad string
14879      * @param {String} Pad
14880      * @return {Object} this
14881      */
14882     this.setPad = function (a) {
14883       b64pad = a || b64pad;
14884       return this;
14885     };
14886     /**
14887      * Defines a base64 pad string
14888      * @param {Boolean}
14889      * @return {Object} [this]
14890      */
14891     this.setUTF8 = function (a) {
14892       if (typeof a === 'boolean') {
14893         utf8 = a;
14894       }
14895       return this;
14896     };
14897
14898     // private methods
14899
14900     /**
14901      * Calculate the MD5 of a raw string
14902      */
14903     function rstr(s) {
14904       s = (utf8) ? utf8Encode(s): s;
14905       return binl2rstr(binl(rstr2binl(s), s.length * 8));
14906     }
14907
14908     /**
14909      * Calculate the HMAC-MD5, of a key and some data (raw strings)
14910      */
14911     function rstr_hmac(key, data) {
14912       var bkey, ipad, opad, hash, i;
14913
14914       key = (utf8) ? utf8Encode(key) : key;
14915       data = (utf8) ? utf8Encode(data) : data;
14916       bkey = rstr2binl(key);
14917       if (bkey.length > 16) {
14918         bkey = binl(bkey, key.length * 8);
14919       }
14920
14921       ipad = Array(16), opad = Array(16);
14922       for (i = 0; i < 16; i+=1) {
14923           ipad[i] = bkey[i] ^ 0x36363636;
14924           opad[i] = bkey[i] ^ 0x5C5C5C5C;
14925       }
14926       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
14927       return binl2rstr(binl(opad.concat(hash), 512 + 128));
14928     }
14929
14930     /**
14931      * Calculate the MD5 of an array of little-endian words, and a bit length.
14932      */
14933     function binl(x, len) {
14934       var i, olda, oldb, oldc, oldd,
14935           a =  1732584193,
14936           b = -271733879,
14937           c = -1732584194,
14938           d =  271733878;
14939
14940       /* append padding */
14941       x[len >> 5] |= 0x80 << ((len) % 32);
14942       x[(((len + 64) >>> 9) << 4) + 14] = len;
14943
14944       for (i = 0; i < x.length; i += 16) {
14945         olda = a;
14946         oldb = b;
14947         oldc = c;
14948         oldd = d;
14949
14950         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
14951         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
14952         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
14953         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
14954         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
14955         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
14956         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
14957         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
14958         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
14959         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
14960         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
14961         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
14962         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
14963         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
14964         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
14965         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
14966
14967         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
14968         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
14969         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
14970         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
14971         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
14972         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
14973         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
14974         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
14975         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
14976         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
14977         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
14978         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
14979         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
14980         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
14981         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
14982         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
14983
14984         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
14985         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
14986         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
14987         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
14988         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
14989         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
14990         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
14991         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
14992         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
14993         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
14994         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
14995         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
14996         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
14997         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
14998         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
14999         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
15000
15001         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
15002         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
15003         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
15004         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
15005         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
15006         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
15007         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
15008         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
15009         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
15010         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
15011         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
15012         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
15013         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
15014         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
15015         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
15016         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
15017
15018         a = safe_add(a, olda);
15019         b = safe_add(b, oldb);
15020         c = safe_add(c, oldc);
15021         d = safe_add(d, oldd);
15022       }
15023       return Array(a, b, c, d);
15024     }
15025
15026     /**
15027      * These functions implement the four basic operations the algorithm uses.
15028      */
15029     function md5_cmn(q, a, b, x, s, t) {
15030       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
15031     }
15032     function md5_ff(a, b, c, d, x, s, t) {
15033       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
15034     }
15035     function md5_gg(a, b, c, d, x, s, t) {
15036       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
15037     }
15038     function md5_hh(a, b, c, d, x, s, t) {
15039       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
15040     }
15041     function md5_ii(a, b, c, d, x, s, t) {
15042       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
15043     }
15044   },
15045   /**
15046    * @member Hashes
15047    * @class Hashes.SHA1
15048    * @param {Object} [config]
15049    * @constructor
15050    *
15051    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
15052    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
15053    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15054    * See http://pajhome.org.uk/crypt/md5 for details.
15055    */
15056   SHA1 : function (options) {
15057    /**
15058      * Private config properties. You may need to tweak these to be compatible with
15059      * the server-side, but the defaults work in most cases.
15060      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
15061      */
15062     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
15063         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
15064         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
15065
15066     // public methods
15067     this.hex = function (s) {
15068         return rstr2hex(rstr(s, utf8), hexcase);
15069     };
15070     this.b64 = function (s) {
15071         return rstr2b64(rstr(s, utf8), b64pad);
15072     };
15073     this.any = function (s, e) {
15074         return rstr2any(rstr(s, utf8), e);
15075     };
15076     this.hex_hmac = function (k, d) {
15077         return rstr2hex(rstr_hmac(k, d));
15078     };
15079     this.b64_hmac = function (k, d) {
15080         return rstr2b64(rstr_hmac(k, d), b64pad);
15081     };
15082     this.any_hmac = function (k, d, e) {
15083         return rstr2any(rstr_hmac(k, d), e);
15084     };
15085     /**
15086      * Perform a simple self-test to see if the VM is working
15087      * @return {String} Hexadecimal hash sample
15088      * @public
15089      */
15090     this.vm_test = function () {
15091       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15092     };
15093     /**
15094      * @description Enable/disable uppercase hexadecimal returned string
15095      * @param {boolean}
15096      * @return {Object} this
15097      * @public
15098      */
15099     this.setUpperCase = function (a) {
15100         if (typeof a === 'boolean') {
15101         hexcase = a;
15102       }
15103         return this;
15104     };
15105     /**
15106      * @description Defines a base64 pad string
15107      * @param {string} Pad
15108      * @return {Object} this
15109      * @public
15110      */
15111     this.setPad = function (a) {
15112       b64pad = a || b64pad;
15113         return this;
15114     };
15115     /**
15116      * @description Defines a base64 pad string
15117      * @param {boolean}
15118      * @return {Object} this
15119      * @public
15120      */
15121     this.setUTF8 = function (a) {
15122         if (typeof a === 'boolean') {
15123         utf8 = a;
15124       }
15125         return this;
15126     };
15127
15128     // private methods
15129
15130     /**
15131          * Calculate the SHA-512 of a raw string
15132          */
15133         function rstr(s) {
15134       s = (utf8) ? utf8Encode(s) : s;
15135       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15136         }
15137
15138     /**
15139      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
15140      */
15141     function rstr_hmac(key, data) {
15142         var bkey, ipad, opad, i, hash;
15143         key = (utf8) ? utf8Encode(key) : key;
15144         data = (utf8) ? utf8Encode(data) : data;
15145         bkey = rstr2binb(key);
15146
15147         if (bkey.length > 16) {
15148         bkey = binb(bkey, key.length * 8);
15149       }
15150         ipad = Array(16), opad = Array(16);
15151         for (i = 0; i < 16; i+=1) {
15152                 ipad[i] = bkey[i] ^ 0x36363636;
15153                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15154         }
15155         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15156         return binb2rstr(binb(opad.concat(hash), 512 + 160));
15157     }
15158
15159     /**
15160      * Calculate the SHA-1 of an array of big-endian words, and a bit length
15161      */
15162     function binb(x, len) {
15163       var i, j, t, olda, oldb, oldc, oldd, olde,
15164           w = Array(80),
15165           a =  1732584193,
15166           b = -271733879,
15167           c = -1732584194,
15168           d =  271733878,
15169           e = -1009589776;
15170
15171       /* append padding */
15172       x[len >> 5] |= 0x80 << (24 - len % 32);
15173       x[((len + 64 >> 9) << 4) + 15] = len;
15174
15175       for (i = 0; i < x.length; i += 16) {
15176         olda = a,
15177         oldb = b;
15178         oldc = c;
15179         oldd = d;
15180         olde = e;
15181
15182         for (j = 0; j < 80; j+=1)       {
15183           if (j < 16) {
15184             w[j] = x[i + j];
15185           } else {
15186             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
15187           }
15188           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
15189                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
15190           e = d;
15191           d = c;
15192           c = bit_rol(b, 30);
15193           b = a;
15194           a = t;
15195         }
15196
15197         a = safe_add(a, olda);
15198         b = safe_add(b, oldb);
15199         c = safe_add(c, oldc);
15200         d = safe_add(d, oldd);
15201         e = safe_add(e, olde);
15202       }
15203       return Array(a, b, c, d, e);
15204     }
15205
15206     /**
15207      * Perform the appropriate triplet combination function for the current
15208      * iteration
15209      */
15210     function sha1_ft(t, b, c, d) {
15211       if (t < 20) { return (b & c) | ((~b) & d); }
15212       if (t < 40) { return b ^ c ^ d; }
15213       if (t < 60) { return (b & c) | (b & d) | (c & d); }
15214       return b ^ c ^ d;
15215     }
15216
15217     /**
15218      * Determine the appropriate additive constant for the current iteration
15219      */
15220     function sha1_kt(t) {
15221       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
15222                  (t < 60) ? -1894007588 : -899497514;
15223     }
15224   },
15225   /**
15226    * @class Hashes.SHA256
15227    * @param {config}
15228    *
15229    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
15230    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
15231    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15232    * See http://pajhome.org.uk/crypt/md5 for details.
15233    * Also http://anmar.eu.org/projects/jssha2/
15234    */
15235   SHA256 : function (options) {
15236     /**
15237      * Private properties configuration variables. You may need to tweak these to be compatible with
15238      * the server-side, but the defaults work in most cases.
15239      * @see this.setUpperCase() method
15240      * @see this.setPad() method
15241      */
15242     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
15243               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
15244               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15245               sha256_K;
15246
15247     /* privileged (public) methods */
15248     this.hex = function (s) {
15249       return rstr2hex(rstr(s, utf8));
15250     };
15251     this.b64 = function (s) {
15252       return rstr2b64(rstr(s, utf8), b64pad);
15253     };
15254     this.any = function (s, e) {
15255       return rstr2any(rstr(s, utf8), e);
15256     };
15257     this.hex_hmac = function (k, d) {
15258       return rstr2hex(rstr_hmac(k, d));
15259     };
15260     this.b64_hmac = function (k, d) {
15261       return rstr2b64(rstr_hmac(k, d), b64pad);
15262     };
15263     this.any_hmac = function (k, d, e) {
15264       return rstr2any(rstr_hmac(k, d), e);
15265     };
15266     /**
15267      * Perform a simple self-test to see if the VM is working
15268      * @return {String} Hexadecimal hash sample
15269      * @public
15270      */
15271     this.vm_test = function () {
15272       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15273     };
15274     /**
15275      * Enable/disable uppercase hexadecimal returned string
15276      * @param {boolean}
15277      * @return {Object} this
15278      * @public
15279      */
15280     this.setUpperCase = function (a) {
15281       if (typeof a === 'boolean') {
15282         hexcase = a;
15283       }
15284       return this;
15285     };
15286     /**
15287      * @description Defines a base64 pad string
15288      * @param {string} Pad
15289      * @return {Object} this
15290      * @public
15291      */
15292     this.setPad = function (a) {
15293       b64pad = a || b64pad;
15294       return this;
15295     };
15296     /**
15297      * Defines a base64 pad string
15298      * @param {boolean}
15299      * @return {Object} this
15300      * @public
15301      */
15302     this.setUTF8 = function (a) {
15303       if (typeof a === 'boolean') {
15304         utf8 = a;
15305       }
15306       return this;
15307     };
15308
15309     // private methods
15310
15311     /**
15312      * Calculate the SHA-512 of a raw string
15313      */
15314     function rstr(s, utf8) {
15315       s = (utf8) ? utf8Encode(s) : s;
15316       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15317     }
15318
15319     /**
15320      * Calculate the HMAC-sha256 of a key and some data (raw strings)
15321      */
15322     function rstr_hmac(key, data) {
15323       key = (utf8) ? utf8Encode(key) : key;
15324       data = (utf8) ? utf8Encode(data) : data;
15325       var hash, i = 0,
15326           bkey = rstr2binb(key),
15327           ipad = Array(16),
15328           opad = Array(16);
15329
15330       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
15331
15332       for (; i < 16; i+=1) {
15333         ipad[i] = bkey[i] ^ 0x36363636;
15334         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15335       }
15336
15337       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15338       return binb2rstr(binb(opad.concat(hash), 512 + 256));
15339     }
15340
15341     /*
15342      * Main sha256 function, with its support functions
15343      */
15344     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
15345     function sha256_R (X, n) {return ( X >>> n );}
15346     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
15347     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
15348     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
15349     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
15350     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
15351     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
15352     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
15353     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
15354     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
15355     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
15356
15357     sha256_K = [
15358       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
15359       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
15360       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
15361       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
15362       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
15363       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
15364       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
15365       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
15366       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
15367       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
15368       -1866530822, -1538233109, -1090935817, -965641998
15369     ];
15370
15371     function binb(m, l) {
15372       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
15373                  1359893119, -1694144372, 528734635, 1541459225];
15374       var W = new Array(64);
15375       var a, b, c, d, e, f, g, h;
15376       var i, j, T1, T2;
15377
15378       /* append padding */
15379       m[l >> 5] |= 0x80 << (24 - l % 32);
15380       m[((l + 64 >> 9) << 4) + 15] = l;
15381
15382       for (i = 0; i < m.length; i += 16)
15383       {
15384       a = HASH[0];
15385       b = HASH[1];
15386       c = HASH[2];
15387       d = HASH[3];
15388       e = HASH[4];
15389       f = HASH[5];
15390       g = HASH[6];
15391       h = HASH[7];
15392
15393       for (j = 0; j < 64; j+=1)
15394       {
15395         if (j < 16) {
15396           W[j] = m[j + i];
15397         } else {
15398           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
15399                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
15400         }
15401
15402         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
15403                                   sha256_K[j]), W[j]);
15404         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
15405         h = g;
15406         g = f;
15407         f = e;
15408         e = safe_add(d, T1);
15409         d = c;
15410         c = b;
15411         b = a;
15412         a = safe_add(T1, T2);
15413       }
15414
15415       HASH[0] = safe_add(a, HASH[0]);
15416       HASH[1] = safe_add(b, HASH[1]);
15417       HASH[2] = safe_add(c, HASH[2]);
15418       HASH[3] = safe_add(d, HASH[3]);
15419       HASH[4] = safe_add(e, HASH[4]);
15420       HASH[5] = safe_add(f, HASH[5]);
15421       HASH[6] = safe_add(g, HASH[6]);
15422       HASH[7] = safe_add(h, HASH[7]);
15423       }
15424       return HASH;
15425     }
15426
15427   },
15428
15429   /**
15430    * @class Hashes.SHA512
15431    * @param {config}
15432    *
15433    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
15434    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
15435    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15436    * See http://pajhome.org.uk/crypt/md5 for details.
15437    */
15438   SHA512 : function (options) {
15439     /**
15440      * Private properties configuration variables. You may need to tweak these to be compatible with
15441      * the server-side, but the defaults work in most cases.
15442      * @see this.setUpperCase() method
15443      * @see this.setPad() method
15444      */
15445     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
15446         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15447         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15448         sha512_k;
15449
15450     /* privileged (public) methods */
15451     this.hex = function (s) {
15452       return rstr2hex(rstr(s));
15453     };
15454     this.b64 = function (s) {
15455       return rstr2b64(rstr(s), b64pad);
15456     };
15457     this.any = function (s, e) {
15458       return rstr2any(rstr(s), e);
15459     };
15460     this.hex_hmac = function (k, d) {
15461       return rstr2hex(rstr_hmac(k, d));
15462     };
15463     this.b64_hmac = function (k, d) {
15464       return rstr2b64(rstr_hmac(k, d), b64pad);
15465     };
15466     this.any_hmac = function (k, d, e) {
15467       return rstr2any(rstr_hmac(k, d), e);
15468     };
15469     /**
15470      * Perform a simple self-test to see if the VM is working
15471      * @return {String} Hexadecimal hash sample
15472      * @public
15473      */
15474     this.vm_test = function () {
15475       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15476     };
15477     /**
15478      * @description Enable/disable uppercase hexadecimal returned string
15479      * @param {boolean}
15480      * @return {Object} this
15481      * @public
15482      */
15483     this.setUpperCase = function (a) {
15484       if (typeof a === 'boolean') {
15485         hexcase = a;
15486       }
15487       return this;
15488     };
15489     /**
15490      * @description Defines a base64 pad string
15491      * @param {string} Pad
15492      * @return {Object} this
15493      * @public
15494      */
15495     this.setPad = function (a) {
15496       b64pad = a || b64pad;
15497       return this;
15498     };
15499     /**
15500      * @description Defines a base64 pad string
15501      * @param {boolean}
15502      * @return {Object} this
15503      * @public
15504      */
15505     this.setUTF8 = function (a) {
15506       if (typeof a === 'boolean') {
15507         utf8 = a;
15508       }
15509       return this;
15510     };
15511
15512     /* private methods */
15513
15514     /**
15515      * Calculate the SHA-512 of a raw string
15516      */
15517     function rstr(s) {
15518       s = (utf8) ? utf8Encode(s) : s;
15519       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15520     }
15521     /*
15522      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
15523      */
15524     function rstr_hmac(key, data) {
15525       key = (utf8) ? utf8Encode(key) : key;
15526       data = (utf8) ? utf8Encode(data) : data;
15527
15528       var hash, i = 0,
15529           bkey = rstr2binb(key),
15530           ipad = Array(32), opad = Array(32);
15531
15532       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
15533
15534       for (; i < 32; i+=1) {
15535         ipad[i] = bkey[i] ^ 0x36363636;
15536         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15537       }
15538
15539       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
15540       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
15541     }
15542
15543     /**
15544      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
15545      */
15546     function binb(x, len) {
15547       var j, i, l,
15548           W = new Array(80),
15549           hash = new Array(16),
15550           //Initial hash values
15551           H = [
15552             new int64(0x6a09e667, -205731576),
15553             new int64(-1150833019, -2067093701),
15554             new int64(0x3c6ef372, -23791573),
15555             new int64(-1521486534, 0x5f1d36f1),
15556             new int64(0x510e527f, -1377402159),
15557             new int64(-1694144372, 0x2b3e6c1f),
15558             new int64(0x1f83d9ab, -79577749),
15559             new int64(0x5be0cd19, 0x137e2179)
15560           ],
15561           T1 = new int64(0, 0),
15562           T2 = new int64(0, 0),
15563           a = new int64(0,0),
15564           b = new int64(0,0),
15565           c = new int64(0,0),
15566           d = new int64(0,0),
15567           e = new int64(0,0),
15568           f = new int64(0,0),
15569           g = new int64(0,0),
15570           h = new int64(0,0),
15571           //Temporary variables not specified by the document
15572           s0 = new int64(0, 0),
15573           s1 = new int64(0, 0),
15574           Ch = new int64(0, 0),
15575           Maj = new int64(0, 0),
15576           r1 = new int64(0, 0),
15577           r2 = new int64(0, 0),
15578           r3 = new int64(0, 0);
15579
15580       if (sha512_k === undefined) {
15581           //SHA512 constants
15582           sha512_k = [
15583             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
15584             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
15585             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
15586             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
15587             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
15588             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
15589             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
15590             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
15591             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
15592             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
15593             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
15594             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
15595             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
15596             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
15597             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
15598             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
15599             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
15600             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
15601             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
15602             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
15603             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
15604             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
15605             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
15606             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
15607             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
15608             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
15609             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
15610             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
15611             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
15612             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
15613             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
15614             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
15615             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
15616             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
15617             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
15618             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
15619             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
15620             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
15621             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
15622             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
15623           ];
15624       }
15625
15626       for (i=0; i<80; i+=1) {
15627         W[i] = new int64(0, 0);
15628       }
15629
15630       // append padding to the source string. The format is described in the FIPS.
15631       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
15632       x[((len + 128 >> 10)<< 5) + 31] = len;
15633       l = x.length;
15634       for (i = 0; i<l; i+=32) { //32 dwords is the block size
15635         int64copy(a, H[0]);
15636         int64copy(b, H[1]);
15637         int64copy(c, H[2]);
15638         int64copy(d, H[3]);
15639         int64copy(e, H[4]);
15640         int64copy(f, H[5]);
15641         int64copy(g, H[6]);
15642         int64copy(h, H[7]);
15643
15644         for (j=0; j<16; j+=1) {
15645           W[j].h = x[i + 2*j];
15646           W[j].l = x[i + 2*j + 1];
15647         }
15648
15649         for (j=16; j<80; j+=1) {
15650           //sigma1
15651           int64rrot(r1, W[j-2], 19);
15652           int64revrrot(r2, W[j-2], 29);
15653           int64shr(r3, W[j-2], 6);
15654           s1.l = r1.l ^ r2.l ^ r3.l;
15655           s1.h = r1.h ^ r2.h ^ r3.h;
15656           //sigma0
15657           int64rrot(r1, W[j-15], 1);
15658           int64rrot(r2, W[j-15], 8);
15659           int64shr(r3, W[j-15], 7);
15660           s0.l = r1.l ^ r2.l ^ r3.l;
15661           s0.h = r1.h ^ r2.h ^ r3.h;
15662
15663           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
15664         }
15665
15666         for (j = 0; j < 80; j+=1) {
15667           //Ch
15668           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
15669           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
15670
15671           //Sigma1
15672           int64rrot(r1, e, 14);
15673           int64rrot(r2, e, 18);
15674           int64revrrot(r3, e, 9);
15675           s1.l = r1.l ^ r2.l ^ r3.l;
15676           s1.h = r1.h ^ r2.h ^ r3.h;
15677
15678           //Sigma0
15679           int64rrot(r1, a, 28);
15680           int64revrrot(r2, a, 2);
15681           int64revrrot(r3, a, 7);
15682           s0.l = r1.l ^ r2.l ^ r3.l;
15683           s0.h = r1.h ^ r2.h ^ r3.h;
15684
15685           //Maj
15686           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
15687           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
15688
15689           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
15690           int64add(T2, s0, Maj);
15691
15692           int64copy(h, g);
15693           int64copy(g, f);
15694           int64copy(f, e);
15695           int64add(e, d, T1);
15696           int64copy(d, c);
15697           int64copy(c, b);
15698           int64copy(b, a);
15699           int64add(a, T1, T2);
15700         }
15701         int64add(H[0], H[0], a);
15702         int64add(H[1], H[1], b);
15703         int64add(H[2], H[2], c);
15704         int64add(H[3], H[3], d);
15705         int64add(H[4], H[4], e);
15706         int64add(H[5], H[5], f);
15707         int64add(H[6], H[6], g);
15708         int64add(H[7], H[7], h);
15709       }
15710
15711       //represent the hash as an array of 32-bit dwords
15712       for (i=0; i<8; i+=1) {
15713         hash[2*i] = H[i].h;
15714         hash[2*i + 1] = H[i].l;
15715       }
15716       return hash;
15717     }
15718
15719     //A constructor for 64-bit numbers
15720     function int64(h, l) {
15721       this.h = h;
15722       this.l = l;
15723       //this.toString = int64toString;
15724     }
15725
15726     //Copies src into dst, assuming both are 64-bit numbers
15727     function int64copy(dst, src) {
15728       dst.h = src.h;
15729       dst.l = src.l;
15730     }
15731
15732     //Right-rotates a 64-bit number by shift
15733     //Won't handle cases of shift>=32
15734     //The function revrrot() is for that
15735     function int64rrot(dst, x, shift) {
15736       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15737       dst.h = (x.h >>> shift) | (x.l << (32-shift));
15738     }
15739
15740     //Reverses the dwords of the source and then rotates right by shift.
15741     //This is equivalent to rotation by 32+shift
15742     function int64revrrot(dst, x, shift) {
15743       dst.l = (x.h >>> shift) | (x.l << (32-shift));
15744       dst.h = (x.l >>> shift) | (x.h << (32-shift));
15745     }
15746
15747     //Bitwise-shifts right a 64-bit number by shift
15748     //Won't handle shift>=32, but it's never needed in SHA512
15749     function int64shr(dst, x, shift) {
15750       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15751       dst.h = (x.h >>> shift);
15752     }
15753
15754     //Adds two 64-bit numbers
15755     //Like the original implementation, does not rely on 32-bit operations
15756     function int64add(dst, x, y) {
15757        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
15758        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
15759        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
15760        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
15761        dst.l = (w0 & 0xffff) | (w1 << 16);
15762        dst.h = (w2 & 0xffff) | (w3 << 16);
15763     }
15764
15765     //Same, except with 4 addends. Works faster than adding them one by one.
15766     function int64add4(dst, a, b, c, d) {
15767        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
15768        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
15769        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
15770        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
15771        dst.l = (w0 & 0xffff) | (w1 << 16);
15772        dst.h = (w2 & 0xffff) | (w3 << 16);
15773     }
15774
15775     //Same, except with 5 addends
15776     function int64add5(dst, a, b, c, d, e) {
15777       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
15778           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
15779           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
15780           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
15781        dst.l = (w0 & 0xffff) | (w1 << 16);
15782        dst.h = (w2 & 0xffff) | (w3 << 16);
15783     }
15784   },
15785   /**
15786    * @class Hashes.RMD160
15787    * @constructor
15788    * @param {Object} [config]
15789    *
15790    * A JavaScript implementation of the RIPEMD-160 Algorithm
15791    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
15792    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15793    * See http://pajhome.org.uk/crypt/md5 for details.
15794    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
15795    */
15796   RMD160 : function (options) {
15797     /**
15798      * Private properties configuration variables. You may need to tweak these to be compatible with
15799      * the server-side, but the defaults work in most cases.
15800      * @see this.setUpperCase() method
15801      * @see this.setPad() method
15802      */
15803     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
15804         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15805         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15806         rmd160_r1 = [
15807            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
15808            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
15809            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
15810            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
15811            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
15812         ],
15813         rmd160_r2 = [
15814            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
15815            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
15816           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
15817            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
15818           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
15819         ],
15820         rmd160_s1 = [
15821           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
15822            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
15823           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
15824           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
15825            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
15826         ],
15827         rmd160_s2 = [
15828            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
15829            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
15830            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
15831           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
15832            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
15833         ];
15834
15835     /* privileged (public) methods */
15836     this.hex = function (s) {
15837       return rstr2hex(rstr(s, utf8));
15838     };
15839     this.b64 = function (s) {
15840       return rstr2b64(rstr(s, utf8), b64pad);
15841     };
15842     this.any = function (s, e) {
15843       return rstr2any(rstr(s, utf8), e);
15844     };
15845     this.hex_hmac = function (k, d) {
15846       return rstr2hex(rstr_hmac(k, d));
15847     };
15848     this.b64_hmac = function (k, d) {
15849       return rstr2b64(rstr_hmac(k, d), b64pad);
15850     };
15851     this.any_hmac = function (k, d, e) {
15852       return rstr2any(rstr_hmac(k, d), e);
15853     };
15854     /**
15855      * Perform a simple self-test to see if the VM is working
15856      * @return {String} Hexadecimal hash sample
15857      * @public
15858      */
15859     this.vm_test = function () {
15860       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15861     };
15862     /**
15863      * @description Enable/disable uppercase hexadecimal returned string
15864      * @param {boolean}
15865      * @return {Object} this
15866      * @public
15867      */
15868     this.setUpperCase = function (a) {
15869       if (typeof a === 'boolean' ) { hexcase = a; }
15870       return this;
15871     };
15872     /**
15873      * @description Defines a base64 pad string
15874      * @param {string} Pad
15875      * @return {Object} this
15876      * @public
15877      */
15878     this.setPad = function (a) {
15879       if (typeof a !== 'undefined' ) { b64pad = a; }
15880       return this;
15881     };
15882     /**
15883      * @description Defines a base64 pad string
15884      * @param {boolean}
15885      * @return {Object} this
15886      * @public
15887      */
15888     this.setUTF8 = function (a) {
15889       if (typeof a === 'boolean') { utf8 = a; }
15890       return this;
15891     };
15892
15893     /* private methods */
15894
15895     /**
15896      * Calculate the rmd160 of a raw string
15897      */
15898     function rstr(s) {
15899       s = (utf8) ? utf8Encode(s) : s;
15900       return binl2rstr(binl(rstr2binl(s), s.length * 8));
15901     }
15902
15903     /**
15904      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
15905      */
15906     function rstr_hmac(key, data) {
15907       key = (utf8) ? utf8Encode(key) : key;
15908       data = (utf8) ? utf8Encode(data) : data;
15909       var i, hash,
15910           bkey = rstr2binl(key),
15911           ipad = Array(16), opad = Array(16);
15912
15913       if (bkey.length > 16) {
15914         bkey = binl(bkey, key.length * 8);
15915       }
15916
15917       for (i = 0; i < 16; i+=1) {
15918         ipad[i] = bkey[i] ^ 0x36363636;
15919         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15920       }
15921       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
15922       return binl2rstr(binl(opad.concat(hash), 512 + 160));
15923     }
15924
15925     /**
15926      * Convert an array of little-endian words to a string
15927      */
15928     function binl2rstr(input) {
15929       var i, output = '', l = input.length * 32;
15930       for (i = 0; i < l; i += 8) {
15931         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15932       }
15933       return output;
15934     }
15935
15936     /**
15937      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
15938      */
15939     function binl(x, len) {
15940       var T, j, i, l,
15941           h0 = 0x67452301,
15942           h1 = 0xefcdab89,
15943           h2 = 0x98badcfe,
15944           h3 = 0x10325476,
15945           h4 = 0xc3d2e1f0,
15946           A1, B1, C1, D1, E1,
15947           A2, B2, C2, D2, E2;
15948
15949       /* append padding */
15950       x[len >> 5] |= 0x80 << (len % 32);
15951       x[(((len + 64) >>> 9) << 4) + 14] = len;
15952       l = x.length;
15953
15954       for (i = 0; i < l; i+=16) {
15955         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
15956         for (j = 0; j <= 79; j+=1) {
15957           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
15958           T = safe_add(T, x[i + rmd160_r1[j]]);
15959           T = safe_add(T, rmd160_K1(j));
15960           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
15961           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
15962           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
15963           T = safe_add(T, x[i + rmd160_r2[j]]);
15964           T = safe_add(T, rmd160_K2(j));
15965           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
15966           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
15967         }
15968
15969         T = safe_add(h1, safe_add(C1, D2));
15970         h1 = safe_add(h2, safe_add(D1, E2));
15971         h2 = safe_add(h3, safe_add(E1, A2));
15972         h3 = safe_add(h4, safe_add(A1, B2));
15973         h4 = safe_add(h0, safe_add(B1, C2));
15974         h0 = T;
15975       }
15976       return [h0, h1, h2, h3, h4];
15977     }
15978
15979     // specific algorithm methods
15980     function rmd160_f(j, x, y, z) {
15981       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
15982          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
15983          (32 <= j && j <= 47) ? (x | ~y) ^ z :
15984          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
15985          (64 <= j && j <= 79) ? x ^ (y | ~z) :
15986          'rmd160_f: j out of range';
15987     }
15988
15989     function rmd160_K1(j) {
15990       return ( 0 <= j && j <= 15) ? 0x00000000 :
15991          (16 <= j && j <= 31) ? 0x5a827999 :
15992          (32 <= j && j <= 47) ? 0x6ed9eba1 :
15993          (48 <= j && j <= 63) ? 0x8f1bbcdc :
15994          (64 <= j && j <= 79) ? 0xa953fd4e :
15995          'rmd160_K1: j out of range';
15996     }
15997
15998     function rmd160_K2(j){
15999       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
16000          (16 <= j && j <= 31) ? 0x5c4dd124 :
16001          (32 <= j && j <= 47) ? 0x6d703ef3 :
16002          (48 <= j && j <= 63) ? 0x7a6d76e9 :
16003          (64 <= j && j <= 79) ? 0x00000000 :
16004          'rmd160_K2: j out of range';
16005     }
16006   }
16007 };
16008
16009   // exposes Hashes
16010   (function( window, undefined ) {
16011     var freeExports = false;
16012     if (typeof exports === 'object' ) {
16013       freeExports = exports;
16014       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
16015     }
16016
16017     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
16018       // define as an anonymous module, so, through path mapping, it can be aliased
16019       define(function () { return Hashes; });
16020     }
16021     else if ( freeExports ) {
16022       // in Node.js or RingoJS v0.8.0+
16023       if ( typeof module === 'object' && module && module.exports === freeExports ) {
16024         module.exports = Hashes;
16025       }
16026       // in Narwhal or RingoJS v0.7.0-
16027       else {
16028         freeExports.Hashes = Hashes;
16029       }
16030     }
16031     else {
16032       // in a browser or Rhino
16033       window.Hashes = Hashes;
16034     }
16035   }( this ));
16036 }()); // IIFE
16037
16038 })(window)
16039 },{}],2:[function(require,module,exports){
16040 'use strict';
16041
16042 var hashes = require('jshashes'),
16043     xtend = require('xtend'),
16044     sha1 = new hashes.SHA1();
16045
16046 var ohauth = {};
16047
16048 ohauth.qsString = function(obj) {
16049     return Object.keys(obj).sort().map(function(key) {
16050         return ohauth.percentEncode(key) + '=' +
16051             ohauth.percentEncode(obj[key]);
16052     }).join('&');
16053 };
16054
16055 ohauth.stringQs = function(str) {
16056     return str.split('&').reduce(function(obj, pair){
16057         var parts = pair.split('=');
16058         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
16059             '' : decodeURIComponent(parts[1]);
16060         return obj;
16061     }, {});
16062 };
16063
16064 ohauth.rawxhr = function(method, url, data, headers, callback) {
16065     var xhr = new XMLHttpRequest(),
16066         twoHundred = /^20\d$/;
16067     xhr.onreadystatechange = function() {
16068         if (4 == xhr.readyState && 0 !== xhr.status) {
16069             if (twoHundred.test(xhr.status)) callback(null, xhr);
16070             else return callback(xhr, null);
16071         }
16072     };
16073     xhr.onerror = function(e) { return callback(e, null); };
16074     xhr.open(method, url, true);
16075     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
16076     xhr.send(data);
16077 };
16078
16079 ohauth.xhr = function(method, url, auth, data, options, callback) {
16080     var headers = (options && options.header) || {
16081         'Content-Type': 'application/x-www-form-urlencoded'
16082     };
16083     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
16084     ohauth.rawxhr(method, url, data, headers, callback);
16085 };
16086
16087 ohauth.nonce = function() {
16088     for (var o = ''; o.length < 6;) {
16089         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
16090     }
16091     return o;
16092 };
16093
16094 ohauth.authHeader = function(obj) {
16095     return Object.keys(obj).sort().map(function(key) {
16096         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
16097     }).join(', ');
16098 };
16099
16100 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
16101
16102 ohauth.percentEncode = function(s) {
16103     return encodeURIComponent(s)
16104         .replace(/\!/g, '%21').replace(/\'/g, '%27')
16105         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
16106 };
16107
16108 ohauth.baseString = function(method, url, params) {
16109     if (params.oauth_signature) delete params.oauth_signature;
16110     return [
16111         method,
16112         ohauth.percentEncode(url),
16113         ohauth.percentEncode(ohauth.qsString(params))].join('&');
16114 };
16115
16116 ohauth.signature = function(oauth_secret, token_secret, baseString) {
16117     return sha1.b64_hmac(
16118         ohauth.percentEncode(oauth_secret) + '&' +
16119         ohauth.percentEncode(token_secret),
16120         baseString);
16121 };
16122
16123 /**
16124  * Takes an options object for configuration (consumer_key,
16125  * consumer_secret, version, signature_method, token) and returns a
16126  * function that generates the Authorization header for given data.
16127  *
16128  * The returned function takes these parameters:
16129  * - method: GET/POST/...
16130  * - uri: full URI with protocol, port, path and query string
16131  * - extra_params: any extra parameters (that are passed in the POST data),
16132  *   can be an object or a from-urlencoded string.
16133  *
16134  * Returned function returns full OAuth header with "OAuth" string in it.
16135  */
16136
16137 ohauth.headerGenerator = function(options) {
16138     options = options || {};
16139     var consumer_key = options.consumer_key || '',
16140         consumer_secret = options.consumer_secret || '',
16141         signature_method = options.signature_method || 'HMAC-SHA1',
16142         version = options.version || '1.0',
16143         token = options.token || '';
16144
16145     return function(method, uri, extra_params) {
16146         method = method.toUpperCase();
16147         if (typeof extra_params === 'string' && extra_params.length > 0) {
16148             extra_params = ohauth.stringQs(extra_params);
16149         }
16150
16151         var uri_parts = uri.split('?', 2),
16152         base_uri = uri_parts[0];
16153
16154         var query_params = uri_parts.length === 2 ?
16155             ohauth.stringQs(uri_parts[1]) : {};
16156
16157         var oauth_params = {
16158             oauth_consumer_key: consumer_key,
16159             oauth_signature_method: signature_method,
16160             oauth_version: version,
16161             oauth_timestamp: ohauth.timestamp(),
16162             oauth_nonce: ohauth.nonce()
16163         };
16164
16165         if (token) oauth_params.oauth_token = token;
16166
16167         var all_params = xtend({}, oauth_params, query_params, extra_params),
16168             base_str = ohauth.baseString(method, base_uri, all_params);
16169
16170         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
16171
16172         return 'OAuth ' + ohauth.authHeader(oauth_params);
16173     };
16174 };
16175
16176 module.exports = ohauth;
16177
16178 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
16179 module.exports = Object.keys || require('./shim');
16180
16181
16182 },{"./shim":8}],8:[function(require,module,exports){
16183 (function () {
16184         "use strict";
16185
16186         // modified from https://github.com/kriskowal/es5-shim
16187         var has = Object.prototype.hasOwnProperty,
16188                 is = require('is'),
16189                 forEach = require('foreach'),
16190                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
16191                 dontEnums = [
16192                         "toString",
16193                         "toLocaleString",
16194                         "valueOf",
16195                         "hasOwnProperty",
16196                         "isPrototypeOf",
16197                         "propertyIsEnumerable",
16198                         "constructor"
16199                 ],
16200                 keysShim;
16201
16202         keysShim = function keys(object) {
16203                 if (!is.object(object) && !is.array(object)) {
16204                         throw new TypeError("Object.keys called on a non-object");
16205                 }
16206
16207                 var name, theKeys = [];
16208                 for (name in object) {
16209                         if (has.call(object, name)) {
16210                                 theKeys.push(name);
16211                         }
16212                 }
16213
16214                 if (hasDontEnumBug) {
16215                         forEach(dontEnums, function (dontEnum) {
16216                                 if (has.call(object, dontEnum)) {
16217                                         theKeys.push(dontEnum);
16218                                 }
16219                         });
16220                 }
16221                 return theKeys;
16222         };
16223
16224         module.exports = keysShim;
16225 }());
16226
16227
16228 },{"is":9,"foreach":10}],9:[function(require,module,exports){
16229
16230 /**!
16231  * is
16232  * the definitive JavaScript type testing library
16233  *
16234  * @copyright 2013 Enrico Marino
16235  * @license MIT
16236  */
16237
16238 var objProto = Object.prototype;
16239 var owns = objProto.hasOwnProperty;
16240 var toString = objProto.toString;
16241 var isActualNaN = function (value) {
16242   return value !== value;
16243 };
16244 var NON_HOST_TYPES = {
16245   "boolean": 1,
16246   "number": 1,
16247   "string": 1,
16248   "undefined": 1
16249 };
16250
16251 /**
16252  * Expose `is`
16253  */
16254
16255 var is = module.exports = {};
16256
16257 /**
16258  * Test general.
16259  */
16260
16261 /**
16262  * is.type
16263  * Test if `value` is a type of `type`.
16264  *
16265  * @param {Mixed} value value to test
16266  * @param {String} type type
16267  * @return {Boolean} true if `value` is a type of `type`, false otherwise
16268  * @api public
16269  */
16270
16271 is.a =
16272 is.type = function (value, type) {
16273   return typeof value === type;
16274 };
16275
16276 /**
16277  * is.defined
16278  * Test if `value` is defined.
16279  *
16280  * @param {Mixed} value value to test
16281  * @return {Boolean} true if 'value' is defined, false otherwise
16282  * @api public
16283  */
16284
16285 is.defined = function (value) {
16286   return value !== undefined;
16287 };
16288
16289 /**
16290  * is.empty
16291  * Test if `value` is empty.
16292  *
16293  * @param {Mixed} value value to test
16294  * @return {Boolean} true if `value` is empty, false otherwise
16295  * @api public
16296  */
16297
16298 is.empty = function (value) {
16299   var type = toString.call(value);
16300   var key;
16301
16302   if ('[object Array]' === type || '[object Arguments]' === type) {
16303     return value.length === 0;
16304   }
16305
16306   if ('[object Object]' === type) {
16307     for (key in value) if (owns.call(value, key)) return false;
16308     return true;
16309   }
16310
16311   if ('[object String]' === type) {
16312     return '' === value;
16313   }
16314
16315   return false;
16316 };
16317
16318 /**
16319  * is.equal
16320  * Test if `value` is equal to `other`.
16321  *
16322  * @param {Mixed} value value to test
16323  * @param {Mixed} other value to compare with
16324  * @return {Boolean} true if `value` is equal to `other`, false otherwise
16325  */
16326
16327 is.equal = function (value, other) {
16328   var type = toString.call(value)
16329   var key;
16330
16331   if (type !== toString.call(other)) {
16332     return false;
16333   }
16334
16335   if ('[object Object]' === type) {
16336     for (key in value) {
16337       if (!is.equal(value[key], other[key])) {
16338         return false;
16339       }
16340     }
16341     return true;
16342   }
16343
16344   if ('[object Array]' === type) {
16345     key = value.length;
16346     if (key !== other.length) {
16347       return false;
16348     }
16349     while (--key) {
16350       if (!is.equal(value[key], other[key])) {
16351         return false;
16352       }
16353     }
16354     return true;
16355   }
16356
16357   if ('[object Function]' === type) {
16358     return value.prototype === other.prototype;
16359   }
16360
16361   if ('[object Date]' === type) {
16362     return value.getTime() === other.getTime();
16363   }
16364
16365   return value === other;
16366 };
16367
16368 /**
16369  * is.hosted
16370  * Test if `value` is hosted by `host`.
16371  *
16372  * @param {Mixed} value to test
16373  * @param {Mixed} host host to test with
16374  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
16375  * @api public
16376  */
16377
16378 is.hosted = function (value, host) {
16379   var type = typeof host[value];
16380   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
16381 };
16382
16383 /**
16384  * is.instance
16385  * Test if `value` is an instance of `constructor`.
16386  *
16387  * @param {Mixed} value value to test
16388  * @return {Boolean} true if `value` is an instance of `constructor`
16389  * @api public
16390  */
16391
16392 is.instance = is['instanceof'] = function (value, constructor) {
16393   return value instanceof constructor;
16394 };
16395
16396 /**
16397  * is.null
16398  * Test if `value` is null.
16399  *
16400  * @param {Mixed} value value to test
16401  * @return {Boolean} true if `value` is null, false otherwise
16402  * @api public
16403  */
16404
16405 is['null'] = function (value) {
16406   return value === null;
16407 };
16408
16409 /**
16410  * is.undefined
16411  * Test if `value` is undefined.
16412  *
16413  * @param {Mixed} value value to test
16414  * @return {Boolean} true if `value` is undefined, false otherwise
16415  * @api public
16416  */
16417
16418 is.undefined = function (value) {
16419   return value === undefined;
16420 };
16421
16422 /**
16423  * Test arguments.
16424  */
16425
16426 /**
16427  * is.arguments
16428  * Test if `value` is an arguments object.
16429  *
16430  * @param {Mixed} value value to test
16431  * @return {Boolean} true if `value` is an arguments object, false otherwise
16432  * @api public
16433  */
16434
16435 is.arguments = function (value) {
16436   var isStandardArguments = '[object Arguments]' === toString.call(value);
16437   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
16438   return isStandardArguments || isOldArguments;
16439 };
16440
16441 /**
16442  * Test array.
16443  */
16444
16445 /**
16446  * is.array
16447  * Test if 'value' is an array.
16448  *
16449  * @param {Mixed} value value to test
16450  * @return {Boolean} true if `value` is an array, false otherwise
16451  * @api public
16452  */
16453
16454 is.array = function (value) {
16455   return '[object Array]' === toString.call(value);
16456 };
16457
16458 /**
16459  * is.arguments.empty
16460  * Test if `value` is an empty arguments object.
16461  *
16462  * @param {Mixed} value value to test
16463  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
16464  * @api public
16465  */
16466 is.arguments.empty = function (value) {
16467   return is.arguments(value) && value.length === 0;
16468 };
16469
16470 /**
16471  * is.array.empty
16472  * Test if `value` is an empty array.
16473  *
16474  * @param {Mixed} value value to test
16475  * @return {Boolean} true if `value` is an empty array, false otherwise
16476  * @api public
16477  */
16478 is.array.empty = function (value) {
16479   return is.array(value) && value.length === 0;
16480 };
16481
16482 /**
16483  * is.arraylike
16484  * Test if `value` is an arraylike object.
16485  *
16486  * @param {Mixed} value value to test
16487  * @return {Boolean} true if `value` is an arguments object, false otherwise
16488  * @api public
16489  */
16490
16491 is.arraylike = function (value) {
16492   return !!value && !is.boolean(value)
16493     && owns.call(value, 'length')
16494     && isFinite(value.length)
16495     && is.number(value.length)
16496     && value.length >= 0;
16497 };
16498
16499 /**
16500  * Test boolean.
16501  */
16502
16503 /**
16504  * is.boolean
16505  * Test if `value` is a boolean.
16506  *
16507  * @param {Mixed} value value to test
16508  * @return {Boolean} true if `value` is a boolean, false otherwise
16509  * @api public
16510  */
16511
16512 is.boolean = function (value) {
16513   return '[object Boolean]' === toString.call(value);
16514 };
16515
16516 /**
16517  * is.false
16518  * Test if `value` is false.
16519  *
16520  * @param {Mixed} value value to test
16521  * @return {Boolean} true if `value` is false, false otherwise
16522  * @api public
16523  */
16524
16525 is['false'] = function (value) {
16526   return is.boolean(value) && (value === false || value.valueOf() === false);
16527 };
16528
16529 /**
16530  * is.true
16531  * Test if `value` is true.
16532  *
16533  * @param {Mixed} value value to test
16534  * @return {Boolean} true if `value` is true, false otherwise
16535  * @api public
16536  */
16537
16538 is['true'] = function (value) {
16539   return is.boolean(value) && (value === true || value.valueOf() === true);
16540 };
16541
16542 /**
16543  * Test date.
16544  */
16545
16546 /**
16547  * is.date
16548  * Test if `value` is a date.
16549  *
16550  * @param {Mixed} value value to test
16551  * @return {Boolean} true if `value` is a date, false otherwise
16552  * @api public
16553  */
16554
16555 is.date = function (value) {
16556   return '[object Date]' === toString.call(value);
16557 };
16558
16559 /**
16560  * Test element.
16561  */
16562
16563 /**
16564  * is.element
16565  * Test if `value` is an html element.
16566  *
16567  * @param {Mixed} value value to test
16568  * @return {Boolean} true if `value` is an HTML Element, false otherwise
16569  * @api public
16570  */
16571
16572 is.element = function (value) {
16573   return value !== undefined
16574     && typeof HTMLElement !== 'undefined'
16575     && value instanceof HTMLElement
16576     && value.nodeType === 1;
16577 };
16578
16579 /**
16580  * Test error.
16581  */
16582
16583 /**
16584  * is.error
16585  * Test if `value` is an error object.
16586  *
16587  * @param {Mixed} value value to test
16588  * @return {Boolean} true if `value` is an error object, false otherwise
16589  * @api public
16590  */
16591
16592 is.error = function (value) {
16593   return '[object Error]' === toString.call(value);
16594 };
16595
16596 /**
16597  * Test function.
16598  */
16599
16600 /**
16601  * is.fn / is.function (deprecated)
16602  * Test if `value` is a function.
16603  *
16604  * @param {Mixed} value value to test
16605  * @return {Boolean} true if `value` is a function, false otherwise
16606  * @api public
16607  */
16608
16609 is.fn = is['function'] = function (value) {
16610   var isAlert = typeof window !== 'undefined' && value === window.alert;
16611   return isAlert || '[object Function]' === toString.call(value);
16612 };
16613
16614 /**
16615  * Test number.
16616  */
16617
16618 /**
16619  * is.number
16620  * Test if `value` is a number.
16621  *
16622  * @param {Mixed} value value to test
16623  * @return {Boolean} true if `value` is a number, false otherwise
16624  * @api public
16625  */
16626
16627 is.number = function (value) {
16628   return '[object Number]' === toString.call(value);
16629 };
16630
16631 /**
16632  * is.infinite
16633  * Test if `value` is positive or negative infinity.
16634  *
16635  * @param {Mixed} value value to test
16636  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
16637  * @api public
16638  */
16639 is.infinite = function (value) {
16640   return value === Infinity || value === -Infinity;
16641 };
16642
16643 /**
16644  * is.decimal
16645  * Test if `value` is a decimal number.
16646  *
16647  * @param {Mixed} value value to test
16648  * @return {Boolean} true if `value` is a decimal number, false otherwise
16649  * @api public
16650  */
16651
16652 is.decimal = function (value) {
16653   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
16654 };
16655
16656 /**
16657  * is.divisibleBy
16658  * Test if `value` is divisible by `n`.
16659  *
16660  * @param {Number} value value to test
16661  * @param {Number} n dividend
16662  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
16663  * @api public
16664  */
16665
16666 is.divisibleBy = function (value, n) {
16667   var isDividendInfinite = is.infinite(value);
16668   var isDivisorInfinite = is.infinite(n);
16669   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
16670   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
16671 };
16672
16673 /**
16674  * is.int
16675  * Test if `value` is an integer.
16676  *
16677  * @param value to test
16678  * @return {Boolean} true if `value` is an integer, false otherwise
16679  * @api public
16680  */
16681
16682 is.int = function (value) {
16683   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
16684 };
16685
16686 /**
16687  * is.maximum
16688  * Test if `value` is greater than 'others' values.
16689  *
16690  * @param {Number} value value to test
16691  * @param {Array} others values to compare with
16692  * @return {Boolean} true if `value` is greater than `others` values
16693  * @api public
16694  */
16695
16696 is.maximum = function (value, others) {
16697   if (isActualNaN(value)) {
16698     throw new TypeError('NaN is not a valid value');
16699   } else if (!is.arraylike(others)) {
16700     throw new TypeError('second argument must be array-like');
16701   }
16702   var len = others.length;
16703
16704   while (--len >= 0) {
16705     if (value < others[len]) {
16706       return false;
16707     }
16708   }
16709
16710   return true;
16711 };
16712
16713 /**
16714  * is.minimum
16715  * Test if `value` is less than `others` values.
16716  *
16717  * @param {Number} value value to test
16718  * @param {Array} others values to compare with
16719  * @return {Boolean} true if `value` is less than `others` values
16720  * @api public
16721  */
16722
16723 is.minimum = function (value, others) {
16724   if (isActualNaN(value)) {
16725     throw new TypeError('NaN is not a valid value');
16726   } else if (!is.arraylike(others)) {
16727     throw new TypeError('second argument must be array-like');
16728   }
16729   var len = others.length;
16730
16731   while (--len >= 0) {
16732     if (value > others[len]) {
16733       return false;
16734     }
16735   }
16736
16737   return true;
16738 };
16739
16740 /**
16741  * is.nan
16742  * Test if `value` is not a number.
16743  *
16744  * @param {Mixed} value value to test
16745  * @return {Boolean} true if `value` is not a number, false otherwise
16746  * @api public
16747  */
16748
16749 is.nan = function (value) {
16750   return !is.number(value) || value !== value;
16751 };
16752
16753 /**
16754  * is.even
16755  * Test if `value` is an even number.
16756  *
16757  * @param {Number} value value to test
16758  * @return {Boolean} true if `value` is an even number, false otherwise
16759  * @api public
16760  */
16761
16762 is.even = function (value) {
16763   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
16764 };
16765
16766 /**
16767  * is.odd
16768  * Test if `value` is an odd number.
16769  *
16770  * @param {Number} value value to test
16771  * @return {Boolean} true if `value` is an odd number, false otherwise
16772  * @api public
16773  */
16774
16775 is.odd = function (value) {
16776   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
16777 };
16778
16779 /**
16780  * is.ge
16781  * Test if `value` is greater than or equal to `other`.
16782  *
16783  * @param {Number} value value to test
16784  * @param {Number} other value to compare with
16785  * @return {Boolean}
16786  * @api public
16787  */
16788
16789 is.ge = function (value, other) {
16790   if (isActualNaN(value) || isActualNaN(other)) {
16791     throw new TypeError('NaN is not a valid value');
16792   }
16793   return !is.infinite(value) && !is.infinite(other) && value >= other;
16794 };
16795
16796 /**
16797  * is.gt
16798  * Test if `value` is greater than `other`.
16799  *
16800  * @param {Number} value value to test
16801  * @param {Number} other value to compare with
16802  * @return {Boolean}
16803  * @api public
16804  */
16805
16806 is.gt = function (value, other) {
16807   if (isActualNaN(value) || isActualNaN(other)) {
16808     throw new TypeError('NaN is not a valid value');
16809   }
16810   return !is.infinite(value) && !is.infinite(other) && value > other;
16811 };
16812
16813 /**
16814  * is.le
16815  * Test if `value` is less than or equal to `other`.
16816  *
16817  * @param {Number} value value to test
16818  * @param {Number} other value to compare with
16819  * @return {Boolean} if 'value' is less than or equal to 'other'
16820  * @api public
16821  */
16822
16823 is.le = function (value, other) {
16824   if (isActualNaN(value) || isActualNaN(other)) {
16825     throw new TypeError('NaN is not a valid value');
16826   }
16827   return !is.infinite(value) && !is.infinite(other) && value <= other;
16828 };
16829
16830 /**
16831  * is.lt
16832  * Test if `value` is less than `other`.
16833  *
16834  * @param {Number} value value to test
16835  * @param {Number} other value to compare with
16836  * @return {Boolean} if `value` is less than `other`
16837  * @api public
16838  */
16839
16840 is.lt = function (value, other) {
16841   if (isActualNaN(value) || isActualNaN(other)) {
16842     throw new TypeError('NaN is not a valid value');
16843   }
16844   return !is.infinite(value) && !is.infinite(other) && value < other;
16845 };
16846
16847 /**
16848  * is.within
16849  * Test if `value` is within `start` and `finish`.
16850  *
16851  * @param {Number} value value to test
16852  * @param {Number} start lower bound
16853  * @param {Number} finish upper bound
16854  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
16855  * @api public
16856  */
16857 is.within = function (value, start, finish) {
16858   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
16859     throw new TypeError('NaN is not a valid value');
16860   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
16861     throw new TypeError('all arguments must be numbers');
16862   }
16863   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
16864   return isAnyInfinite || (value >= start && value <= finish);
16865 };
16866
16867 /**
16868  * Test object.
16869  */
16870
16871 /**
16872  * is.object
16873  * Test if `value` is an object.
16874  *
16875  * @param {Mixed} value value to test
16876  * @return {Boolean} true if `value` is an object, false otherwise
16877  * @api public
16878  */
16879
16880 is.object = function (value) {
16881   return value && '[object Object]' === toString.call(value);
16882 };
16883
16884 /**
16885  * is.hash
16886  * Test if `value` is a hash - a plain object literal.
16887  *
16888  * @param {Mixed} value value to test
16889  * @return {Boolean} true if `value` is a hash, false otherwise
16890  * @api public
16891  */
16892
16893 is.hash = function (value) {
16894   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
16895 };
16896
16897 /**
16898  * Test regexp.
16899  */
16900
16901 /**
16902  * is.regexp
16903  * Test if `value` is a regular expression.
16904  *
16905  * @param {Mixed} value value to test
16906  * @return {Boolean} true if `value` is a regexp, false otherwise
16907  * @api public
16908  */
16909
16910 is.regexp = function (value) {
16911   return '[object RegExp]' === toString.call(value);
16912 };
16913
16914 /**
16915  * Test string.
16916  */
16917
16918 /**
16919  * is.string
16920  * Test if `value` is a string.
16921  *
16922  * @param {Mixed} value value to test
16923  * @return {Boolean} true if 'value' is a string, false otherwise
16924  * @api public
16925  */
16926
16927 is.string = function (value) {
16928   return '[object String]' === toString.call(value);
16929 };
16930
16931
16932 },{}],10:[function(require,module,exports){
16933
16934 var hasOwn = Object.prototype.hasOwnProperty;
16935 var toString = Object.prototype.toString;
16936
16937 module.exports = function forEach (obj, fn, ctx) {
16938     if (toString.call(fn) !== '[object Function]') {
16939         throw new TypeError('iterator must be a function');
16940     }
16941     var l = obj.length;
16942     if (l === +l) {
16943         for (var i = 0; i < l; i++) {
16944             fn.call(ctx, obj[i], i, obj);
16945         }
16946     } else {
16947         for (var k in obj) {
16948             if (hasOwn.call(obj, k)) {
16949                 fn.call(ctx, obj[k], k, obj);
16950             }
16951         }
16952     }
16953 };
16954
16955
16956 },{}]},{},[1])(1)
16957 });
16958 ;
16959 /*
16960  (c) 2015, Vladimir Agafonkin
16961  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
16962  https://github.com/mourner/rbush
16963 */
16964
16965 (function () {
16966 'use strict';
16967
16968 function rbush(maxEntries, format) {
16969
16970     // jshint newcap: false, validthis: true
16971     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
16972
16973     // max entries in a node is 9 by default; min node fill is 40% for best performance
16974     this._maxEntries = Math.max(4, maxEntries || 9);
16975     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
16976
16977     if (format) {
16978         this._initFormat(format);
16979     }
16980
16981     this.clear();
16982 }
16983
16984 rbush.prototype = {
16985
16986     all: function () {
16987         return this._all(this.data, []);
16988     },
16989
16990     search: function (bbox) {
16991
16992         var node = this.data,
16993             result = [],
16994             toBBox = this.toBBox;
16995
16996         if (!intersects(bbox, node.bbox)) return result;
16997
16998         var nodesToSearch = [],
16999             i, len, child, childBBox;
17000
17001         while (node) {
17002             for (i = 0, len = node.children.length; i < len; i++) {
17003
17004                 child = node.children[i];
17005                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17006
17007                 if (intersects(bbox, childBBox)) {
17008                     if (node.leaf) result.push(child);
17009                     else if (contains(bbox, childBBox)) this._all(child, result);
17010                     else nodesToSearch.push(child);
17011                 }
17012             }
17013             node = nodesToSearch.pop();
17014         }
17015
17016         return result;
17017     },
17018
17019     collides: function (bbox) {
17020
17021         var node = this.data,
17022             toBBox = this.toBBox;
17023
17024         if (!intersects(bbox, node.bbox)) return false;
17025
17026         var nodesToSearch = [],
17027             i, len, child, childBBox;
17028
17029         while (node) {
17030             for (i = 0, len = node.children.length; i < len; i++) {
17031
17032                 child = node.children[i];
17033                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17034
17035                 if (intersects(bbox, childBBox)) {
17036                     if (node.leaf || contains(bbox, childBBox)) return true;
17037                     nodesToSearch.push(child);
17038                 }
17039             }
17040             node = nodesToSearch.pop();
17041         }
17042
17043         return false;
17044     },
17045
17046     load: function (data) {
17047         if (!(data && data.length)) return this;
17048
17049         if (data.length < this._minEntries) {
17050             for (var i = 0, len = data.length; i < len; i++) {
17051                 this.insert(data[i]);
17052             }
17053             return this;
17054         }
17055
17056         // recursively build the tree with the given data from stratch using OMT algorithm
17057         var node = this._build(data.slice(), 0, data.length - 1, 0);
17058
17059         if (!this.data.children.length) {
17060             // save as is if tree is empty
17061             this.data = node;
17062
17063         } else if (this.data.height === node.height) {
17064             // split root if trees have the same height
17065             this._splitRoot(this.data, node);
17066
17067         } else {
17068             if (this.data.height < node.height) {
17069                 // swap trees if inserted one is bigger
17070                 var tmpNode = this.data;
17071                 this.data = node;
17072                 node = tmpNode;
17073             }
17074
17075             // insert the small tree into the large tree at appropriate level
17076             this._insert(node, this.data.height - node.height - 1, true);
17077         }
17078
17079         return this;
17080     },
17081
17082     insert: function (item) {
17083         if (item) this._insert(item, this.data.height - 1);
17084         return this;
17085     },
17086
17087     clear: function () {
17088         this.data = {
17089             children: [],
17090             height: 1,
17091             bbox: empty(),
17092             leaf: true
17093         };
17094         return this;
17095     },
17096
17097     remove: function (item) {
17098         if (!item) return this;
17099
17100         var node = this.data,
17101             bbox = this.toBBox(item),
17102             path = [],
17103             indexes = [],
17104             i, parent, index, goingUp;
17105
17106         // depth-first iterative tree traversal
17107         while (node || path.length) {
17108
17109             if (!node) { // go up
17110                 node = path.pop();
17111                 parent = path[path.length - 1];
17112                 i = indexes.pop();
17113                 goingUp = true;
17114             }
17115
17116             if (node.leaf) { // check current node
17117                 index = node.children.indexOf(item);
17118
17119                 if (index !== -1) {
17120                     // item found, remove the item and condense tree upwards
17121                     node.children.splice(index, 1);
17122                     path.push(node);
17123                     this._condense(path);
17124                     return this;
17125                 }
17126             }
17127
17128             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
17129                 path.push(node);
17130                 indexes.push(i);
17131                 i = 0;
17132                 parent = node;
17133                 node = node.children[0];
17134
17135             } else if (parent) { // go right
17136                 i++;
17137                 node = parent.children[i];
17138                 goingUp = false;
17139
17140             } else node = null; // nothing found
17141         }
17142
17143         return this;
17144     },
17145
17146     toBBox: function (item) { return item; },
17147
17148     compareMinX: function (a, b) { return a[0] - b[0]; },
17149     compareMinY: function (a, b) { return a[1] - b[1]; },
17150
17151     toJSON: function () { return this.data; },
17152
17153     fromJSON: function (data) {
17154         this.data = data;
17155         return this;
17156     },
17157
17158     _all: function (node, result) {
17159         var nodesToSearch = [];
17160         while (node) {
17161             if (node.leaf) result.push.apply(result, node.children);
17162             else nodesToSearch.push.apply(nodesToSearch, node.children);
17163
17164             node = nodesToSearch.pop();
17165         }
17166         return result;
17167     },
17168
17169     _build: function (items, left, right, height) {
17170
17171         var N = right - left + 1,
17172             M = this._maxEntries,
17173             node;
17174
17175         if (N <= M) {
17176             // reached leaf level; return leaf
17177             node = {
17178                 children: items.slice(left, right + 1),
17179                 height: 1,
17180                 bbox: null,
17181                 leaf: true
17182             };
17183             calcBBox(node, this.toBBox);
17184             return node;
17185         }
17186
17187         if (!height) {
17188             // target height of the bulk-loaded tree
17189             height = Math.ceil(Math.log(N) / Math.log(M));
17190
17191             // target number of root entries to maximize storage utilization
17192             M = Math.ceil(N / Math.pow(M, height - 1));
17193         }
17194
17195         node = {
17196             children: [],
17197             height: height,
17198             bbox: null,
17199             leaf: false
17200         };
17201
17202         // split the items into M mostly square tiles
17203
17204         var N2 = Math.ceil(N / M),
17205             N1 = N2 * Math.ceil(Math.sqrt(M)),
17206             i, j, right2, right3;
17207
17208         multiSelect(items, left, right, N1, this.compareMinX);
17209
17210         for (i = left; i <= right; i += N1) {
17211
17212             right2 = Math.min(i + N1 - 1, right);
17213
17214             multiSelect(items, i, right2, N2, this.compareMinY);
17215
17216             for (j = i; j <= right2; j += N2) {
17217
17218                 right3 = Math.min(j + N2 - 1, right2);
17219
17220                 // pack each entry recursively
17221                 node.children.push(this._build(items, j, right3, height - 1));
17222             }
17223         }
17224
17225         calcBBox(node, this.toBBox);
17226
17227         return node;
17228     },
17229
17230     _chooseSubtree: function (bbox, node, level, path) {
17231
17232         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
17233
17234         while (true) {
17235             path.push(node);
17236
17237             if (node.leaf || path.length - 1 === level) break;
17238
17239             minArea = minEnlargement = Infinity;
17240
17241             for (i = 0, len = node.children.length; i < len; i++) {
17242                 child = node.children[i];
17243                 area = bboxArea(child.bbox);
17244                 enlargement = enlargedArea(bbox, child.bbox) - area;
17245
17246                 // choose entry with the least area enlargement
17247                 if (enlargement < minEnlargement) {
17248                     minEnlargement = enlargement;
17249                     minArea = area < minArea ? area : minArea;
17250                     targetNode = child;
17251
17252                 } else if (enlargement === minEnlargement) {
17253                     // otherwise choose one with the smallest area
17254                     if (area < minArea) {
17255                         minArea = area;
17256                         targetNode = child;
17257                     }
17258                 }
17259             }
17260
17261             node = targetNode;
17262         }
17263
17264         return node;
17265     },
17266
17267     _insert: function (item, level, isNode) {
17268
17269         var toBBox = this.toBBox,
17270             bbox = isNode ? item.bbox : toBBox(item),
17271             insertPath = [];
17272
17273         // find the best node for accommodating the item, saving all nodes along the path too
17274         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
17275
17276         // put the item into the node
17277         node.children.push(item);
17278         extend(node.bbox, bbox);
17279
17280         // split on node overflow; propagate upwards if necessary
17281         while (level >= 0) {
17282             if (insertPath[level].children.length > this._maxEntries) {
17283                 this._split(insertPath, level);
17284                 level--;
17285             } else break;
17286         }
17287
17288         // adjust bboxes along the insertion path
17289         this._adjustParentBBoxes(bbox, insertPath, level);
17290     },
17291
17292     // split overflowed node into two
17293     _split: function (insertPath, level) {
17294
17295         var node = insertPath[level],
17296             M = node.children.length,
17297             m = this._minEntries;
17298
17299         this._chooseSplitAxis(node, m, M);
17300
17301         var splitIndex = this._chooseSplitIndex(node, m, M);
17302
17303         var newNode = {
17304             children: node.children.splice(splitIndex, node.children.length - splitIndex),
17305             height: node.height,
17306             bbox: null,
17307             leaf: false
17308         };
17309
17310         if (node.leaf) newNode.leaf = true;
17311
17312         calcBBox(node, this.toBBox);
17313         calcBBox(newNode, this.toBBox);
17314
17315         if (level) insertPath[level - 1].children.push(newNode);
17316         else this._splitRoot(node, newNode);
17317     },
17318
17319     _splitRoot: function (node, newNode) {
17320         // split root node
17321         this.data = {
17322             children: [node, newNode],
17323             height: node.height + 1,
17324             bbox: null,
17325             leaf: false
17326         };
17327         calcBBox(this.data, this.toBBox);
17328     },
17329
17330     _chooseSplitIndex: function (node, m, M) {
17331
17332         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
17333
17334         minOverlap = minArea = Infinity;
17335
17336         for (i = m; i <= M - m; i++) {
17337             bbox1 = distBBox(node, 0, i, this.toBBox);
17338             bbox2 = distBBox(node, i, M, this.toBBox);
17339
17340             overlap = intersectionArea(bbox1, bbox2);
17341             area = bboxArea(bbox1) + bboxArea(bbox2);
17342
17343             // choose distribution with minimum overlap
17344             if (overlap < minOverlap) {
17345                 minOverlap = overlap;
17346                 index = i;
17347
17348                 minArea = area < minArea ? area : minArea;
17349
17350             } else if (overlap === minOverlap) {
17351                 // otherwise choose distribution with minimum area
17352                 if (area < minArea) {
17353                     minArea = area;
17354                     index = i;
17355                 }
17356             }
17357         }
17358
17359         return index;
17360     },
17361
17362     // sorts node children by the best axis for split
17363     _chooseSplitAxis: function (node, m, M) {
17364
17365         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
17366             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
17367             xMargin = this._allDistMargin(node, m, M, compareMinX),
17368             yMargin = this._allDistMargin(node, m, M, compareMinY);
17369
17370         // if total distributions margin value is minimal for x, sort by minX,
17371         // otherwise it's already sorted by minY
17372         if (xMargin < yMargin) node.children.sort(compareMinX);
17373     },
17374
17375     // total margin of all possible split distributions where each node is at least m full
17376     _allDistMargin: function (node, m, M, compare) {
17377
17378         node.children.sort(compare);
17379
17380         var toBBox = this.toBBox,
17381             leftBBox = distBBox(node, 0, m, toBBox),
17382             rightBBox = distBBox(node, M - m, M, toBBox),
17383             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
17384             i, child;
17385
17386         for (i = m; i < M - m; i++) {
17387             child = node.children[i];
17388             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
17389             margin += bboxMargin(leftBBox);
17390         }
17391
17392         for (i = M - m - 1; i >= m; i--) {
17393             child = node.children[i];
17394             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
17395             margin += bboxMargin(rightBBox);
17396         }
17397
17398         return margin;
17399     },
17400
17401     _adjustParentBBoxes: function (bbox, path, level) {
17402         // adjust bboxes along the given tree path
17403         for (var i = level; i >= 0; i--) {
17404             extend(path[i].bbox, bbox);
17405         }
17406     },
17407
17408     _condense: function (path) {
17409         // go through the path, removing empty nodes and updating bboxes
17410         for (var i = path.length - 1, siblings; i >= 0; i--) {
17411             if (path[i].children.length === 0) {
17412                 if (i > 0) {
17413                     siblings = path[i - 1].children;
17414                     siblings.splice(siblings.indexOf(path[i]), 1);
17415
17416                 } else this.clear();
17417
17418             } else calcBBox(path[i], this.toBBox);
17419         }
17420     },
17421
17422     _initFormat: function (format) {
17423         // data format (minX, minY, maxX, maxY accessors)
17424
17425         // uses eval-type function compilation instead of just accepting a toBBox function
17426         // because the algorithms are very sensitive to sorting functions performance,
17427         // so they should be dead simple and without inner calls
17428
17429         // jshint evil: true
17430
17431         var compareArr = ['return a', ' - b', ';'];
17432
17433         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
17434         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
17435
17436         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
17437     }
17438 };
17439
17440
17441 // calculate node's bbox from bboxes of its children
17442 function calcBBox(node, toBBox) {
17443     node.bbox = distBBox(node, 0, node.children.length, toBBox);
17444 }
17445
17446 // min bounding rectangle of node children from k to p-1
17447 function distBBox(node, k, p, toBBox) {
17448     var bbox = empty();
17449
17450     for (var i = k, child; i < p; i++) {
17451         child = node.children[i];
17452         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
17453     }
17454
17455     return bbox;
17456 }
17457
17458 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
17459
17460 function extend(a, b) {
17461     a[0] = Math.min(a[0], b[0]);
17462     a[1] = Math.min(a[1], b[1]);
17463     a[2] = Math.max(a[2], b[2]);
17464     a[3] = Math.max(a[3], b[3]);
17465     return a;
17466 }
17467
17468 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
17469 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
17470
17471 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
17472 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
17473
17474 function enlargedArea(a, b) {
17475     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
17476            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
17477 }
17478
17479 function intersectionArea(a, b) {
17480     var minX = Math.max(a[0], b[0]),
17481         minY = Math.max(a[1], b[1]),
17482         maxX = Math.min(a[2], b[2]),
17483         maxY = Math.min(a[3], b[3]);
17484
17485     return Math.max(0, maxX - minX) *
17486            Math.max(0, maxY - minY);
17487 }
17488
17489 function contains(a, b) {
17490     return a[0] <= b[0] &&
17491            a[1] <= b[1] &&
17492            b[2] <= a[2] &&
17493            b[3] <= a[3];
17494 }
17495
17496 function intersects(a, b) {
17497     return b[0] <= a[2] &&
17498            b[1] <= a[3] &&
17499            b[2] >= a[0] &&
17500            b[3] >= a[1];
17501 }
17502
17503 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
17504 // combines selection algorithm with binary divide & conquer approach
17505
17506 function multiSelect(arr, left, right, n, compare) {
17507     var stack = [left, right],
17508         mid;
17509
17510     while (stack.length) {
17511         right = stack.pop();
17512         left = stack.pop();
17513
17514         if (right - left <= n) continue;
17515
17516         mid = left + Math.ceil((right - left) / n / 2) * n;
17517         select(arr, left, right, mid, compare);
17518
17519         stack.push(left, mid, mid, right);
17520     }
17521 }
17522
17523 // Floyd-Rivest selection algorithm:
17524 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
17525 function select(arr, left, right, k, compare) {
17526     var n, i, z, s, sd, newLeft, newRight, t, j;
17527
17528     while (right > left) {
17529         if (right - left > 600) {
17530             n = right - left + 1;
17531             i = k - left + 1;
17532             z = Math.log(n);
17533             s = 0.5 * Math.exp(2 * z / 3);
17534             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
17535             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
17536             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
17537             select(arr, newLeft, newRight, k, compare);
17538         }
17539
17540         t = arr[k];
17541         i = left;
17542         j = right;
17543
17544         swap(arr, left, k);
17545         if (compare(arr[right], t) > 0) swap(arr, left, right);
17546
17547         while (i < j) {
17548             swap(arr, i, j);
17549             i++;
17550             j--;
17551             while (compare(arr[i], t) < 0) i++;
17552             while (compare(arr[j], t) > 0) j--;
17553         }
17554
17555         if (compare(arr[left], t) === 0) swap(arr, left, j);
17556         else {
17557             j++;
17558             swap(arr, j, right);
17559         }
17560
17561         if (j <= k) left = j + 1;
17562         if (k <= j) right = j - 1;
17563     }
17564 }
17565
17566 function swap(arr, i, j) {
17567     var tmp = arr[i];
17568     arr[i] = arr[j];
17569     arr[j] = tmp;
17570 }
17571
17572
17573 // export as AMD/CommonJS module or global variable
17574 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
17575 else if (typeof module !== 'undefined') module.exports = rbush;
17576 else if (typeof self !== 'undefined') self.rbush = rbush;
17577 else window.rbush = rbush;
17578
17579 })();
17580 (function(e){if("function"==typeof bootstrap)bootstrap("sexagesimal",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeSexagesimal=e}else"undefined"!=typeof window?window.sexagesimal=e():global.sexagesimal=e()})(function(){var define,ses,bootstrap,module,exports;
17581 return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
17582 module.exports = element;
17583 module.exports.pair = pair;
17584 module.exports.format = format;
17585 module.exports.formatPair = formatPair;
17586
17587 function element(x, dims) {
17588     return search(x, dims).val;
17589 }
17590
17591 function formatPair(x) {
17592     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
17593 }
17594
17595 // Is 0 North or South?
17596 function format(x, dim) {
17597     var dirs = {
17598             lat: ['N', 'S'],
17599             lon: ['E', 'W']
17600         }[dim] || '',
17601         dir = dirs[x >= 0 ? 0 : 1],
17602         abs = Math.abs(x),
17603         whole = Math.floor(abs),
17604         fraction = abs - whole,
17605         fractionMinutes = fraction * 60,
17606         minutes = Math.floor(fractionMinutes),
17607         seconds = Math.floor((fractionMinutes - minutes) * 60);
17608
17609     return whole + '° ' +
17610         (minutes ? minutes + "' " : '') +
17611         (seconds ? seconds + '" ' : '') + dir;
17612 }
17613
17614 function search(x, dims, r) {
17615     if (!dims) dims = 'NSEW';
17616     if (typeof x !== 'string') return { val: null, regex: r };
17617     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
17618     var m = r.exec(x);
17619     if (!m) return { val: null, regex: r };
17620     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
17621     else return {
17622         val: (((m[1]) ? parseFloat(m[1]) : 0) +
17623             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
17624             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
17625             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
17626         regex: r,
17627         raw: m[0],
17628         dim: m[4]
17629     };
17630 }
17631
17632 function pair(x, dims) {
17633     x = x.trim();
17634     var one = search(x, dims);
17635     if (one.val === null) return null;
17636     var two = search(x, dims, one.regex);
17637     if (two.val === null) return null;
17638     // null if one/two are not contiguous.
17639     if (one.raw + two.raw !== x) return null;
17640     if (one.dim) return swapdim(one.val, two.val, one.dim);
17641     else return [one.val, two.val];
17642 }
17643
17644 function swapdim(a, b, dim) {
17645     if (dim == 'N' || dim == 'S') return [a, b];
17646     if (dim == 'W' || dim == 'E') return [b, a];
17647 }
17648
17649 },{}]},{},[1])
17650 (1)
17651 });
17652 ;toGeoJSON = (function() {
17653     'use strict';
17654
17655     var removeSpace = (/\s*/g),
17656         trimSpace = (/^\s*|\s*$/g),
17657         splitSpace = (/\s+/);
17658     // generate a short, numeric hash of a string
17659     function okhash(x) {
17660         if (!x || !x.length) return 0;
17661         for (var i = 0, h = 0; i < x.length; i++) {
17662             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
17663         } return h;
17664     }
17665     // all Y children of X
17666     function get(x, y) { return x.getElementsByTagName(y); }
17667     function attr(x, y) { return x.getAttribute(y); }
17668     function attrf(x, y) { return parseFloat(attr(x, y)); }
17669     // one Y child of X, if any, otherwise null
17670     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
17671     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
17672     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
17673     // cast array x into numbers
17674     function numarray(x) {
17675         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
17676         return o;
17677     }
17678     function clean(x) {
17679         var o = {};
17680         for (var i in x) if (x[i]) o[i] = x[i];
17681         return o;
17682     }
17683     // get the content of a text node, if any
17684     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
17685     // get one coordinate from a coordinate array, if any
17686     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
17687     // get all coordinates from a coordinate array as [[],[]]
17688     function coord(v) {
17689         var coords = v.replace(trimSpace, '').split(splitSpace),
17690             o = [];
17691         for (var i = 0; i < coords.length; i++) {
17692             o.push(coord1(coords[i]));
17693         }
17694         return o;
17695     }
17696     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
17697
17698     // create a new feature collection parent object
17699     function fc() {
17700         return {
17701             type: 'FeatureCollection',
17702             features: []
17703         };
17704     }
17705
17706     var styleSupport = false;
17707     if (typeof XMLSerializer !== 'undefined') {
17708         var serializer = new XMLSerializer();
17709         styleSupport = true;
17710     }
17711     function xml2str(str) { return serializer.serializeToString(str); }
17712
17713     var t = {
17714         kml: function(doc, o) {
17715             o = o || {};
17716
17717             var gj = fc(),
17718                 // styleindex keeps track of hashed styles in order to match features
17719                 styleIndex = {},
17720                 // atomic geospatial types supported by KML - MultiGeometry is
17721                 // handled separately
17722                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
17723                 // all root placemarks in the file
17724                 placemarks = get(doc, 'Placemark'),
17725                 styles = get(doc, 'Style');
17726
17727             if (styleSupport) for (var k = 0; k < styles.length; k++) {
17728                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
17729             }
17730             for (var j = 0; j < placemarks.length; j++) {
17731                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
17732             }
17733             function gxCoord(v) { return numarray(v.split(' ')); }
17734             function gxCoords(root) {
17735                 var elems = get(root, 'coord', 'gx'), coords = [];
17736                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
17737                 return coords;
17738             }
17739             function getGeometry(root) {
17740                 var geomNode, geomNodes, i, j, k, geoms = [];
17741                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
17742                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
17743                 for (i = 0; i < geotypes.length; i++) {
17744                     geomNodes = get(root, geotypes[i]);
17745                     if (geomNodes) {
17746                         for (j = 0; j < geomNodes.length; j++) {
17747                             geomNode = geomNodes[j];
17748                             if (geotypes[i] == 'Point') {
17749                                 geoms.push({
17750                                     type: 'Point',
17751                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
17752                                 });
17753                             } else if (geotypes[i] == 'LineString') {
17754                                 geoms.push({
17755                                     type: 'LineString',
17756                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
17757                                 });
17758                             } else if (geotypes[i] == 'Polygon') {
17759                                 var rings = get(geomNode, 'LinearRing'),
17760                                     coords = [];
17761                                 for (k = 0; k < rings.length; k++) {
17762                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
17763                                 }
17764                                 geoms.push({
17765                                     type: 'Polygon',
17766                                     coordinates: coords
17767                                 });
17768                             } else if (geotypes[i] == 'Track') {
17769                                 geoms.push({
17770                                     type: 'LineString',
17771                                     coordinates: gxCoords(geomNode)
17772                                 });
17773                             }
17774                         }
17775                     }
17776                 }
17777                 return geoms;
17778             }
17779             function getPlacemark(root) {
17780                 var geoms = getGeometry(root), i, properties = {},
17781                     name = nodeVal(get1(root, 'name')),
17782                     styleUrl = nodeVal(get1(root, 'styleUrl')),
17783                     description = nodeVal(get1(root, 'description')),
17784                     extendedData = get1(root, 'ExtendedData');
17785
17786                 if (!geoms.length) return [];
17787                 if (name) properties.name = name;
17788                 if (styleUrl && styleIndex[styleUrl]) {
17789                     properties.styleUrl = styleUrl;
17790                     properties.styleHash = styleIndex[styleUrl];
17791                 }
17792                 if (description) properties.description = description;
17793                 if (extendedData) {
17794                     var datas = get(extendedData, 'Data'),
17795                         simpleDatas = get(extendedData, 'SimpleData');
17796
17797                     for (i = 0; i < datas.length; i++) {
17798                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
17799                     }
17800                     for (i = 0; i < simpleDatas.length; i++) {
17801                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
17802                     }
17803                 }
17804                 return [{
17805                     type: 'Feature',
17806                     geometry: (geoms.length === 1) ? geoms[0] : {
17807                         type: 'GeometryCollection',
17808                         geometries: geoms
17809                     },
17810                     properties: properties
17811                 }];
17812             }
17813             return gj;
17814         },
17815         gpx: function(doc, o) {
17816             var i,
17817                 tracks = get(doc, 'trk'),
17818                 routes = get(doc, 'rte'),
17819                 waypoints = get(doc, 'wpt'),
17820                 // a feature collection
17821                 gj = fc();
17822             for (i = 0; i < tracks.length; i++) {
17823                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
17824             }
17825             for (i = 0; i < routes.length; i++) {
17826                 gj.features.push(getLinestring(routes[i], 'rtept'));
17827             }
17828             for (i = 0; i < waypoints.length; i++) {
17829                 gj.features.push(getPoint(waypoints[i]));
17830             }
17831             function getLinestring(node, pointname) {
17832                 var j, pts = get(node, pointname), line = [];
17833                 for (j = 0; j < pts.length; j++) {
17834                     line.push(coordPair(pts[j]));
17835                 }
17836                 return {
17837                     type: 'Feature',
17838                     properties: getProperties(node),
17839                     geometry: {
17840                         type: 'LineString',
17841                         coordinates: line
17842                     }
17843                 };
17844             }
17845             function getPoint(node) {
17846                 var prop = getProperties(node);
17847                 prop.ele = nodeVal(get1(node, 'ele'));
17848                 prop.sym = nodeVal(get1(node, 'sym'));
17849                 return {
17850                     type: 'Feature',
17851                     properties: prop,
17852                     geometry: {
17853                         type: 'Point',
17854                         coordinates: coordPair(node)
17855                     }
17856                 };
17857             }
17858             function getProperties(node) {
17859                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
17860                             'time', 'keywords'],
17861                     prop = {},
17862                     k;
17863                 for (k = 0; k < meta.length; k++) {
17864                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
17865                 }
17866                 return clean(prop);
17867             }
17868             return gj;
17869         }
17870     };
17871     return t;
17872 })();
17873
17874 if (typeof module !== 'undefined') module.exports = toGeoJSON;
17875 /**
17876  * marked - a markdown parser
17877  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
17878  * https://github.com/chjj/marked
17879  */
17880
17881 ;(function() {
17882
17883 /**
17884  * Block-Level Grammar
17885  */
17886
17887 var block = {
17888   newline: /^\n+/,
17889   code: /^( {4}[^\n]+\n*)+/,
17890   fences: noop,
17891   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17892   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17893   nptable: noop,
17894   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
17895   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
17896   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17897   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
17898   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17899   table: noop,
17900   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17901   text: /^[^\n]+/
17902 };
17903
17904 block.bullet = /(?:[*+-]|\d+\.)/;
17905 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17906 block.item = replace(block.item, 'gm')
17907   (/bull/g, block.bullet)
17908   ();
17909
17910 block.list = replace(block.list)
17911   (/bull/g, block.bullet)
17912   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
17913   ();
17914
17915 block._tag = '(?!(?:'
17916   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17917   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17918   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
17919
17920 block.html = replace(block.html)
17921   ('comment', /<!--[\s\S]*?-->/)
17922   ('closed', /<(tag)[\s\S]+?<\/\1>/)
17923   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17924   (/tag/g, block._tag)
17925   ();
17926
17927 block.paragraph = replace(block.paragraph)
17928   ('hr', block.hr)
17929   ('heading', block.heading)
17930   ('lheading', block.lheading)
17931   ('blockquote', block.blockquote)
17932   ('tag', '<' + block._tag)
17933   ('def', block.def)
17934   ();
17935
17936 /**
17937  * Normal Block Grammar
17938  */
17939
17940 block.normal = merge({}, block);
17941
17942 /**
17943  * GFM Block Grammar
17944  */
17945
17946 block.gfm = merge({}, block.normal, {
17947   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
17948   paragraph: /^/
17949 });
17950
17951 block.gfm.paragraph = replace(block.paragraph)
17952   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
17953   ();
17954
17955 /**
17956  * GFM + Tables Block Grammar
17957  */
17958
17959 block.tables = merge({}, block.gfm, {
17960   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17961   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17962 });
17963
17964 /**
17965  * Block Lexer
17966  */
17967
17968 function Lexer(options) {
17969   this.tokens = [];
17970   this.tokens.links = {};
17971   this.options = options || marked.defaults;
17972   this.rules = block.normal;
17973
17974   if (this.options.gfm) {
17975     if (this.options.tables) {
17976       this.rules = block.tables;
17977     } else {
17978       this.rules = block.gfm;
17979     }
17980   }
17981 }
17982
17983 /**
17984  * Expose Block Rules
17985  */
17986
17987 Lexer.rules = block;
17988
17989 /**
17990  * Static Lex Method
17991  */
17992
17993 Lexer.lex = function(src, options) {
17994   var lexer = new Lexer(options);
17995   return lexer.lex(src);
17996 };
17997
17998 /**
17999  * Preprocessing
18000  */
18001
18002 Lexer.prototype.lex = function(src) {
18003   src = src
18004     .replace(/\r\n|\r/g, '\n')
18005     .replace(/\t/g, '    ')
18006     .replace(/\u00a0/g, ' ')
18007     .replace(/\u2424/g, '\n');
18008
18009   return this.token(src, true);
18010 };
18011
18012 /**
18013  * Lexing
18014  */
18015
18016 Lexer.prototype.token = function(src, top) {
18017   var src = src.replace(/^ +$/gm, '')
18018     , next
18019     , loose
18020     , cap
18021     , bull
18022     , b
18023     , item
18024     , space
18025     , i
18026     , l;
18027
18028   while (src) {
18029     // newline
18030     if (cap = this.rules.newline.exec(src)) {
18031       src = src.substring(cap[0].length);
18032       if (cap[0].length > 1) {
18033         this.tokens.push({
18034           type: 'space'
18035         });
18036       }
18037     }
18038
18039     // code
18040     if (cap = this.rules.code.exec(src)) {
18041       src = src.substring(cap[0].length);
18042       cap = cap[0].replace(/^ {4}/gm, '');
18043       this.tokens.push({
18044         type: 'code',
18045         text: !this.options.pedantic
18046           ? cap.replace(/\n+$/, '')
18047           : cap
18048       });
18049       continue;
18050     }
18051
18052     // fences (gfm)
18053     if (cap = this.rules.fences.exec(src)) {
18054       src = src.substring(cap[0].length);
18055       this.tokens.push({
18056         type: 'code',
18057         lang: cap[2],
18058         text: cap[3]
18059       });
18060       continue;
18061     }
18062
18063     // heading
18064     if (cap = this.rules.heading.exec(src)) {
18065       src = src.substring(cap[0].length);
18066       this.tokens.push({
18067         type: 'heading',
18068         depth: cap[1].length,
18069         text: cap[2]
18070       });
18071       continue;
18072     }
18073
18074     // table no leading pipe (gfm)
18075     if (top && (cap = this.rules.nptable.exec(src))) {
18076       src = src.substring(cap[0].length);
18077
18078       item = {
18079         type: 'table',
18080         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18081         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18082         cells: cap[3].replace(/\n$/, '').split('\n')
18083       };
18084
18085       for (i = 0; i < item.align.length; i++) {
18086         if (/^ *-+: *$/.test(item.align[i])) {
18087           item.align[i] = 'right';
18088         } else if (/^ *:-+: *$/.test(item.align[i])) {
18089           item.align[i] = 'center';
18090         } else if (/^ *:-+ *$/.test(item.align[i])) {
18091           item.align[i] = 'left';
18092         } else {
18093           item.align[i] = null;
18094         }
18095       }
18096
18097       for (i = 0; i < item.cells.length; i++) {
18098         item.cells[i] = item.cells[i].split(/ *\| */);
18099       }
18100
18101       this.tokens.push(item);
18102
18103       continue;
18104     }
18105
18106     // lheading
18107     if (cap = this.rules.lheading.exec(src)) {
18108       src = src.substring(cap[0].length);
18109       this.tokens.push({
18110         type: 'heading',
18111         depth: cap[2] === '=' ? 1 : 2,
18112         text: cap[1]
18113       });
18114       continue;
18115     }
18116
18117     // hr
18118     if (cap = this.rules.hr.exec(src)) {
18119       src = src.substring(cap[0].length);
18120       this.tokens.push({
18121         type: 'hr'
18122       });
18123       continue;
18124     }
18125
18126     // blockquote
18127     if (cap = this.rules.blockquote.exec(src)) {
18128       src = src.substring(cap[0].length);
18129
18130       this.tokens.push({
18131         type: 'blockquote_start'
18132       });
18133
18134       cap = cap[0].replace(/^ *> ?/gm, '');
18135
18136       // Pass `top` to keep the current
18137       // "toplevel" state. This is exactly
18138       // how markdown.pl works.
18139       this.token(cap, top);
18140
18141       this.tokens.push({
18142         type: 'blockquote_end'
18143       });
18144
18145       continue;
18146     }
18147
18148     // list
18149     if (cap = this.rules.list.exec(src)) {
18150       src = src.substring(cap[0].length);
18151       bull = cap[2];
18152
18153       this.tokens.push({
18154         type: 'list_start',
18155         ordered: bull.length > 1
18156       });
18157
18158       // Get each top-level item.
18159       cap = cap[0].match(this.rules.item);
18160
18161       next = false;
18162       l = cap.length;
18163       i = 0;
18164
18165       for (; i < l; i++) {
18166         item = cap[i];
18167
18168         // Remove the list item's bullet
18169         // so it is seen as the next token.
18170         space = item.length;
18171         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18172
18173         // Outdent whatever the
18174         // list item contains. Hacky.
18175         if (~item.indexOf('\n ')) {
18176           space -= item.length;
18177           item = !this.options.pedantic
18178             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18179             : item.replace(/^ {1,4}/gm, '');
18180         }
18181
18182         // Determine whether the next list item belongs here.
18183         // Backpedal if it does not belong in this list.
18184         if (this.options.smartLists && i !== l - 1) {
18185           b = block.bullet.exec(cap[i+1])[0];
18186           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18187             src = cap.slice(i + 1).join('\n') + src;
18188             i = l - 1;
18189           }
18190         }
18191
18192         // Determine whether item is loose or not.
18193         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18194         // for discount behavior.
18195         loose = next || /\n\n(?!\s*$)/.test(item);
18196         if (i !== l - 1) {
18197           next = item[item.length-1] === '\n';
18198           if (!loose) loose = next;
18199         }
18200
18201         this.tokens.push({
18202           type: loose
18203             ? 'loose_item_start'
18204             : 'list_item_start'
18205         });
18206
18207         // Recurse.
18208         this.token(item, false);
18209
18210         this.tokens.push({
18211           type: 'list_item_end'
18212         });
18213       }
18214
18215       this.tokens.push({
18216         type: 'list_end'
18217       });
18218
18219       continue;
18220     }
18221
18222     // html
18223     if (cap = this.rules.html.exec(src)) {
18224       src = src.substring(cap[0].length);
18225       this.tokens.push({
18226         type: this.options.sanitize
18227           ? 'paragraph'
18228           : 'html',
18229         pre: cap[1] === 'pre' || cap[1] === 'script',
18230         text: cap[0]
18231       });
18232       continue;
18233     }
18234
18235     // def
18236     if (top && (cap = this.rules.def.exec(src))) {
18237       src = src.substring(cap[0].length);
18238       this.tokens.links[cap[1].toLowerCase()] = {
18239         href: cap[2],
18240         title: cap[3]
18241       };
18242       continue;
18243     }
18244
18245     // table (gfm)
18246     if (top && (cap = this.rules.table.exec(src))) {
18247       src = src.substring(cap[0].length);
18248
18249       item = {
18250         type: 'table',
18251         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18252         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18253         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18254       };
18255
18256       for (i = 0; i < item.align.length; i++) {
18257         if (/^ *-+: *$/.test(item.align[i])) {
18258           item.align[i] = 'right';
18259         } else if (/^ *:-+: *$/.test(item.align[i])) {
18260           item.align[i] = 'center';
18261         } else if (/^ *:-+ *$/.test(item.align[i])) {
18262           item.align[i] = 'left';
18263         } else {
18264           item.align[i] = null;
18265         }
18266       }
18267
18268       for (i = 0; i < item.cells.length; i++) {
18269         item.cells[i] = item.cells[i]
18270           .replace(/^ *\| *| *\| *$/g, '')
18271           .split(/ *\| */);
18272       }
18273
18274       this.tokens.push(item);
18275
18276       continue;
18277     }
18278
18279     // top-level paragraph
18280     if (top && (cap = this.rules.paragraph.exec(src))) {
18281       src = src.substring(cap[0].length);
18282       this.tokens.push({
18283         type: 'paragraph',
18284         text: cap[1][cap[1].length-1] === '\n'
18285           ? cap[1].slice(0, -1)
18286           : cap[1]
18287       });
18288       continue;
18289     }
18290
18291     // text
18292     if (cap = this.rules.text.exec(src)) {
18293       // Top-level should never reach here.
18294       src = src.substring(cap[0].length);
18295       this.tokens.push({
18296         type: 'text',
18297         text: cap[0]
18298       });
18299       continue;
18300     }
18301
18302     if (src) {
18303       throw new
18304         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18305     }
18306   }
18307
18308   return this.tokens;
18309 };
18310
18311 /**
18312  * Inline-Level Grammar
18313  */
18314
18315 var inline = {
18316   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18317   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18318   url: noop,
18319   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18320   link: /^!?\[(inside)\]\(href\)/,
18321   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18322   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18323   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18324   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18325   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18326   br: /^ {2,}\n(?!\s*$)/,
18327   del: noop,
18328   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18329 };
18330
18331 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
18332 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18333
18334 inline.link = replace(inline.link)
18335   ('inside', inline._inside)
18336   ('href', inline._href)
18337   ();
18338
18339 inline.reflink = replace(inline.reflink)
18340   ('inside', inline._inside)
18341   ();
18342
18343 /**
18344  * Normal Inline Grammar
18345  */
18346
18347 inline.normal = merge({}, inline);
18348
18349 /**
18350  * Pedantic Inline Grammar
18351  */
18352
18353 inline.pedantic = merge({}, inline.normal, {
18354   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18355   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18356 });
18357
18358 /**
18359  * GFM Inline Grammar
18360  */
18361
18362 inline.gfm = merge({}, inline.normal, {
18363   escape: replace(inline.escape)('])', '~|])')(),
18364   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18365   del: /^~~(?=\S)([\s\S]*?\S)~~/,
18366   text: replace(inline.text)
18367     (']|', '~]|')
18368     ('|', '|https?://|')
18369     ()
18370 });
18371
18372 /**
18373  * GFM + Line Breaks Inline Grammar
18374  */
18375
18376 inline.breaks = merge({}, inline.gfm, {
18377   br: replace(inline.br)('{2,}', '*')(),
18378   text: replace(inline.gfm.text)('{2,}', '*')()
18379 });
18380
18381 /**
18382  * Inline Lexer & Compiler
18383  */
18384
18385 function InlineLexer(links, options) {
18386   this.options = options || marked.defaults;
18387   this.links = links;
18388   this.rules = inline.normal;
18389
18390   if (!this.links) {
18391     throw new
18392       Error('Tokens array requires a `links` property.');
18393   }
18394
18395   if (this.options.gfm) {
18396     if (this.options.breaks) {
18397       this.rules = inline.breaks;
18398     } else {
18399       this.rules = inline.gfm;
18400     }
18401   } else if (this.options.pedantic) {
18402     this.rules = inline.pedantic;
18403   }
18404 }
18405
18406 /**
18407  * Expose Inline Rules
18408  */
18409
18410 InlineLexer.rules = inline;
18411
18412 /**
18413  * Static Lexing/Compiling Method
18414  */
18415
18416 InlineLexer.output = function(src, links, options) {
18417   var inline = new InlineLexer(links, options);
18418   return inline.output(src);
18419 };
18420
18421 /**
18422  * Lexing/Compiling
18423  */
18424
18425 InlineLexer.prototype.output = function(src) {
18426   var out = ''
18427     , link
18428     , text
18429     , href
18430     , cap;
18431
18432   while (src) {
18433     // escape
18434     if (cap = this.rules.escape.exec(src)) {
18435       src = src.substring(cap[0].length);
18436       out += cap[1];
18437       continue;
18438     }
18439
18440     // autolink
18441     if (cap = this.rules.autolink.exec(src)) {
18442       src = src.substring(cap[0].length);
18443       if (cap[2] === '@') {
18444         text = cap[1][6] === ':'
18445           ? this.mangle(cap[1].substring(7))
18446           : this.mangle(cap[1]);
18447         href = this.mangle('mailto:') + text;
18448       } else {
18449         text = escape(cap[1]);
18450         href = text;
18451       }
18452       out += '<a href="'
18453         + href
18454         + '">'
18455         + text
18456         + '</a>';
18457       continue;
18458     }
18459
18460     // url (gfm)
18461     if (cap = this.rules.url.exec(src)) {
18462       src = src.substring(cap[0].length);
18463       text = escape(cap[1]);
18464       href = text;
18465       out += '<a href="'
18466         + href
18467         + '">'
18468         + text
18469         + '</a>';
18470       continue;
18471     }
18472
18473     // tag
18474     if (cap = this.rules.tag.exec(src)) {
18475       src = src.substring(cap[0].length);
18476       out += this.options.sanitize
18477         ? escape(cap[0])
18478         : cap[0];
18479       continue;
18480     }
18481
18482     // link
18483     if (cap = this.rules.link.exec(src)) {
18484       src = src.substring(cap[0].length);
18485       out += this.outputLink(cap, {
18486         href: cap[2],
18487         title: cap[3]
18488       });
18489       continue;
18490     }
18491
18492     // reflink, nolink
18493     if ((cap = this.rules.reflink.exec(src))
18494         || (cap = this.rules.nolink.exec(src))) {
18495       src = src.substring(cap[0].length);
18496       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18497       link = this.links[link.toLowerCase()];
18498       if (!link || !link.href) {
18499         out += cap[0][0];
18500         src = cap[0].substring(1) + src;
18501         continue;
18502       }
18503       out += this.outputLink(cap, link);
18504       continue;
18505     }
18506
18507     // strong
18508     if (cap = this.rules.strong.exec(src)) {
18509       src = src.substring(cap[0].length);
18510       out += '<strong>'
18511         + this.output(cap[2] || cap[1])
18512         + '</strong>';
18513       continue;
18514     }
18515
18516     // em
18517     if (cap = this.rules.em.exec(src)) {
18518       src = src.substring(cap[0].length);
18519       out += '<em>'
18520         + this.output(cap[2] || cap[1])
18521         + '</em>';
18522       continue;
18523     }
18524
18525     // code
18526     if (cap = this.rules.code.exec(src)) {
18527       src = src.substring(cap[0].length);
18528       out += '<code>'
18529         + escape(cap[2], true)
18530         + '</code>';
18531       continue;
18532     }
18533
18534     // br
18535     if (cap = this.rules.br.exec(src)) {
18536       src = src.substring(cap[0].length);
18537       out += '<br>';
18538       continue;
18539     }
18540
18541     // del (gfm)
18542     if (cap = this.rules.del.exec(src)) {
18543       src = src.substring(cap[0].length);
18544       out += '<del>'
18545         + this.output(cap[1])
18546         + '</del>';
18547       continue;
18548     }
18549
18550     // text
18551     if (cap = this.rules.text.exec(src)) {
18552       src = src.substring(cap[0].length);
18553       out += escape(cap[0]);
18554       continue;
18555     }
18556
18557     if (src) {
18558       throw new
18559         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18560     }
18561   }
18562
18563   return out;
18564 };
18565
18566 /**
18567  * Compile Link
18568  */
18569
18570 InlineLexer.prototype.outputLink = function(cap, link) {
18571   if (cap[0][0] !== '!') {
18572     return '<a href="'
18573       + escape(link.href)
18574       + '"'
18575       + (link.title
18576       ? ' title="'
18577       + escape(link.title)
18578       + '"'
18579       : '')
18580       + '>'
18581       + this.output(cap[1])
18582       + '</a>';
18583   } else {
18584     return '<img src="'
18585       + escape(link.href)
18586       + '" alt="'
18587       + escape(cap[1])
18588       + '"'
18589       + (link.title
18590       ? ' title="'
18591       + escape(link.title)
18592       + '"'
18593       : '')
18594       + '>';
18595   }
18596 };
18597
18598 /**
18599  * Smartypants Transformations
18600  */
18601
18602 InlineLexer.prototype.smartypants = function(text) {
18603   if (!this.options.smartypants) return text;
18604   return text
18605     .replace(/--/g, '—')
18606     .replace(/'([^']*)'/g, '‘$1’')
18607     .replace(/"([^"]*)"/g, '“$1”')
18608     .replace(/\.{3}/g, '…');
18609 };
18610
18611 /**
18612  * Mangle Links
18613  */
18614
18615 InlineLexer.prototype.mangle = function(text) {
18616   var out = ''
18617     , l = text.length
18618     , i = 0
18619     , ch;
18620
18621   for (; i < l; i++) {
18622     ch = text.charCodeAt(i);
18623     if (Math.random() > 0.5) {
18624       ch = 'x' + ch.toString(16);
18625     }
18626     out += '&#' + ch + ';';
18627   }
18628
18629   return out;
18630 };
18631
18632 /**
18633  * Parsing & Compiling
18634  */
18635
18636 function Parser(options) {
18637   this.tokens = [];
18638   this.token = null;
18639   this.options = options || marked.defaults;
18640 }
18641
18642 /**
18643  * Static Parse Method
18644  */
18645
18646 Parser.parse = function(src, options) {
18647   var parser = new Parser(options);
18648   return parser.parse(src);
18649 };
18650
18651 /**
18652  * Parse Loop
18653  */
18654
18655 Parser.prototype.parse = function(src) {
18656   this.inline = new InlineLexer(src.links, this.options);
18657   this.tokens = src.reverse();
18658
18659   var out = '';
18660   while (this.next()) {
18661     out += this.tok();
18662   }
18663
18664   return out;
18665 };
18666
18667 /**
18668  * Next Token
18669  */
18670
18671 Parser.prototype.next = function() {
18672   return this.token = this.tokens.pop();
18673 };
18674
18675 /**
18676  * Preview Next Token
18677  */
18678
18679 Parser.prototype.peek = function() {
18680   return this.tokens[this.tokens.length-1] || 0;
18681 };
18682
18683 /**
18684  * Parse Text Tokens
18685  */
18686
18687 Parser.prototype.parseText = function() {
18688   var body = this.token.text;
18689
18690   while (this.peek().type === 'text') {
18691     body += '\n' + this.next().text;
18692   }
18693
18694   return this.inline.output(body);
18695 };
18696
18697 /**
18698  * Parse Current Token
18699  */
18700
18701 Parser.prototype.tok = function() {
18702   switch (this.token.type) {
18703     case 'space': {
18704       return '';
18705     }
18706     case 'hr': {
18707       return '<hr>\n';
18708     }
18709     case 'heading': {
18710       return '<h'
18711         + this.token.depth
18712         + '>'
18713         + this.inline.output(this.token.text)
18714         + '</h'
18715         + this.token.depth
18716         + '>\n';
18717     }
18718     case 'code': {
18719       if (this.options.highlight) {
18720         var code = this.options.highlight(this.token.text, this.token.lang);
18721         if (code != null && code !== this.token.text) {
18722           this.token.escaped = true;
18723           this.token.text = code;
18724         }
18725       }
18726
18727       if (!this.token.escaped) {
18728         this.token.text = escape(this.token.text, true);
18729       }
18730
18731       return '<pre><code'
18732         + (this.token.lang
18733         ? ' class="'
18734         + this.options.langPrefix
18735         + this.token.lang
18736         + '"'
18737         : '')
18738         + '>'
18739         + this.token.text
18740         + '</code></pre>\n';
18741     }
18742     case 'table': {
18743       var body = ''
18744         , heading
18745         , i
18746         , row
18747         , cell
18748         , j;
18749
18750       // header
18751       body += '<thead>\n<tr>\n';
18752       for (i = 0; i < this.token.header.length; i++) {
18753         heading = this.inline.output(this.token.header[i]);
18754         body += this.token.align[i]
18755           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
18756           : '<th>' + heading + '</th>\n';
18757       }
18758       body += '</tr>\n</thead>\n';
18759
18760       // body
18761       body += '<tbody>\n'
18762       for (i = 0; i < this.token.cells.length; i++) {
18763         row = this.token.cells[i];
18764         body += '<tr>\n';
18765         for (j = 0; j < row.length; j++) {
18766           cell = this.inline.output(row[j]);
18767           body += this.token.align[j]
18768             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
18769             : '<td>' + cell + '</td>\n';
18770         }
18771         body += '</tr>\n';
18772       }
18773       body += '</tbody>\n';
18774
18775       return '<table>\n'
18776         + body
18777         + '</table>\n';
18778     }
18779     case 'blockquote_start': {
18780       var body = '';
18781
18782       while (this.next().type !== 'blockquote_end') {
18783         body += this.tok();
18784       }
18785
18786       return '<blockquote>\n'
18787         + body
18788         + '</blockquote>\n';
18789     }
18790     case 'list_start': {
18791       var type = this.token.ordered ? 'ol' : 'ul'
18792         , body = '';
18793
18794       while (this.next().type !== 'list_end') {
18795         body += this.tok();
18796       }
18797
18798       return '<'
18799         + type
18800         + '>\n'
18801         + body
18802         + '</'
18803         + type
18804         + '>\n';
18805     }
18806     case 'list_item_start': {
18807       var body = '';
18808
18809       while (this.next().type !== 'list_item_end') {
18810         body += this.token.type === 'text'
18811           ? this.parseText()
18812           : this.tok();
18813       }
18814
18815       return '<li>'
18816         + body
18817         + '</li>\n';
18818     }
18819     case 'loose_item_start': {
18820       var body = '';
18821
18822       while (this.next().type !== 'list_item_end') {
18823         body += this.tok();
18824       }
18825
18826       return '<li>'
18827         + body
18828         + '</li>\n';
18829     }
18830     case 'html': {
18831       return !this.token.pre && !this.options.pedantic
18832         ? this.inline.output(this.token.text)
18833         : this.token.text;
18834     }
18835     case 'paragraph': {
18836       return '<p>'
18837         + this.inline.output(this.token.text)
18838         + '</p>\n';
18839     }
18840     case 'text': {
18841       return '<p>'
18842         + this.parseText()
18843         + '</p>\n';
18844     }
18845   }
18846 };
18847
18848 /**
18849  * Helpers
18850  */
18851
18852 function escape(html, encode) {
18853   return html
18854     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18855     .replace(/</g, '&lt;')
18856     .replace(/>/g, '&gt;')
18857     .replace(/"/g, '&quot;')
18858     .replace(/'/g, '&#39;');
18859 }
18860
18861 function replace(regex, opt) {
18862   regex = regex.source;
18863   opt = opt || '';
18864   return function self(name, val) {
18865     if (!name) return new RegExp(regex, opt);
18866     val = val.source || val;
18867     val = val.replace(/(^|[^\[])\^/g, '$1');
18868     regex = regex.replace(name, val);
18869     return self;
18870   };
18871 }
18872
18873 function noop() {}
18874 noop.exec = noop;
18875
18876 function merge(obj) {
18877   var i = 1
18878     , target
18879     , key;
18880
18881   for (; i < arguments.length; i++) {
18882     target = arguments[i];
18883     for (key in target) {
18884       if (Object.prototype.hasOwnProperty.call(target, key)) {
18885         obj[key] = target[key];
18886       }
18887     }
18888   }
18889
18890   return obj;
18891 }
18892
18893 /**
18894  * Marked
18895  */
18896
18897 function marked(src, opt, callback) {
18898   if (callback || typeof opt === 'function') {
18899     if (!callback) {
18900       callback = opt;
18901       opt = null;
18902     }
18903
18904     if (opt) opt = merge({}, marked.defaults, opt);
18905
18906     var tokens = Lexer.lex(tokens, opt)
18907       , highlight = opt.highlight
18908       , pending = 0
18909       , l = tokens.length
18910       , i = 0;
18911
18912     if (!highlight || highlight.length < 3) {
18913       return callback(null, Parser.parse(tokens, opt));
18914     }
18915
18916     var done = function() {
18917       delete opt.highlight;
18918       var out = Parser.parse(tokens, opt);
18919       opt.highlight = highlight;
18920       return callback(null, out);
18921     };
18922
18923     for (; i < l; i++) {
18924       (function(token) {
18925         if (token.type !== 'code') return;
18926         pending++;
18927         return highlight(token.text, token.lang, function(err, code) {
18928           if (code == null || code === token.text) {
18929             return --pending || done();
18930           }
18931           token.text = code;
18932           token.escaped = true;
18933           --pending || done();
18934         });
18935       })(tokens[i]);
18936     }
18937
18938     return;
18939   }
18940   try {
18941     if (opt) opt = merge({}, marked.defaults, opt);
18942     return Parser.parse(Lexer.lex(src, opt), opt);
18943   } catch (e) {
18944     e.message += '\nPlease report this to https://github.com/chjj/marked.';
18945     if ((opt || marked.defaults).silent) {
18946       return '<p>An error occured:</p><pre>'
18947         + escape(e.message + '', true)
18948         + '</pre>';
18949     }
18950     throw e;
18951   }
18952 }
18953
18954 /**
18955  * Options
18956  */
18957
18958 marked.options =
18959 marked.setOptions = function(opt) {
18960   merge(marked.defaults, opt);
18961   return marked;
18962 };
18963
18964 marked.defaults = {
18965   gfm: true,
18966   tables: true,
18967   breaks: false,
18968   pedantic: false,
18969   sanitize: false,
18970   smartLists: false,
18971   silent: false,
18972   highlight: null,
18973   langPrefix: 'lang-'
18974 };
18975
18976 /**
18977  * Expose
18978  */
18979
18980 marked.Parser = Parser;
18981 marked.parser = Parser.parse;
18982
18983 marked.Lexer = Lexer;
18984 marked.lexer = Lexer.lex;
18985
18986 marked.InlineLexer = InlineLexer;
18987 marked.inlineLexer = InlineLexer.output;
18988
18989 marked.parse = marked;
18990
18991 if (typeof exports === 'object') {
18992   module.exports = marked;
18993 } else if (typeof define === 'function' && define.amd) {
18994   define(function() { return marked; });
18995 } else {
18996   this.marked = marked;
18997 }
18998
18999 }).call(function() {
19000   return this || (typeof window !== 'undefined' ? window : global);
19001 }());
19002 (function () {
19003 'use strict';
19004 window.iD = function () {
19005     window.locale.en = iD.data.en;
19006     window.locale.current('en');
19007
19008     var dispatch = d3.dispatch('enter', 'exit'),
19009         context = {};
19010
19011     // https://github.com/openstreetmap/iD/issues/772
19012     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19013     var storage;
19014     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
19015     storage = storage || (function() {
19016         var s = {};
19017         return {
19018             getItem: function(k) { return s[k]; },
19019             setItem: function(k, v) { s[k] = v; },
19020             removeItem: function(k) { delete s[k]; }
19021         };
19022     })();
19023
19024     context.storage = function(k, v) {
19025         try {
19026             if (arguments.length === 1) return storage.getItem(k);
19027             else if (v === null) storage.removeItem(k);
19028             else storage.setItem(k, v);
19029         } catch(e) {
19030             // localstorage quota exceeded
19031             /* eslint-disable no-console */
19032             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
19033             /* eslint-enable no-console */
19034         }
19035     };
19036
19037     var locale, localePath;
19038     context.locale = function(loc, path) {
19039         locale = loc;
19040         localePath = path;
19041
19042         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
19043         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
19044             iD.detect().locale = loc;
19045         }
19046
19047         return context;
19048     };
19049
19050     context.loadLocale = function(cb) {
19051         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
19052             localePath = localePath || context.assetPath() + 'locales/' + locale + '.json';
19053             d3.json(localePath, function(err, result) {
19054                 window.locale[locale] = result;
19055                 window.locale.current(locale);
19056                 cb();
19057             });
19058         } else {
19059             cb();
19060         }
19061     };
19062
19063
19064     /* Straight accessors. Avoid using these if you can. */
19065     var ui, connection, history;
19066     context.ui = function() { return ui; };
19067     context.connection = function() { return connection; };
19068     context.history = function() { return history; };
19069
19070
19071     /* Connection */
19072     function entitiesLoaded(err, result) {
19073         if (!err) history.merge(result.data, result.extent);
19074     }
19075
19076     context.preauth = function(options) {
19077         connection.switch(options);
19078         return context;
19079     };
19080
19081     context.loadTiles = function(projection, dimensions, callback) {
19082         function done(err, result) {
19083             entitiesLoaded(err, result);
19084             if (callback) callback(err, result);
19085         }
19086         connection.loadTiles(projection, dimensions, done);
19087     };
19088
19089     context.loadEntity = function(id, callback) {
19090         function done(err, result) {
19091             entitiesLoaded(err, result);
19092             if (callback) callback(err, result);
19093         }
19094         connection.loadEntity(id, done);
19095     };
19096
19097     context.zoomToEntity = function(id, zoomTo) {
19098         if (zoomTo !== false) {
19099             this.loadEntity(id, function(err, result) {
19100                 if (err) return;
19101                 var entity = _.find(result.data, function(e) { return e.id === id; });
19102                 if (entity) { map.zoomTo(entity); }
19103             });
19104         }
19105
19106         map.on('drawn.zoomToEntity', function() {
19107             if (!context.hasEntity(id)) return;
19108             map.on('drawn.zoomToEntity', null);
19109             context.on('enter.zoomToEntity', null);
19110             context.enter(iD.modes.Select(context, [id]));
19111         });
19112
19113         context.on('enter.zoomToEntity', function() {
19114             if (mode.id !== 'browse') {
19115                 map.on('drawn.zoomToEntity', null);
19116                 context.on('enter.zoomToEntity', null);
19117             }
19118         });
19119     };
19120
19121     var minEditableZoom = 16;
19122     context.minEditableZoom = function(_) {
19123         if (!arguments.length) return minEditableZoom;
19124         minEditableZoom = _;
19125         connection.tileZoom(_);
19126         return context;
19127     };
19128
19129
19130     /* History */
19131     var inIntro = false;
19132     context.inIntro = function(_) {
19133         if (!arguments.length) return inIntro;
19134         inIntro = _;
19135         return context;
19136     };
19137
19138     context.save = function() {
19139         if (inIntro || (mode && mode.id === 'save')) return;
19140         history.save();
19141         if (history.hasChanges()) return t('save.unsaved_changes');
19142     };
19143
19144     context.flush = function() {
19145         context.debouncedSave.cancel();
19146         connection.flush();
19147         features.reset();
19148         history.reset();
19149         _.each(iD.services, function(service) {
19150             var reset = service().reset;
19151             if (reset) reset(context);
19152         });
19153         return context;
19154     };
19155
19156
19157     /* Graph */
19158     context.hasEntity = function(id) {
19159         return history.graph().hasEntity(id);
19160     };
19161     context.entity = function(id) {
19162         return history.graph().entity(id);
19163     };
19164     context.childNodes = function(way) {
19165         return history.graph().childNodes(way);
19166     };
19167     context.geometry = function(id) {
19168         return context.entity(id).geometry(history.graph());
19169     };
19170
19171
19172     /* Modes */
19173     var mode;
19174     context.mode = function() {
19175         return mode;
19176     };
19177     context.enter = function(newMode) {
19178         if (mode) {
19179             mode.exit();
19180             dispatch.exit(mode);
19181         }
19182
19183         mode = newMode;
19184         mode.enter();
19185         dispatch.enter(mode);
19186     };
19187
19188     context.selectedIDs = function() {
19189         if (mode && mode.selectedIDs) {
19190             return mode.selectedIDs();
19191         } else {
19192             return [];
19193         }
19194     };
19195
19196
19197     /* Behaviors */
19198     context.install = function(behavior) {
19199         context.surface().call(behavior);
19200     };
19201     context.uninstall = function(behavior) {
19202         context.surface().call(behavior.off);
19203     };
19204
19205
19206     /* Copy/Paste */
19207     var copyIDs = [], copyGraph;
19208     context.copyGraph = function() { return copyGraph; };
19209     context.copyIDs = function(_) {
19210         if (!arguments.length) return copyIDs;
19211         copyIDs = _;
19212         copyGraph = history.graph();
19213         return context;
19214     };
19215
19216
19217     /* Background */
19218     var background;
19219     context.background = function() { return background; };
19220
19221
19222     /* Features */
19223     var features;
19224     context.features = function() { return features; };
19225     context.hasHiddenConnections = function(id) {
19226         var graph = history.graph(),
19227             entity = graph.entity(id);
19228         return features.hasHiddenConnections(entity, graph);
19229     };
19230
19231
19232     /* Map */
19233     var map;
19234     context.map = function() { return map; };
19235     context.layers = function() { return map.layers; };
19236     context.surface = function() { return map.surface; };
19237     context.editable = function() { return map.editable(); };
19238
19239     context.surfaceRect = function() {
19240         // Work around a bug in Firefox.
19241         //   http://stackoverflow.com/questions/18153989/
19242         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
19243         return context.surface().node().parentNode.getBoundingClientRect();
19244     };
19245
19246
19247     /* Presets */
19248     var presets;
19249     context.presets = function(_) {
19250         if (!arguments.length) return presets;
19251         presets.load(_);
19252         iD.areaKeys = presets.areaKeys();
19253         return context;
19254     };
19255
19256
19257     /* Imagery */
19258     context.imagery = function(_) {
19259         background.load(_);
19260         return context;
19261     };
19262
19263
19264     /* Container */
19265     var container, embed;
19266     context.container = function(_) {
19267         if (!arguments.length) return container;
19268         container = _;
19269         container.classed('id-container', true);
19270         return context;
19271     };
19272     context.embed = function(_) {
19273         if (!arguments.length) return embed;
19274         embed = _;
19275         return context;
19276     };
19277
19278
19279     /* Taginfo */
19280     var taginfo;
19281     context.taginfo = function(_) {
19282         if (!arguments.length) return taginfo;
19283         taginfo = _;
19284         return context;
19285     };
19286
19287
19288     /* Assets */
19289     var assetPath = '';
19290     context.assetPath = function(_) {
19291         if (!arguments.length) return assetPath;
19292         assetPath = _;
19293         return context;
19294     };
19295
19296     var assetMap = {};
19297     context.assetMap = function(_) {
19298         if (!arguments.length) return assetMap;
19299         assetMap = _;
19300         return context;
19301     };
19302
19303     context.imagePath = function(_) {
19304         var asset = 'img/' + _;
19305         return assetMap[asset] || assetPath + asset;
19306     };
19307
19308
19309     /* Init */
19310
19311     context.projection = iD.geo.RawMercator();
19312
19313     locale = iD.detect().locale;
19314     if (locale && iD.data.locales.indexOf(locale) === -1) {
19315         locale = locale.split('-')[0];
19316     }
19317
19318     history = iD.History(context);
19319     context.graph = history.graph;
19320     context.changes = history.changes;
19321     context.intersects = history.intersects;
19322
19323     // Debounce save, since it's a synchronous localStorage write,
19324     // and history changes can happen frequently (e.g. when dragging).
19325     context.debouncedSave = _.debounce(context.save, 350);
19326     function withDebouncedSave(fn) {
19327         return function() {
19328             var result = fn.apply(history, arguments);
19329             context.debouncedSave();
19330             return result;
19331         };
19332     }
19333
19334     context.perform = withDebouncedSave(history.perform);
19335     context.replace = withDebouncedSave(history.replace);
19336     context.pop = withDebouncedSave(history.pop);
19337     context.overwrite = withDebouncedSave(history.overwrite);
19338     context.undo = withDebouncedSave(history.undo);
19339     context.redo = withDebouncedSave(history.redo);
19340
19341     ui = iD.ui(context);
19342
19343     connection = iD.Connection();
19344
19345     background = iD.Background(context);
19346
19347     features = iD.Features(context);
19348
19349     map = iD.Map(context);
19350     context.mouse = map.mouse;
19351     context.extent = map.extent;
19352     context.pan = map.pan;
19353     context.zoomIn = map.zoomIn;
19354     context.zoomOut = map.zoomOut;
19355     context.zoomInFurther = map.zoomInFurther;
19356     context.zoomOutFurther = map.zoomOutFurther;
19357     context.redrawEnable = map.redrawEnable;
19358
19359     presets = iD.presets();
19360
19361     return d3.rebind(context, dispatch, 'on');
19362 };
19363
19364
19365 iD.version = '1.9.0';
19366
19367 (function() {
19368     var detected = {};
19369
19370     var ua = navigator.userAgent,
19371         m = null;
19372
19373     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
19374     if (m !== null) {
19375         detected.browser = m[1];
19376         detected.version = m[2];
19377     }
19378     if (!detected.browser) {
19379         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
19380         if (m !== null) {
19381             detected.browser = 'msie';
19382             detected.version = m[1];
19383         }
19384     }
19385     if (!detected.browser) {
19386         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
19387         if (m !== null) {
19388             detected.browser = 'Opera';
19389             detected.version = m[2];
19390         }
19391     }
19392     if (!detected.browser) {
19393         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
19394         if (m !== null) {
19395             detected.browser = m[1];
19396             detected.version = m[2];
19397             m = ua.match(/version\/([\.\d]+)/i);
19398             if (m !== null) detected.version = m[1];
19399         }
19400     }
19401     if (!detected.browser) {
19402         detected.browser = navigator.appName;
19403         detected.version = navigator.appVersion;
19404     }
19405
19406     // keep major.minor version only..
19407     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
19408
19409     if (detected.browser.toLowerCase() === 'msie') {
19410         detected.ie = true;
19411         detected.browser = 'Internet Explorer';
19412         detected.support = parseFloat(detected.version) >= 11;
19413     } else {
19414         detected.ie = false;
19415         detected.support = true;
19416     }
19417
19418     // Added due to incomplete svg style support. See #715
19419     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
19420
19421     detected.locale = (navigator.languages && navigator.languages.length)
19422         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
19423
19424     detected.filedrop = (window.FileReader && 'ondrop' in window);
19425
19426     function nav(x) {
19427         return navigator.userAgent.indexOf(x) !== -1;
19428     }
19429
19430     if (nav('Win')) {
19431         detected.os = 'win';
19432         detected.platform = 'Windows';
19433     }
19434     else if (nav('Mac')) {
19435         detected.os = 'mac';
19436         detected.platform = 'Macintosh';
19437     }
19438     else if (nav('X11') || nav('Linux')) {
19439         detected.os = 'linux';
19440         detected.platform = 'Linux';
19441     }
19442     else {
19443         detected.os = 'win';
19444         detected.platform = 'Unknown';
19445     }
19446
19447     iD.detect = function() { return detected; };
19448 })();
19449 iD.services = {};
19450 iD.services.mapillary = function() {
19451     var mapillary = {},
19452         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
19453         apibase = 'https://a.mapillary.com/v2/',
19454         urlImage = 'https://www.mapillary.com/map/im/',
19455         urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/',
19456         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
19457         maxResults = 1000,
19458         tileZoom = 14;
19459
19460
19461     function loadSignStyles(context) {
19462         d3.select('head').selectAll('#traffico')
19463             .data([0])
19464             .enter()
19465             .append('link')
19466             .attr('id', 'traffico')
19467             .attr('rel', 'stylesheet')
19468             .attr('href', context.assetPath() + 'traffico/stylesheets/traffico.css');
19469     }
19470
19471     function loadSignDefs(context) {
19472         if (!iD.services.mapillary.sign_defs) {
19473             iD.services.mapillary.sign_defs = {};
19474             _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
19475                 d3.json(context.assetPath() + 'traffico/string-maps/' + region + '-map.json', function(err, data) {
19476                     if (err) return;
19477                     if (region === 'de') region = 'eu';
19478                     iD.services.mapillary.sign_defs[region] = data;
19479                 });
19480             });
19481         }
19482     }
19483
19484     function abortRequest(i) {
19485         i.abort();
19486     }
19487
19488     function getTiles(projection, dimensions) {
19489         var s = projection.scale() * 2 * Math.PI,
19490             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
19491             ts = 256 * Math.pow(2, z - tileZoom),
19492             origin = [
19493                 s / 2 - projection.translate()[0],
19494                 s / 2 - projection.translate()[1]];
19495
19496         return d3.geo.tile()
19497             .scaleExtent([tileZoom, tileZoom])
19498             .scale(s)
19499             .size(dimensions)
19500             .translate(projection.translate())()
19501             .map(function(tile) {
19502                 var x = tile[0] * ts - origin[0],
19503                     y = tile[1] * ts - origin[1];
19504
19505                 return {
19506                     id: tile.toString(),
19507                     extent: iD.geo.Extent(
19508                         projection.invert([x, y + ts]),
19509                         projection.invert([x + ts, y]))
19510                 };
19511             });
19512     }
19513
19514
19515     function loadTiles(which, url, projection, dimensions) {
19516         var tiles = getTiles(projection, dimensions);
19517
19518         _.filter(which.inflight, function(v, k) {
19519             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
19520             if (!wanted) delete which.inflight[k];
19521             return !wanted;
19522         }).map(abortRequest);
19523
19524         tiles.forEach(function(tile) {
19525             loadTilePage(which, url, tile, 0);
19526         });
19527     }
19528
19529     function loadTilePage(which, url, tile, page) {
19530         var cache = iD.services.mapillary.cache[which],
19531             id = tile.id + ',' + String(page),
19532             rect = tile.extent.rectangle();
19533
19534         if (cache.loaded[id] || cache.inflight[id]) return;
19535
19536         cache.inflight[id] = d3.json(url +
19537             iD.util.qsString({
19538                 geojson: 'true',
19539                 limit: maxResults,
19540                 page: page,
19541                 client_id: clientId,
19542                 min_lon: rect[0],
19543                 min_lat: rect[1],
19544                 max_lon: rect[2],
19545                 max_lat: rect[3]
19546             }), function(err, data) {
19547                 cache.loaded[id] = true;
19548                 delete cache.inflight[id];
19549                 if (err || !data.features || !data.features.length) return;
19550
19551                 var features = [],
19552                     feature, loc, d;
19553
19554                 for (var i = 0; i < data.features.length; i++) {
19555                     feature = data.features[i];
19556                     loc = feature.geometry.coordinates;
19557                     d = { key: feature.properties.key, loc: loc };
19558                     if (which === 'images') d.ca = feature.properties.ca;
19559                     if (which === 'signs') d.signs = feature.properties.rects;
19560
19561                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
19562                 }
19563
19564                 cache.rtree.load(features);
19565
19566                 if (which === 'images') dispatch.loadedImages();
19567                 if (which === 'signs') dispatch.loadedSigns();
19568
19569                 if (data.features.length === maxResults) {
19570                     loadTilePage(which, url, tile, ++page);
19571                 }
19572             }
19573         );
19574     }
19575
19576     mapillary.loadImages = function(projection, dimensions) {
19577         var url = apibase + 'search/im/geojson?';
19578         loadTiles('images', url, projection, dimensions);
19579     };
19580
19581     mapillary.loadSigns = function(context, projection, dimensions) {
19582         var url = apibase + 'search/im/geojson/or?';
19583         loadSignStyles(context);
19584         loadSignDefs(context);
19585         loadTiles('signs', url, projection, dimensions);
19586     };
19587
19588
19589     // partition viewport into `psize` x `psize` regions
19590     function partitionViewport(psize, projection, dimensions) {
19591         psize = psize || 16;
19592         var cols = d3.range(0, dimensions[0], psize),
19593             rows = d3.range(0, dimensions[1], psize),
19594             partitions = [];
19595
19596         rows.forEach(function(y) {
19597             cols.forEach(function(x) {
19598                 var min = [x, y + psize],
19599                     max = [x + psize, y];
19600                 partitions.push(
19601                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
19602             });
19603         });
19604
19605         return partitions;
19606     }
19607
19608     // no more than `limit` results per partition.
19609     function searchLimited(psize, limit, projection, dimensions, rtree) {
19610         limit = limit || 3;
19611
19612         var partitions = partitionViewport(psize, projection, dimensions);
19613         return _.flatten(_.compact(_.map(partitions, function(extent) {
19614             return rtree.search(extent.rectangle())
19615                 .slice(0, limit)
19616                 .map(function(d) { return d[4]; });
19617         })));
19618     }
19619
19620     mapillary.images = function(projection, dimensions) {
19621         var psize = 16, limit = 3;
19622         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
19623     };
19624
19625     mapillary.signs = function(projection, dimensions) {
19626         var psize = 32, limit = 3;
19627         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
19628     };
19629
19630     mapillary.signsSupported = function() {
19631         var detected = iD.detect();
19632         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
19633     };
19634
19635     mapillary.signHTML = function(d) {
19636         if (!iD.services.mapillary.sign_defs) return;
19637
19638         var detectionPackage = d.signs[0].package,
19639             type = d.signs[0].type,
19640             country = detectionPackage.split('_')[1];
19641         return iD.services.mapillary.sign_defs[country][type];
19642     };
19643
19644     mapillary.showThumbnail = function(imageKey, position) {
19645         if (!imageKey) return;
19646
19647         var positionClass = {
19648             'ar': (position !== 'left'),
19649             'al': (position === 'left')
19650         };
19651
19652         var thumbnail = d3.select('#content').selectAll('.mapillary-image')
19653             .data([0]);
19654
19655         // Enter
19656         var enter = thumbnail.enter().append('div')
19657             .attr('class', 'mapillary-image ar');
19658
19659         enter.append('button')
19660             .on('click', function () {
19661                 mapillary.hideThumbnail();
19662             })
19663             .append('div')
19664             .call(iD.svg.Icon('#icon-close'));
19665
19666         enter.append('img');
19667
19668         enter.append('a')
19669             .attr('class', 'link ar')
19670             .attr('target', '_blank')
19671             .call(iD.svg.Icon('#icon-out-link', 'inline'))
19672             .append('span')
19673             .text(t('mapillary.view_on_mapillary'));
19674
19675         // Update
19676         thumbnail.selectAll('img')
19677             .attr('src', urlThumb + imageKey + '/thumb-320.jpg');
19678
19679         var link = thumbnail.selectAll('a')
19680             .attr('href', urlImage + imageKey);
19681
19682         if (position) {
19683             thumbnail.classed(positionClass);
19684             link.classed(positionClass);
19685         }
19686
19687         thumbnail
19688             .transition()
19689             .duration(200)
19690             .style('opacity', 1);
19691     };
19692
19693     mapillary.hideThumbnail = function() {
19694         if (iD.services.mapillary) {
19695             iD.services.mapillary.thumb = null;
19696         }
19697         d3.select('#content').selectAll('.mapillary-image')
19698             .transition()
19699             .duration(200)
19700             .style('opacity', 0)
19701             .remove();
19702     };
19703
19704     mapillary.selectedThumbnail = function(d) {
19705         if (!iD.services.mapillary) return null;
19706         if (!arguments.length) return iD.services.mapillary.thumb;
19707         iD.services.mapillary.thumb = d;
19708     };
19709
19710     mapillary.reset = function() {
19711         var cache = iD.services.mapillary.cache;
19712
19713         if (cache) {
19714             _.forEach(cache.images.inflight, abortRequest);
19715             _.forEach(cache.signs.inflight, abortRequest);
19716         }
19717
19718         iD.services.mapillary.cache = {
19719             images: { inflight: {}, loaded: {}, rtree: rbush() },
19720             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
19721         };
19722
19723         iD.services.mapillary.thumb = null;
19724
19725         return mapillary;
19726     };
19727
19728
19729     if (!iD.services.mapillary.cache) {
19730         mapillary.reset();
19731     }
19732
19733     return d3.rebind(mapillary, dispatch, 'on');
19734 };
19735 iD.services.nominatim  = function() {
19736     var nominatim = {},
19737         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
19738
19739
19740     nominatim.countryCode = function(location, callback) {
19741         var cache = iD.services.nominatim.cache,
19742             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
19743
19744         if (countryCodes.length > 0)
19745             return callback(null, countryCodes[0][4]);
19746
19747         d3.json(endpoint +
19748             iD.util.qsString({
19749                 format: 'json',
19750                 addressdetails: 1,
19751                 lat: location[1],
19752                 lon: location[0]
19753             }), function(err, result) {
19754                 if (err)
19755                     return callback(err);
19756                 else if (result && result.error)
19757                     return callback(result.error);
19758
19759                 var extent = iD.geo.Extent(location).padByMeters(1000);
19760
19761                 cache.insert(extent.rectangle().concat(result.address.country_code));
19762
19763                 callback(null, result.address.country_code);
19764             });
19765     };
19766
19767     nominatim.reset = function() {
19768         iD.services.nominatim.cache = rbush();
19769         return nominatim;
19770     };
19771
19772
19773     if (!iD.services.nominatim.cache) {
19774         nominatim.reset();
19775     }
19776
19777     return nominatim;
19778 };
19779 iD.services.taginfo = function() {
19780     var taginfo = {},
19781         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
19782         tag_sorts = {
19783             point: 'count_nodes',
19784             vertex: 'count_nodes',
19785             area: 'count_ways',
19786             line: 'count_ways'
19787         },
19788         tag_filters = {
19789             point: 'nodes',
19790             vertex: 'nodes',
19791             area: 'ways',
19792             line: 'ways'
19793         };
19794
19795
19796     function sets(parameters, n, o) {
19797         if (parameters.geometry && o[parameters.geometry]) {
19798             parameters[n] = o[parameters.geometry];
19799         }
19800         return parameters;
19801     }
19802
19803     function setFilter(parameters) {
19804         return sets(parameters, 'filter', tag_filters);
19805     }
19806
19807     function setSort(parameters) {
19808         return sets(parameters, 'sortname', tag_sorts);
19809     }
19810
19811     function clean(parameters) {
19812         return _.omit(parameters, 'geometry', 'debounce');
19813     }
19814
19815     function popularKeys(parameters) {
19816         var pop_field = 'count_all';
19817         if (parameters.filter) pop_field = 'count_' + parameters.filter;
19818         return function(d) { return parseFloat(d[pop_field]) > 5000 || d.in_wiki; };
19819     }
19820
19821     function popularValues() {
19822         return function(d) { return parseFloat(d.fraction) > 0.01 || d.in_wiki; };
19823     }
19824
19825     function valKey(d) { return { value: d.key }; }
19826
19827     function valKeyDescription(d) {
19828         return {
19829             value: d.value,
19830             title: d.description
19831         };
19832     }
19833
19834     // sort keys with ':' lower than keys without ':'
19835     function sortKeys(a, b) {
19836         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
19837             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
19838             : 0;
19839     }
19840
19841     var debounced = _.debounce(d3.json, 100, true);
19842
19843     function request(url, debounce, callback) {
19844         var cache = iD.services.taginfo.cache;
19845
19846         if (cache[url]) {
19847             callback(null, cache[url]);
19848         } else if (debounce) {
19849             debounced(url, done);
19850         } else {
19851             d3.json(url, done);
19852         }
19853
19854         function done(err, data) {
19855             if (!err) cache[url] = data;
19856             callback(err, data);
19857         }
19858     }
19859
19860     taginfo.keys = function(parameters, callback) {
19861         var debounce = parameters.debounce;
19862         parameters = clean(setSort(parameters));
19863         request(endpoint + 'keys/all?' +
19864             iD.util.qsString(_.extend({
19865                 rp: 10,
19866                 sortname: 'count_all',
19867                 sortorder: 'desc',
19868                 page: 1
19869             }, parameters)), debounce, function(err, d) {
19870                 if (err) return callback(err);
19871                 callback(null, d.data.filter(popularKeys(parameters)).sort(sortKeys).map(valKey));
19872             });
19873     };
19874
19875     taginfo.values = function(parameters, callback) {
19876         var debounce = parameters.debounce;
19877         parameters = clean(setSort(setFilter(parameters)));
19878         request(endpoint + 'key/values?' +
19879             iD.util.qsString(_.extend({
19880                 rp: 25,
19881                 sortname: 'count_all',
19882                 sortorder: 'desc',
19883                 page: 1
19884             }, parameters)), debounce, function(err, d) {
19885                 if (err) return callback(err);
19886                 callback(null, d.data.filter(popularValues()).map(valKeyDescription), parameters);
19887             });
19888     };
19889
19890     taginfo.docs = function(parameters, callback) {
19891         var debounce = parameters.debounce;
19892         parameters = clean(setSort(parameters));
19893
19894         var path = 'key/wiki_pages?';
19895         if (parameters.value) path = 'tag/wiki_pages?';
19896         else if (parameters.rtype) path = 'relation/wiki_pages?';
19897
19898         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
19899             if (err) return callback(err);
19900             callback(null, d.data);
19901         });
19902     };
19903
19904     taginfo.endpoint = function(_) {
19905         if (!arguments.length) return endpoint;
19906         endpoint = _;
19907         return taginfo;
19908     };
19909
19910     taginfo.reset = function() {
19911         iD.services.taginfo.cache = {};
19912         return taginfo;
19913     };
19914
19915
19916     if (!iD.services.taginfo.cache) {
19917         taginfo.reset();
19918     }
19919
19920     return taginfo;
19921 };
19922 iD.services.wikipedia = function() {
19923     var wiki = {},
19924         endpoint = 'https://en.wikipedia.org/w/api.php?';
19925
19926     wiki.search = function(lang, query, callback) {
19927         lang = lang || 'en';
19928         d3.jsonp(endpoint.replace('en', lang) +
19929             iD.util.qsString({
19930                 action: 'query',
19931                 list: 'search',
19932                 srlimit: '10',
19933                 srinfo: 'suggestion',
19934                 format: 'json',
19935                 callback: '{callback}',
19936                 srsearch: query
19937             }), function(data) {
19938                 if (!data.query) return;
19939                 callback(query, data.query.search.map(function(d) {
19940                     return d.title;
19941                 }));
19942             });
19943     };
19944
19945     wiki.suggestions = function(lang, query, callback) {
19946         lang = lang || 'en';
19947         d3.jsonp(endpoint.replace('en', lang) +
19948             iD.util.qsString({
19949                 action: 'opensearch',
19950                 namespace: 0,
19951                 suggest: '',
19952                 format: 'json',
19953                 callback: '{callback}',
19954                 search: query
19955             }), function(d) {
19956                 callback(d[0], d[1]);
19957             });
19958     };
19959
19960     wiki.translations = function(lang, title, callback) {
19961         d3.jsonp(endpoint.replace('en', lang) +
19962             iD.util.qsString({
19963                 action: 'query',
19964                 prop: 'langlinks',
19965                 format: 'json',
19966                 callback: '{callback}',
19967                 lllimit: 500,
19968                 titles: title
19969             }), function(d) {
19970                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
19971                     translations = {};
19972                 if (list && list.langlinks) {
19973                     list.langlinks.forEach(function(d) {
19974                         translations[d.lang] = d['*'];
19975                     });
19976                     callback(translations);
19977                 }
19978             });
19979     };
19980
19981     return wiki;
19982 };
19983 iD.util = {};
19984
19985 iD.util.tagText = function(entity) {
19986     return d3.entries(entity.tags).map(function(e) {
19987         return e.key + '=' + e.value;
19988     }).join(', ');
19989 };
19990
19991 iD.util.entitySelector = function(ids) {
19992     return ids.length ? '.' + ids.join(',.') : 'nothing';
19993 };
19994
19995 iD.util.entityOrMemberSelector = function(ids, graph) {
19996     var s = iD.util.entitySelector(ids);
19997
19998     ids.forEach(function(id) {
19999         var entity = graph.hasEntity(id);
20000         if (entity && entity.type === 'relation') {
20001             entity.members.forEach(function(member) {
20002                 s += ',.' + member.id;
20003             });
20004         }
20005     });
20006
20007     return s;
20008 };
20009
20010 iD.util.displayName = function(entity) {
20011     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
20012     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
20013 };
20014
20015 iD.util.displayType = function(id) {
20016     return {
20017         n: t('inspector.node'),
20018         w: t('inspector.way'),
20019         r: t('inspector.relation')
20020     }[id.charAt(0)];
20021 };
20022
20023 iD.util.stringQs = function(str) {
20024     return str.split('&').reduce(function(obj, pair){
20025         var parts = pair.split('=');
20026         if (parts.length === 2) {
20027             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
20028         }
20029         return obj;
20030     }, {});
20031 };
20032
20033 iD.util.qsString = function(obj, noencode) {
20034     function softEncode(s) {
20035       // encode everything except special characters used in certain hash parameters:
20036       // "/" in map states, ":", ",", {" and "}" in background
20037       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
20038     }
20039     return Object.keys(obj).sort().map(function(key) {
20040         return encodeURIComponent(key) + '=' + (
20041             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
20042     }).join('&');
20043 };
20044
20045 iD.util.prefixDOMProperty = function(property) {
20046     var prefixes = ['webkit', 'ms', 'moz', 'o'],
20047         i = -1,
20048         n = prefixes.length,
20049         s = document.body;
20050
20051     if (property in s)
20052         return property;
20053
20054     property = property.substr(0, 1).toUpperCase() + property.substr(1);
20055
20056     while (++i < n)
20057         if (prefixes[i] + property in s)
20058             return prefixes[i] + property;
20059
20060     return false;
20061 };
20062
20063 iD.util.prefixCSSProperty = function(property) {
20064     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
20065         i = -1,
20066         n = prefixes.length,
20067         s = document.body.style;
20068
20069     if (property.toLowerCase() in s)
20070         return property.toLowerCase();
20071
20072     while (++i < n)
20073         if (prefixes[i] + property in s)
20074             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
20075
20076     return false;
20077 };
20078
20079
20080 iD.util.setTransform = function(el, x, y, scale) {
20081     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
20082         translate = iD.detect().opera ?
20083             'translate('   + x + 'px,' + y + 'px)' :
20084             'translate3d(' + x + 'px,' + y + 'px,0)';
20085     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
20086 };
20087
20088 iD.util.getStyle = function(selector) {
20089     for (var i = 0; i < document.styleSheets.length; i++) {
20090         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
20091         for (var k = 0; k < rules.length; k++) {
20092             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
20093             if (_.contains(selectorText, selector)) {
20094                 return rules[k];
20095             }
20096         }
20097     }
20098 };
20099
20100 iD.util.editDistance = function(a, b) {
20101     if (a.length === 0) return b.length;
20102     if (b.length === 0) return a.length;
20103     var matrix = [];
20104     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20105     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20106     for (i = 1; i <= b.length; i++) {
20107         for (j = 1; j <= a.length; j++) {
20108             if (b.charAt(i-1) === a.charAt(j-1)) {
20109                 matrix[i][j] = matrix[i-1][j-1];
20110             } else {
20111                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20112                     Math.min(matrix[i][j-1] + 1, // insertion
20113                     matrix[i-1][j] + 1)); // deletion
20114             }
20115         }
20116     }
20117     return matrix[b.length][a.length];
20118 };
20119
20120 // a d3.mouse-alike which
20121 // 1. Only works on HTML elements, not SVG
20122 // 2. Does not cause style recalculation
20123 iD.util.fastMouse = function(container) {
20124     var rect = container.getBoundingClientRect(),
20125         rectLeft = rect.left,
20126         rectTop = rect.top,
20127         clientLeft = +container.clientLeft,
20128         clientTop = +container.clientTop;
20129     return function(e) {
20130         return [
20131             e.clientX - rectLeft - clientLeft,
20132             e.clientY - rectTop - clientTop];
20133     };
20134 };
20135
20136 /* eslint-disable no-proto */
20137 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
20138 /* eslint-enable no-proto */
20139
20140 iD.util.asyncMap = function(inputs, func, callback) {
20141     var remaining = inputs.length,
20142         results = [],
20143         errors = [];
20144
20145     inputs.forEach(function(d, i) {
20146         func(d, function done(err, data) {
20147             errors[i] = err;
20148             results[i] = data;
20149             remaining--;
20150             if (!remaining) callback(errors, results);
20151         });
20152     });
20153 };
20154
20155 // wraps an index to an interval [0..length-1]
20156 iD.util.wrap = function(index, length) {
20157     if (index < 0)
20158         index += Math.ceil(-index/length)*length;
20159     return index % length;
20160 };
20161 // A per-domain session mutex backed by a cookie and dead man's
20162 // switch. If the session crashes, the mutex will auto-release
20163 // after 5 seconds.
20164
20165 iD.util.SessionMutex = function(name) {
20166     var mutex = {},
20167         intervalID;
20168
20169     function renew() {
20170         var expires = new Date();
20171         expires.setSeconds(expires.getSeconds() + 5);
20172         document.cookie = name + '=1; expires=' + expires.toUTCString();
20173     }
20174
20175     mutex.lock = function() {
20176         if (intervalID) return true;
20177         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
20178         if (cookie) return false;
20179         renew();
20180         intervalID = window.setInterval(renew, 4000);
20181         return true;
20182     };
20183
20184     mutex.unlock = function() {
20185         if (!intervalID) return;
20186         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
20187         clearInterval(intervalID);
20188         intervalID = null;
20189     };
20190
20191     mutex.locked = function() {
20192         return !!intervalID;
20193     };
20194
20195     return mutex;
20196 };
20197 iD.util.SuggestNames = function(preset, suggestions) {
20198     preset = preset.id.split('/', 2);
20199     var k = preset[0],
20200         v = preset[1];
20201
20202     return function(value, callback) {
20203         var result = [];
20204         if (value && value.length > 2) {
20205             if (suggestions[k] && suggestions[k][v]) {
20206                 for (var sugg in suggestions[k][v]) {
20207                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
20208                     if (dist < 3) {
20209                         result.push({
20210                             title: sugg,
20211                             value: sugg,
20212                             dist: dist
20213                         });
20214                     }
20215                 }
20216             }
20217             result.sort(function(a, b) {
20218                 return a.dist - b.dist;
20219             });
20220         }
20221         result = result.slice(0,3);
20222         callback(result);
20223     };
20224 };
20225 iD.geo = {};
20226
20227 iD.geo.roundCoords = function(c) {
20228     return [Math.floor(c[0]), Math.floor(c[1])];
20229 };
20230
20231 iD.geo.interp = function(p1, p2, t) {
20232     return [p1[0] + (p2[0] - p1[0]) * t,
20233             p1[1] + (p2[1] - p1[1]) * t];
20234 };
20235
20236 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
20237 // Returns a positive value, if OAB makes a counter-clockwise turn,
20238 // negative for clockwise turn, and zero if the points are collinear.
20239 iD.geo.cross = function(o, a, b) {
20240     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
20241 };
20242
20243 // http://jsperf.com/id-dist-optimization
20244 iD.geo.euclideanDistance = function(a, b) {
20245     var x = a[0] - b[0], y = a[1] - b[1];
20246     return Math.sqrt((x * x) + (y * y));
20247 };
20248
20249 // using WGS84 polar radius (6356752.314245179 m)
20250 // const = 2 * PI * r / 360
20251 iD.geo.latToMeters = function(dLat) {
20252     return dLat * 110946.257617;
20253 };
20254
20255 // using WGS84 equatorial radius (6378137.0 m)
20256 // const = 2 * PI * r / 360
20257 iD.geo.lonToMeters = function(dLon, atLat) {
20258     return Math.abs(atLat) >= 90 ? 0 :
20259         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
20260 };
20261
20262 // using WGS84 polar radius (6356752.314245179 m)
20263 // const = 2 * PI * r / 360
20264 iD.geo.metersToLat = function(m) {
20265     return m / 110946.257617;
20266 };
20267
20268 // using WGS84 equatorial radius (6378137.0 m)
20269 // const = 2 * PI * r / 360
20270 iD.geo.metersToLon = function(m, atLat) {
20271     return Math.abs(atLat) >= 90 ? 0 :
20272         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
20273 };
20274
20275 // Equirectangular approximation of spherical distances on Earth
20276 iD.geo.sphericalDistance = function(a, b) {
20277     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
20278         y = iD.geo.latToMeters(a[1] - b[1]);
20279     return Math.sqrt((x * x) + (y * y));
20280 };
20281
20282 iD.geo.edgeEqual = function(a, b) {
20283     return (a[0] === b[0] && a[1] === b[1]) ||
20284         (a[0] === b[1] && a[1] === b[0]);
20285 };
20286
20287 // Return the counterclockwise angle in the range (-pi, pi)
20288 // between the positive X axis and the line intersecting a and b.
20289 iD.geo.angle = function(a, b, projection) {
20290     a = projection(a.loc);
20291     b = projection(b.loc);
20292     return Math.atan2(b[1] - a[1], b[0] - a[0]);
20293 };
20294
20295 // Choose the edge with the minimal distance from `point` to its orthogonal
20296 // projection onto that edge, if such a projection exists, or the distance to
20297 // the closest vertex on that edge. Returns an object with the `index` of the
20298 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
20299 iD.geo.chooseEdge = function(nodes, point, projection) {
20300     var dist = iD.geo.euclideanDistance,
20301         points = nodes.map(function(n) { return projection(n.loc); }),
20302         min = Infinity,
20303         idx, loc;
20304
20305     function dot(p, q) {
20306         return p[0] * q[0] + p[1] * q[1];
20307     }
20308
20309     for (var i = 0; i < points.length - 1; i++) {
20310         var o = points[i],
20311             s = [points[i + 1][0] - o[0],
20312                  points[i + 1][1] - o[1]],
20313             v = [point[0] - o[0],
20314                  point[1] - o[1]],
20315             proj = dot(v, s) / dot(s, s),
20316             p;
20317
20318         if (proj < 0) {
20319             p = o;
20320         } else if (proj > 1) {
20321             p = points[i + 1];
20322         } else {
20323             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
20324         }
20325
20326         var d = dist(p, point);
20327         if (d < min) {
20328             min = d;
20329             idx = i + 1;
20330             loc = projection.invert(p);
20331         }
20332     }
20333
20334     return {
20335         index: idx,
20336         distance: min,
20337         loc: loc
20338     };
20339 };
20340
20341 // Return the intersection point of 2 line segments.
20342 // From https://github.com/pgkelley4/line-segments-intersect
20343 // This uses the vector cross product approach described below:
20344 //  http://stackoverflow.com/a/565282/786339
20345 iD.geo.lineIntersection = function(a, b) {
20346     function subtractPoints(point1, point2) {
20347         return [point1[0] - point2[0], point1[1] - point2[1]];
20348     }
20349     function crossProduct(point1, point2) {
20350         return point1[0] * point2[1] - point1[1] * point2[0];
20351     }
20352
20353     var p = [a[0][0], a[0][1]],
20354         p2 = [a[1][0], a[1][1]],
20355         q = [b[0][0], b[0][1]],
20356         q2 = [b[1][0], b[1][1]],
20357         r = subtractPoints(p2, p),
20358         s = subtractPoints(q2, q),
20359         uNumerator = crossProduct(subtractPoints(q, p), r),
20360         denominator = crossProduct(r, s);
20361
20362     if (uNumerator && denominator) {
20363         var u = uNumerator / denominator,
20364             t = crossProduct(subtractPoints(q, p), s) / denominator;
20365
20366         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
20367             return iD.geo.interp(p, p2, t);
20368         }
20369     }
20370
20371     return null;
20372 };
20373
20374 iD.geo.pathIntersections = function(path1, path2) {
20375     var intersections = [];
20376     for (var i = 0; i < path1.length - 1; i++) {
20377         for (var j = 0; j < path2.length - 1; j++) {
20378             var a = [ path1[i], path1[i+1] ],
20379                 b = [ path2[j], path2[j+1] ],
20380                 hit = iD.geo.lineIntersection(a, b);
20381             if (hit) intersections.push(hit);
20382         }
20383     }
20384     return intersections;
20385 };
20386
20387 // Return whether point is contained in polygon.
20388 //
20389 // `point` should be a 2-item array of coordinates.
20390 // `polygon` should be an array of 2-item arrays of coordinates.
20391 //
20392 // From https://github.com/substack/point-in-polygon.
20393 // ray-casting algorithm based on
20394 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
20395 //
20396 iD.geo.pointInPolygon = function(point, polygon) {
20397     var x = point[0],
20398         y = point[1],
20399         inside = false;
20400
20401     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
20402         var xi = polygon[i][0], yi = polygon[i][1];
20403         var xj = polygon[j][0], yj = polygon[j][1];
20404
20405         var intersect = ((yi > y) !== (yj > y)) &&
20406             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
20407         if (intersect) inside = !inside;
20408     }
20409
20410     return inside;
20411 };
20412
20413 iD.geo.polygonContainsPolygon = function(outer, inner) {
20414     return _.every(inner, function(point) {
20415         return iD.geo.pointInPolygon(point, outer);
20416     });
20417 };
20418
20419 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
20420     function testSegments(outer, inner) {
20421         for (var i = 0; i < outer.length - 1; i++) {
20422             for (var j = 0; j < inner.length - 1; j++) {
20423                 var a = [ outer[i], outer[i+1] ],
20424                     b = [ inner[j], inner[j+1] ];
20425                 if (iD.geo.lineIntersection(a, b)) return true;
20426             }
20427         }
20428         return false;
20429     }
20430
20431     function testPoints(outer, inner) {
20432         return _.some(inner, function(point) {
20433             return iD.geo.pointInPolygon(point, outer);
20434         });
20435     }
20436
20437    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
20438 };
20439
20440 iD.geo.pathLength = function(path) {
20441     var length = 0,
20442         dx, dy;
20443     for (var i = 0; i < path.length - 1; i++) {
20444         dx = path[i][0] - path[i + 1][0];
20445         dy = path[i][1] - path[i + 1][1];
20446         length += Math.sqrt(dx * dx + dy * dy);
20447     }
20448     return length;
20449 };
20450 iD.geo.Extent = function geoExtent(min, max) {
20451     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
20452     if (min instanceof iD.geo.Extent) {
20453         return min;
20454     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
20455         this[0] = min[0];
20456         this[1] = min[1];
20457     } else {
20458         this[0] = min        || [ Infinity,  Infinity];
20459         this[1] = max || min || [-Infinity, -Infinity];
20460     }
20461 };
20462
20463 iD.geo.Extent.prototype = new Array(2);
20464
20465 _.extend(iD.geo.Extent.prototype, {
20466     equals: function (obj) {
20467         return this[0][0] === obj[0][0] &&
20468             this[0][1] === obj[0][1] &&
20469             this[1][0] === obj[1][0] &&
20470             this[1][1] === obj[1][1];
20471     },
20472
20473     extend: function(obj) {
20474         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20475         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
20476                               Math.min(obj[0][1], this[0][1])],
20477                              [Math.max(obj[1][0], this[1][0]),
20478                               Math.max(obj[1][1], this[1][1])]);
20479     },
20480
20481     _extend: function(extent) {
20482         this[0][0] = Math.min(extent[0][0], this[0][0]);
20483         this[0][1] = Math.min(extent[0][1], this[0][1]);
20484         this[1][0] = Math.max(extent[1][0], this[1][0]);
20485         this[1][1] = Math.max(extent[1][1], this[1][1]);
20486     },
20487
20488     area: function() {
20489         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
20490     },
20491
20492     center: function() {
20493         return [(this[0][0] + this[1][0]) / 2,
20494                 (this[0][1] + this[1][1]) / 2];
20495     },
20496
20497     rectangle: function() {
20498         return [this[0][0], this[0][1], this[1][0], this[1][1]];
20499     },
20500
20501     polygon: function() {
20502         return [
20503             [this[0][0], this[0][1]],
20504             [this[0][0], this[1][1]],
20505             [this[1][0], this[1][1]],
20506             [this[1][0], this[0][1]],
20507             [this[0][0], this[0][1]]
20508         ];
20509     },
20510
20511     contains: function(obj) {
20512         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20513         return obj[0][0] >= this[0][0] &&
20514                obj[0][1] >= this[0][1] &&
20515                obj[1][0] <= this[1][0] &&
20516                obj[1][1] <= this[1][1];
20517     },
20518
20519     intersects: function(obj) {
20520         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20521         return obj[0][0] <= this[1][0] &&
20522                obj[0][1] <= this[1][1] &&
20523                obj[1][0] >= this[0][0] &&
20524                obj[1][1] >= this[0][1];
20525     },
20526
20527     intersection: function(obj) {
20528         if (!this.intersects(obj)) return new iD.geo.Extent();
20529         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
20530                                   Math.max(obj[0][1], this[0][1])],
20531                                  [Math.min(obj[1][0], this[1][0]),
20532                                   Math.min(obj[1][1], this[1][1])]);
20533     },
20534
20535     percentContainedIn: function(obj) {
20536         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20537         var a1 = this.intersection(obj).area(),
20538             a2 = this.area();
20539
20540         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
20541             return 0;
20542         } else {
20543             return a1 / a2;
20544         }
20545     },
20546
20547     padByMeters: function(meters) {
20548         var dLat = iD.geo.metersToLat(meters),
20549             dLon = iD.geo.metersToLon(meters, this.center()[1]);
20550         return iD.geo.Extent(
20551                 [this[0][0] - dLon, this[0][1] - dLat],
20552                 [this[1][0] + dLon, this[1][1] + dLat]);
20553     },
20554
20555     toParam: function() {
20556         return this.rectangle().join(',');
20557     }
20558
20559 });
20560 iD.geo.Turn = function(turn) {
20561     if (!(this instanceof iD.geo.Turn))
20562         return new iD.geo.Turn(turn);
20563     _.extend(this, turn);
20564 };
20565
20566 iD.geo.Intersection = function(graph, vertexId) {
20567     var vertex = graph.entity(vertexId),
20568         parentWays = graph.parentWays(vertex),
20569         coincident = [],
20570         highways = {};
20571
20572     function addHighway(way, adjacentNodeId) {
20573         if (highways[adjacentNodeId]) {
20574             coincident.push(adjacentNodeId);
20575         } else {
20576             highways[adjacentNodeId] = way;
20577         }
20578     }
20579
20580     // Pre-split ways that would need to be split in
20581     // order to add a restriction. The real split will
20582     // happen when the restriction is added.
20583     parentWays.forEach(function(way) {
20584         if (!way.tags.highway || way.isArea() || way.isDegenerate())
20585             return;
20586
20587         var isFirst = (vertexId === way.first()),
20588             isLast = (vertexId === way.last()),
20589             isAffix = (isFirst || isLast),
20590             isClosingNode = (isFirst && isLast);
20591
20592         if (isAffix && !isClosingNode) {
20593             var index = (isFirst ? 1 : way.nodes.length - 2);
20594             addHighway(way, way.nodes[index]);
20595
20596         } else {
20597             var splitIndex, wayA, wayB, indexA, indexB;
20598             if (isClosingNode) {
20599                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
20600                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
20601                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20602                 indexA = 1;
20603                 indexB = way.nodes.length - 2;
20604             } else {
20605                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
20606                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
20607                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20608                 indexA = splitIndex - 1;
20609                 indexB = splitIndex + 1;
20610             }
20611             graph = graph.replace(wayA).replace(wayB);
20612             addHighway(wayA, way.nodes[indexA]);
20613             addHighway(wayB, way.nodes[indexB]);
20614         }
20615     });
20616
20617     // remove any ways from this intersection that are coincident
20618     // (i.e. any adjacent node used by more than one intersecting way)
20619     coincident.forEach(function (n) {
20620         delete highways[n];
20621     });
20622
20623
20624     var intersection = {
20625         highways: highways,
20626         ways: _.values(highways),
20627         graph: graph
20628     };
20629
20630     intersection.adjacentNodeId = function(fromWayId) {
20631         return _.find(_.keys(highways), function(k) {
20632             return highways[k].id === fromWayId;
20633         });
20634     };
20635
20636     intersection.turns = function(fromNodeId) {
20637         var start = highways[fromNodeId];
20638         if (!start)
20639             return [];
20640
20641         if (start.first() === vertex.id && start.tags.oneway === 'yes')
20642             return [];
20643         if (start.last() === vertex.id && start.tags.oneway === '-1')
20644             return [];
20645
20646         function withRestriction(turn) {
20647             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
20648                 if (relation.tags.type !== 'restriction')
20649                     return;
20650
20651                 var f = relation.memberByRole('from'),
20652                     t = relation.memberByRole('to'),
20653                     v = relation.memberByRole('via');
20654
20655                 if (f && f.id === turn.from.way &&
20656                     v && v.id === turn.via.node &&
20657                     t && t.id === turn.to.way) {
20658                     turn.restriction = relation.id;
20659                 } else if (/^only_/.test(relation.tags.restriction) &&
20660                     f && f.id === turn.from.way &&
20661                     v && v.id === turn.via.node &&
20662                     t && t.id !== turn.to.way) {
20663                     turn.restriction = relation.id;
20664                     turn.indirect_restriction = true;
20665                 }
20666             });
20667
20668             return iD.geo.Turn(turn);
20669         }
20670
20671         var from = {
20672                 node: fromNodeId,
20673                 way: start.id.split(/-(a|b)/)[0]
20674             },
20675             via = { node: vertex.id },
20676             turns = [];
20677
20678         _.each(highways, function(end, adjacentNodeId) {
20679             if (end === start)
20680                 return;
20681
20682             // backward
20683             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
20684                 turns.push(withRestriction({
20685                     from: from,
20686                     via: via,
20687                     to: {
20688                         node: adjacentNodeId,
20689                         way: end.id.split(/-(a|b)/)[0]
20690                     }
20691                 }));
20692             }
20693
20694             // forward
20695             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
20696                 turns.push(withRestriction({
20697                     from: from,
20698                     via: via,
20699                     to: {
20700                         node: adjacentNodeId,
20701                         way: end.id.split(/-(a|b)/)[0]
20702                     }
20703                 }));
20704             }
20705
20706         });
20707
20708         // U-turn
20709         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
20710             turns.push(withRestriction({
20711                 from: from,
20712                 via: via,
20713                 to: from,
20714                 u: true
20715             }));
20716         }
20717
20718         return turns;
20719     };
20720
20721     return intersection;
20722 };
20723
20724
20725 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
20726     var fromWay = graph.entity(from.way),
20727         fromNode = graph.entity(from.node),
20728         toWay = graph.entity(to.way),
20729         toNode = graph.entity(to.node),
20730         viaNode = graph.entity(via.node),
20731         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
20732             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
20733         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
20734             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
20735         angle = iD.geo.angle(viaNode, fromNode, projection) -
20736                 iD.geo.angle(viaNode, toNode, projection);
20737
20738     angle = angle * 180 / Math.PI;
20739
20740     while (angle < 0)
20741         angle += 360;
20742
20743     if (fromNode === toNode)
20744         return 'no_u_turn';
20745     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
20746         return 'no_u_turn';
20747     if (angle < 158)
20748         return 'no_right_turn';
20749     if (angle > 202)
20750         return 'no_left_turn';
20751
20752     return 'no_straight_on';
20753 };
20754 // For fixing up rendering of multipolygons with tags on the outer member.
20755 // https://github.com/openstreetmap/iD/issues/613
20756 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
20757     if (entity.type !== 'way')
20758         return false;
20759
20760     var parents = graph.parentRelations(entity);
20761     if (parents.length !== 1)
20762         return false;
20763
20764     var parent = parents[0];
20765     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20766         return false;
20767
20768     var members = parent.members, member;
20769     for (var i = 0; i < members.length; i++) {
20770         member = members[i];
20771         if (member.id === entity.id && member.role && member.role !== 'outer')
20772             return false; // Not outer member
20773         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
20774             return false; // Not a simple multipolygon
20775     }
20776
20777     return parent;
20778 };
20779
20780 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
20781     if (entity.type !== 'way')
20782         return false;
20783
20784     var parents = graph.parentRelations(entity);
20785     if (parents.length !== 1)
20786         return false;
20787
20788     var parent = parents[0];
20789     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20790         return false;
20791
20792     var members = parent.members, member, outerMember;
20793     for (var i = 0; i < members.length; i++) {
20794         member = members[i];
20795         if (!member.role || member.role === 'outer') {
20796             if (outerMember)
20797                 return false; // Not a simple multipolygon
20798             outerMember = member;
20799         }
20800     }
20801
20802     return outerMember && graph.hasEntity(outerMember.id);
20803 };
20804
20805 // Join `array` into sequences of connecting ways.
20806 //
20807 // Segments which share identical start/end nodes will, as much as possible,
20808 // be connected with each other.
20809 //
20810 // The return value is a nested array. Each constituent array contains elements
20811 // of `array` which have been determined to connect. Each consitituent array
20812 // also has a `nodes` property whose value is an ordered array of member nodes,
20813 // with appropriate order reversal and start/end coordinate de-duplication.
20814 //
20815 // Members of `array` must have, at minimum, `type` and `id` properties.
20816 // Thus either an array of `iD.Way`s or a relation member array may be
20817 // used.
20818 //
20819 // If an member has a `tags` property, its tags will be reversed via
20820 // `iD.actions.Reverse` in the output.
20821 //
20822 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
20823 // false) and non-way members are ignored.
20824 //
20825 iD.geo.joinWays = function(array, graph) {
20826     var joined = [], member, current, nodes, first, last, i, how, what;
20827
20828     array = array.filter(function(member) {
20829         return member.type === 'way' && graph.hasEntity(member.id);
20830     });
20831
20832     function resolve(member) {
20833         return graph.childNodes(graph.entity(member.id));
20834     }
20835
20836     function reverse(member) {
20837         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
20838     }
20839
20840     while (array.length) {
20841         member = array.shift();
20842         current = [member];
20843         current.nodes = nodes = resolve(member).slice();
20844         joined.push(current);
20845
20846         while (array.length && _.first(nodes) !== _.last(nodes)) {
20847             first = _.first(nodes);
20848             last  = _.last(nodes);
20849
20850             for (i = 0; i < array.length; i++) {
20851                 member = array[i];
20852                 what = resolve(member);
20853
20854                 if (last === _.first(what)) {
20855                     how  = nodes.push;
20856                     what = what.slice(1);
20857                     break;
20858                 } else if (last === _.last(what)) {
20859                     how  = nodes.push;
20860                     what = what.slice(0, -1).reverse();
20861                     member = reverse(member);
20862                     break;
20863                 } else if (first === _.last(what)) {
20864                     how  = nodes.unshift;
20865                     what = what.slice(0, -1);
20866                     break;
20867                 } else if (first === _.first(what)) {
20868                     how  = nodes.unshift;
20869                     what = what.slice(1).reverse();
20870                     member = reverse(member);
20871                     break;
20872                 } else {
20873                     what = how = null;
20874                 }
20875             }
20876
20877             if (!what)
20878                 break; // No more joinable ways.
20879
20880             how.apply(current, [member]);
20881             how.apply(nodes, what);
20882
20883             array.splice(i, 1);
20884         }
20885     }
20886
20887     return joined;
20888 };
20889 /*
20890     Bypasses features of D3's default projection stream pipeline that are unnecessary:
20891     * Antimeridian clipping
20892     * Spherical rotation
20893     * Resampling
20894 */
20895 iD.geo.RawMercator = function () {
20896     var project = d3.geo.mercator.raw,
20897         k = 512 / Math.PI, // scale
20898         x = 0, y = 0, // translate
20899         clipExtent = [[0, 0], [0, 0]];
20900
20901     function projection(point) {
20902         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
20903         return [point[0] * k + x, y - point[1] * k];
20904     }
20905
20906     projection.invert = function(point) {
20907         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
20908         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
20909     };
20910
20911     projection.scale = function(_) {
20912         if (!arguments.length) return k;
20913         k = +_;
20914         return projection;
20915     };
20916
20917     projection.translate = function(_) {
20918         if (!arguments.length) return [x, y];
20919         x = +_[0];
20920         y = +_[1];
20921         return projection;
20922     };
20923
20924     projection.clipExtent = function(_) {
20925         if (!arguments.length) return clipExtent;
20926         clipExtent = _;
20927         return projection;
20928     };
20929
20930     projection.stream = d3.geo.transform({
20931         point: function(x, y) {
20932             x = projection([x, y]);
20933             this.stream.point(x[0], x[1]);
20934         }
20935     }).stream;
20936
20937     return projection;
20938 };
20939 iD.actions = {};
20940 iD.actions.AddEntity = function(way) {
20941     return function(graph) {
20942         return graph.replace(way);
20943     };
20944 };
20945 iD.actions.AddMember = function(relationId, member, memberIndex) {
20946     return function(graph) {
20947         var relation = graph.entity(relationId);
20948
20949         if (isNaN(memberIndex) && member.type === 'way') {
20950             var members = relation.indexedMembers();
20951             members.push(member);
20952
20953             var joined = iD.geo.joinWays(members, graph);
20954             for (var i = 0; i < joined.length; i++) {
20955                 var segment = joined[i];
20956                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
20957                     if (segment[j] !== member)
20958                         continue;
20959
20960                     if (j === 0) {
20961                         memberIndex = segment[j + 1].index;
20962                     } else if (j === segment.length - 1) {
20963                         memberIndex = segment[j - 1].index + 1;
20964                     } else {
20965                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
20966                     }
20967                 }
20968             }
20969         }
20970
20971         return graph.replace(relation.addMember(member, memberIndex));
20972     };
20973 };
20974 iD.actions.AddMidpoint = function(midpoint, node) {
20975     return function(graph) {
20976         graph = graph.replace(node.move(midpoint.loc));
20977
20978         var parents = _.intersection(
20979             graph.parentWays(graph.entity(midpoint.edge[0])),
20980             graph.parentWays(graph.entity(midpoint.edge[1])));
20981
20982         parents.forEach(function(way) {
20983             for (var i = 0; i < way.nodes.length - 1; i++) {
20984                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
20985                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
20986
20987                     // Add only one midpoint on doubled-back segments,
20988                     // turning them into self-intersections.
20989                     return;
20990                 }
20991             }
20992         });
20993
20994         return graph;
20995     };
20996 };
20997 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
20998 iD.actions.AddVertex = function(wayId, nodeId, index) {
20999     return function(graph) {
21000         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21001     };
21002 };
21003 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
21004     return function(graph) {
21005         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21006     };
21007 };
21008 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
21009     return function(graph) {
21010         var entity = graph.entity(entityId),
21011             geometry = entity.geometry(graph),
21012             tags = entity.tags;
21013
21014         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
21015         if (newPreset) tags = newPreset.applyTags(tags, geometry);
21016
21017         return graph.replace(entity.update({tags: tags}));
21018     };
21019 };
21020 iD.actions.ChangeTags = function(entityId, tags) {
21021     return function(graph) {
21022         var entity = graph.entity(entityId);
21023         return graph.replace(entity.update({tags: tags}));
21024     };
21025 };
21026 iD.actions.Circularize = function(wayId, projection, maxAngle) {
21027     maxAngle = (maxAngle || 20) * Math.PI / 180;
21028
21029     var action = function(graph) {
21030         var way = graph.entity(wayId);
21031
21032         if (!way.isConvex(graph)) {
21033             graph = action.makeConvex(graph);
21034         }
21035
21036         var nodes = _.uniq(graph.childNodes(way)),
21037             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
21038             points = nodes.map(function(n) { return projection(n.loc); }),
21039             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
21040             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
21041             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
21042             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21043             ids;
21044
21045         // we need atleast two key nodes for the algorithm to work
21046         if (!keyNodes.length) {
21047             keyNodes = [nodes[0]];
21048             keyPoints = [points[0]];
21049         }
21050
21051         if (keyNodes.length === 1) {
21052             var index = nodes.indexOf(keyNodes[0]),
21053                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21054
21055             keyNodes.push(nodes[oppositeIndex]);
21056             keyPoints.push(points[oppositeIndex]);
21057         }
21058
21059         // key points and nodes are those connected to the ways,
21060         // they are projected onto the circle, inbetween nodes are moved
21061         // to constant intervals between key nodes, extra inbetween nodes are
21062         // added if necessary.
21063         for (var i = 0; i < keyPoints.length; i++) {
21064             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
21065                 startNode = keyNodes[i],
21066                 endNode = keyNodes[nextKeyNodeIndex],
21067                 startNodeIndex = nodes.indexOf(startNode),
21068                 endNodeIndex = nodes.indexOf(endNode),
21069                 numberNewPoints = -1,
21070                 indexRange = endNodeIndex - startNodeIndex,
21071                 distance, totalAngle, eachAngle, startAngle, endAngle,
21072                 angle, loc, node, j,
21073                 inBetweenNodes = [];
21074
21075             if (indexRange < 0) {
21076                 indexRange += nodes.length;
21077             }
21078
21079             // position this key node
21080             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
21081             if (distance === 0) { distance = 1e-4; }
21082             keyPoints[i] = [
21083                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21084                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
21085             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
21086
21087             // figure out the between delta angle we want to match to
21088             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21089             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21090             totalAngle = endAngle - startAngle;
21091
21092             // detects looping around -pi/pi
21093             if (totalAngle * sign > 0) {
21094                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21095             }
21096
21097             do {
21098                 numberNewPoints++;
21099                 eachAngle = totalAngle / (indexRange + numberNewPoints);
21100             } while (Math.abs(eachAngle) > maxAngle);
21101
21102             // move existing points
21103             for (j = 1; j < indexRange; j++) {
21104                 angle = startAngle + j * eachAngle;
21105                 loc = projection.invert([
21106                     centroid[0] + Math.cos(angle)*radius,
21107                     centroid[1] + Math.sin(angle)*radius]);
21108
21109                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
21110                 graph = graph.replace(node);
21111             }
21112
21113             // add new inbetween nodes if necessary
21114             for (j = 0; j < numberNewPoints; j++) {
21115                 angle = startAngle + (indexRange + j) * eachAngle;
21116                 loc = projection.invert([
21117                     centroid[0] + Math.cos(angle) * radius,
21118                     centroid[1] + Math.sin(angle) * radius]);
21119
21120                 node = iD.Node({loc: loc});
21121                 graph = graph.replace(node);
21122
21123                 nodes.splice(endNodeIndex + j, 0, node);
21124                 inBetweenNodes.push(node.id);
21125             }
21126
21127             // Check for other ways that share these keyNodes..
21128             // If keyNodes are adjacent in both ways,
21129             // we can add inBetween nodes to that shared way too..
21130             if (indexRange === 1 && inBetweenNodes.length) {
21131                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
21132                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
21133                     wayDirection1 = (endIndex1 - startIndex1);
21134                 if (wayDirection1 < -1) { wayDirection1 = 1; }
21135
21136                 /* eslint-disable no-loop-func */
21137                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
21138                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21139                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
21140                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
21141                             wayDirection2 = (endIndex2 - startIndex2),
21142                             insertAt = endIndex2;
21143                         if (wayDirection2 < -1) { wayDirection2 = 1; }
21144
21145                         if (wayDirection1 !== wayDirection2) {
21146                             inBetweenNodes.reverse();
21147                             insertAt = startIndex2;
21148                         }
21149                         for (j = 0; j < inBetweenNodes.length; j++) {
21150                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
21151                         }
21152                         graph = graph.replace(sharedWay);
21153                     }
21154                 });
21155                 /* eslint-enable no-loop-func */
21156             }
21157
21158         }
21159
21160         // update the way to have all the new nodes
21161         ids = nodes.map(function(n) { return n.id; });
21162         ids.push(ids[0]);
21163
21164         way = way.update({nodes: ids});
21165         graph = graph.replace(way);
21166
21167         return graph;
21168     };
21169
21170     action.makeConvex = function(graph) {
21171         var way = graph.entity(wayId),
21172             nodes = _.uniq(graph.childNodes(way)),
21173             points = nodes.map(function(n) { return projection(n.loc); }),
21174             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21175             hull = d3.geom.hull(points);
21176
21177         // D3 convex hulls go counterclockwise..
21178         if (sign === -1) {
21179             nodes.reverse();
21180             points.reverse();
21181         }
21182
21183         for (var i = 0; i < hull.length - 1; i++) {
21184             var startIndex = points.indexOf(hull[i]),
21185                 endIndex = points.indexOf(hull[i+1]),
21186                 indexRange = (endIndex - startIndex);
21187
21188             if (indexRange < 0) {
21189                 indexRange += nodes.length;
21190             }
21191
21192             // move interior nodes to the surface of the convex hull..
21193             for (var j = 1; j < indexRange; j++) {
21194                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
21195                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
21196                 graph = graph.replace(node);
21197             }
21198         }
21199         return graph;
21200     };
21201
21202     action.disabled = function(graph) {
21203         if (!graph.entity(wayId).isClosed())
21204             return 'not_closed';
21205     };
21206
21207     return action;
21208 };
21209 // Connect the ways at the given nodes.
21210 //
21211 // The last node will survive. All other nodes will be replaced with
21212 // the surviving node in parent ways, and then removed.
21213 //
21214 // Tags and relation memberships of of non-surviving nodes are merged
21215 // to the survivor.
21216 //
21217 // This is the inverse of `iD.actions.Disconnect`.
21218 //
21219 // Reference:
21220 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
21221 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
21222 //
21223 iD.actions.Connect = function(nodeIds) {
21224     return function(graph) {
21225         var survivor = graph.entity(_.last(nodeIds));
21226
21227         for (var i = 0; i < nodeIds.length - 1; i++) {
21228             var node = graph.entity(nodeIds[i]);
21229
21230             /* eslint-disable no-loop-func */
21231             graph.parentWays(node).forEach(function(parent) {
21232                 if (!parent.areAdjacent(node.id, survivor.id)) {
21233                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
21234                 }
21235             });
21236
21237             graph.parentRelations(node).forEach(function(parent) {
21238                 graph = graph.replace(parent.replaceMember(node, survivor));
21239             });
21240             /* eslint-enable no-loop-func */
21241
21242             survivor = survivor.mergeTags(node.tags);
21243             graph = iD.actions.DeleteNode(node.id)(graph);
21244         }
21245
21246         graph = graph.replace(survivor);
21247
21248         return graph;
21249     };
21250 };
21251 iD.actions.CopyEntities = function(ids, fromGraph) {
21252     var copies = {};
21253
21254     var action = function(graph) {
21255         ids.forEach(function(id) {
21256             fromGraph.entity(id).copy(fromGraph, copies);
21257         });
21258
21259         for (var id in copies) {
21260             graph = graph.replace(copies[id]);
21261         }
21262
21263         return graph;
21264     };
21265
21266     action.copies = function() {
21267         return copies;
21268     };
21269
21270     return action;
21271 };
21272 iD.actions.DeleteMember = function(relationId, memberIndex) {
21273     return function(graph) {
21274         var relation = graph.entity(relationId)
21275             .removeMember(memberIndex);
21276
21277         graph = graph.replace(relation);
21278
21279         if (relation.isDegenerate())
21280             graph = iD.actions.DeleteRelation(relation.id)(graph);
21281
21282         return graph;
21283     };
21284 };
21285 iD.actions.DeleteMultiple = function(ids) {
21286     var actions = {
21287         way: iD.actions.DeleteWay,
21288         node: iD.actions.DeleteNode,
21289         relation: iD.actions.DeleteRelation
21290     };
21291
21292     var action = function(graph) {
21293         ids.forEach(function(id) {
21294             if (graph.hasEntity(id)) { // It may have been deleted aready.
21295                 graph = actions[graph.entity(id).type](id)(graph);
21296             }
21297         });
21298
21299         return graph;
21300     };
21301
21302     action.disabled = function(graph) {
21303         for (var i = 0; i < ids.length; i++) {
21304             var id = ids[i],
21305                 disabled = actions[graph.entity(id).type](id).disabled(graph);
21306             if (disabled) return disabled;
21307         }
21308     };
21309
21310     return action;
21311 };
21312 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
21313 iD.actions.DeleteNode = function(nodeId) {
21314     var action = function(graph) {
21315         var node = graph.entity(nodeId);
21316
21317         graph.parentWays(node)
21318             .forEach(function(parent) {
21319                 parent = parent.removeNode(nodeId);
21320                 graph = graph.replace(parent);
21321
21322                 if (parent.isDegenerate()) {
21323                     graph = iD.actions.DeleteWay(parent.id)(graph);
21324                 }
21325             });
21326
21327         graph.parentRelations(node)
21328             .forEach(function(parent) {
21329                 parent = parent.removeMembersWithID(nodeId);
21330                 graph = graph.replace(parent);
21331
21332                 if (parent.isDegenerate()) {
21333                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21334                 }
21335             });
21336
21337         return graph.remove(node);
21338     };
21339
21340     action.disabled = function() {
21341         return false;
21342     };
21343
21344     return action;
21345 };
21346 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
21347 iD.actions.DeleteRelation = function(relationId) {
21348     function deleteEntity(entity, graph) {
21349         return !graph.parentWays(entity).length &&
21350             !graph.parentRelations(entity).length &&
21351             !entity.hasInterestingTags();
21352     }
21353
21354     var action = function(graph) {
21355         var relation = graph.entity(relationId);
21356
21357         graph.parentRelations(relation)
21358             .forEach(function(parent) {
21359                 parent = parent.removeMembersWithID(relationId);
21360                 graph = graph.replace(parent);
21361
21362                 if (parent.isDegenerate()) {
21363                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21364                 }
21365             });
21366
21367         _.uniq(_.pluck(relation.members, 'id')).forEach(function(memberId) {
21368             graph = graph.replace(relation.removeMembersWithID(memberId));
21369
21370             var entity = graph.entity(memberId);
21371             if (deleteEntity(entity, graph)) {
21372                 graph = iD.actions.DeleteMultiple([memberId])(graph);
21373             }
21374         });
21375
21376         return graph.remove(relation);
21377     };
21378
21379     action.disabled = function(graph) {
21380         if (!graph.entity(relationId).isComplete(graph))
21381             return 'incomplete_relation';
21382     };
21383
21384     return action;
21385 };
21386 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
21387 iD.actions.DeleteWay = function(wayId) {
21388     function deleteNode(node, graph) {
21389         return !graph.parentWays(node).length &&
21390             !graph.parentRelations(node).length &&
21391             !node.hasInterestingTags();
21392     }
21393
21394     var action = function(graph) {
21395         var way = graph.entity(wayId);
21396
21397         graph.parentRelations(way)
21398             .forEach(function(parent) {
21399                 parent = parent.removeMembersWithID(wayId);
21400                 graph = graph.replace(parent);
21401
21402                 if (parent.isDegenerate()) {
21403                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21404                 }
21405             });
21406
21407         _.uniq(way.nodes).forEach(function(nodeId) {
21408             graph = graph.replace(way.removeNode(nodeId));
21409
21410             var node = graph.entity(nodeId);
21411             if (deleteNode(node, graph)) {
21412                 graph = graph.remove(node);
21413             }
21414         });
21415
21416         return graph.remove(way);
21417     };
21418
21419     action.disabled = function(graph) {
21420         var disabled = false;
21421
21422         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
21423             var type = parent.tags.type,
21424                 role = parent.memberById(wayId).role || 'outer';
21425             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
21426                 disabled = 'part_of_relation';
21427             }
21428         });
21429
21430         return disabled;
21431     };
21432
21433     return action;
21434 };
21435 iD.actions.DeprecateTags = function(entityId) {
21436     return function(graph) {
21437         var entity = graph.entity(entityId),
21438             newtags = _.clone(entity.tags),
21439             change = false,
21440             rule;
21441
21442         // This handles deprecated tags with a single condition
21443         for (var i = 0; i < iD.data.deprecated.length; i++) {
21444
21445             rule = iD.data.deprecated[i];
21446             var match = _.pairs(rule.old)[0],
21447                 replacements = rule.replace ? _.pairs(rule.replace) : null;
21448
21449             if (entity.tags[match[0]] && match[1] === '*') {
21450
21451                 var value = entity.tags[match[0]];
21452                 if (replacements && !newtags[replacements[0][0]]) {
21453                     newtags[replacements[0][0]] = value;
21454                 }
21455                 delete newtags[match[0]];
21456                 change = true;
21457
21458             } else if (entity.tags[match[0]] === match[1]) {
21459                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
21460                 change = true;
21461             }
21462         }
21463
21464         if (change) {
21465             return graph.replace(entity.update({tags: newtags}));
21466         } else {
21467             return graph;
21468         }
21469     };
21470 };
21471 iD.actions.DiscardTags = function(difference) {
21472     return function(graph) {
21473         function discardTags(entity) {
21474             if (!_.isEmpty(entity.tags)) {
21475                 var tags = {};
21476                 _.each(entity.tags, function(v, k) {
21477                     if (v) tags[k] = v;
21478                 });
21479
21480                 graph = graph.replace(entity.update({
21481                     tags: _.omit(tags, iD.data.discarded)
21482                 }));
21483             }
21484         }
21485
21486         difference.modified().forEach(discardTags);
21487         difference.created().forEach(discardTags);
21488
21489         return graph;
21490     };
21491 };
21492 // Disconect the ways at the given node.
21493 //
21494 // Optionally, disconnect only the given ways.
21495 //
21496 // For testing convenience, accepts an ID to assign to the (first) new node.
21497 // Normally, this will be undefined and the way will automatically
21498 // be assigned a new ID.
21499 //
21500 // This is the inverse of `iD.actions.Connect`.
21501 //
21502 // Reference:
21503 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
21504 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
21505 //
21506 iD.actions.Disconnect = function(nodeId, newNodeId) {
21507     var wayIds;
21508
21509     var action = function(graph) {
21510         var node = graph.entity(nodeId),
21511             connections = action.connections(graph);
21512
21513         connections.forEach(function(connection) {
21514             var way = graph.entity(connection.wayID),
21515                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
21516
21517             graph = graph.replace(newNode);
21518             if (connection.index === 0 && way.isArea()) {
21519                 // replace shared node with shared node..
21520                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
21521             } else {
21522                 // replace shared node with multiple new nodes..
21523                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
21524             }
21525         });
21526
21527         return graph;
21528     };
21529
21530     action.connections = function(graph) {
21531         var candidates = [],
21532             keeping = false,
21533             parentWays = graph.parentWays(graph.entity(nodeId));
21534
21535         parentWays.forEach(function(way) {
21536             if (wayIds && wayIds.indexOf(way.id) === -1) {
21537                 keeping = true;
21538                 return;
21539             }
21540             if (way.isArea() && (way.nodes[0] === nodeId)) {
21541                 candidates.push({wayID: way.id, index: 0});
21542             } else {
21543                 way.nodes.forEach(function(waynode, index) {
21544                     if (waynode === nodeId) {
21545                         candidates.push({wayID: way.id, index: index});
21546                     }
21547                 });
21548             }
21549         });
21550
21551         return keeping ? candidates : candidates.slice(1);
21552     };
21553
21554     action.disabled = function(graph) {
21555         var connections = action.connections(graph);
21556         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
21557             return 'not_connected';
21558
21559         var parentWays = graph.parentWays(graph.entity(nodeId)),
21560             seenRelationIds = {},
21561             sharedRelation;
21562
21563         parentWays.forEach(function(way) {
21564             if (wayIds && wayIds.indexOf(way.id) === -1)
21565                 return;
21566
21567             var relations = graph.parentRelations(way);
21568             relations.forEach(function(relation) {
21569                 if (relation.id in seenRelationIds) {
21570                     sharedRelation = relation;
21571                 } else {
21572                     seenRelationIds[relation.id] = true;
21573                 }
21574             });
21575         });
21576
21577         if (sharedRelation)
21578             return 'relation';
21579     };
21580
21581     action.limitWays = function(_) {
21582         if (!arguments.length) return wayIds;
21583         wayIds = _;
21584         return action;
21585     };
21586
21587     return action;
21588 };
21589 // Join ways at the end node they share.
21590 //
21591 // This is the inverse of `iD.actions.Split`.
21592 //
21593 // Reference:
21594 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
21595 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
21596 //
21597 iD.actions.Join = function(ids) {
21598
21599     function groupEntitiesByGeometry(graph) {
21600         var entities = ids.map(function(id) { return graph.entity(id); });
21601         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21602     }
21603
21604     var action = function(graph) {
21605         var ways = ids.map(graph.entity, graph),
21606             survivor = ways[0];
21607
21608         // Prefer to keep an existing way.
21609         for (var i = 0; i < ways.length; i++) {
21610             if (!ways[i].isNew()) {
21611                 survivor = ways[i];
21612                 break;
21613             }
21614         }
21615
21616         var joined = iD.geo.joinWays(ways, graph)[0];
21617
21618         survivor = survivor.update({nodes: _.pluck(joined.nodes, 'id')});
21619         graph = graph.replace(survivor);
21620
21621         joined.forEach(function(way) {
21622             if (way.id === survivor.id)
21623                 return;
21624
21625             graph.parentRelations(way).forEach(function(parent) {
21626                 graph = graph.replace(parent.replaceMember(way, survivor));
21627             });
21628
21629             survivor = survivor.mergeTags(way.tags);
21630
21631             graph = graph.replace(survivor);
21632             graph = iD.actions.DeleteWay(way.id)(graph);
21633         });
21634
21635         return graph;
21636     };
21637
21638     action.disabled = function(graph) {
21639         var geometries = groupEntitiesByGeometry(graph);
21640         if (ids.length < 2 || ids.length !== geometries.line.length)
21641             return 'not_eligible';
21642
21643         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
21644         if (joined.length > 1)
21645             return 'not_adjacent';
21646
21647         var nodeIds = _.pluck(joined[0].nodes, 'id').slice(1, -1),
21648             relation,
21649             tags = {},
21650             conflicting = false;
21651
21652         joined[0].forEach(function(way) {
21653             var parents = graph.parentRelations(way);
21654             parents.forEach(function(parent) {
21655                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
21656                     relation = parent;
21657             });
21658
21659             for (var k in way.tags) {
21660                 if (!(k in tags)) {
21661                     tags[k] = way.tags[k];
21662                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
21663                     conflicting = true;
21664                 }
21665             }
21666         });
21667
21668         if (relation)
21669             return 'restriction';
21670
21671         if (conflicting)
21672             return 'conflicting_tags';
21673     };
21674
21675     return action;
21676 };
21677 iD.actions.Merge = function(ids) {
21678     function groupEntitiesByGeometry(graph) {
21679         var entities = ids.map(function(id) { return graph.entity(id); });
21680         return _.extend({point: [], area: [], line: [], relation: []},
21681             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21682     }
21683
21684     var action = function(graph) {
21685         var geometries = groupEntitiesByGeometry(graph),
21686             target = geometries.area[0] || geometries.line[0],
21687             points = geometries.point;
21688
21689         points.forEach(function(point) {
21690             target = target.mergeTags(point.tags);
21691
21692             graph.parentRelations(point).forEach(function(parent) {
21693                 graph = graph.replace(parent.replaceMember(point, target));
21694             });
21695
21696             graph = graph.remove(point);
21697         });
21698
21699         graph = graph.replace(target);
21700
21701         return graph;
21702     };
21703
21704     action.disabled = function(graph) {
21705         var geometries = groupEntitiesByGeometry(graph);
21706         if (geometries.point.length === 0 ||
21707             (geometries.area.length + geometries.line.length) !== 1 ||
21708             geometries.relation.length !== 0)
21709             return 'not_eligible';
21710     };
21711
21712     return action;
21713 };
21714 iD.actions.MergePolygon = function(ids, newRelationId) {
21715
21716     function groupEntities(graph) {
21717         var entities = ids.map(function (id) { return graph.entity(id); });
21718         return _.extend({
21719                 closedWay: [],
21720                 multipolygon: [],
21721                 other: []
21722             }, _.groupBy(entities, function(entity) {
21723                 if (entity.type === 'way' && entity.isClosed()) {
21724                     return 'closedWay';
21725                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
21726                     return 'multipolygon';
21727                 } else {
21728                     return 'other';
21729                 }
21730             }));
21731     }
21732
21733     var action = function(graph) {
21734         var entities = groupEntities(graph);
21735
21736         // An array representing all the polygons that are part of the multipolygon.
21737         //
21738         // Each element is itself an array of objects with an id property, and has a
21739         // locs property which is an array of the locations forming the polygon.
21740         var polygons = entities.multipolygon.reduce(function(polygons, m) {
21741             return polygons.concat(iD.geo.joinWays(m.members, graph));
21742         }, []).concat(entities.closedWay.map(function(d) {
21743             var member = [{id: d.id}];
21744             member.nodes = graph.childNodes(d);
21745             return member;
21746         }));
21747
21748         // contained is an array of arrays of boolean values,
21749         // where contained[j][k] is true iff the jth way is
21750         // contained by the kth way.
21751         var contained = polygons.map(function(w, i) {
21752             return polygons.map(function(d, n) {
21753                 if (i === n) return null;
21754                 return iD.geo.polygonContainsPolygon(
21755                     _.pluck(d.nodes, 'loc'),
21756                     _.pluck(w.nodes, 'loc'));
21757             });
21758         });
21759
21760         // Sort all polygons as either outer or inner ways
21761         var members = [],
21762             outer = true;
21763
21764         while (polygons.length) {
21765             extractUncontained(polygons);
21766             polygons = polygons.filter(isContained);
21767             contained = contained.filter(isContained).map(filterContained);
21768         }
21769
21770         function isContained(d, i) {
21771             return _.any(contained[i]);
21772         }
21773
21774         function filterContained(d) {
21775             return d.filter(isContained);
21776         }
21777
21778         function extractUncontained(polygons) {
21779             polygons.forEach(function(d, i) {
21780                 if (!isContained(d, i)) {
21781                     d.forEach(function(member) {
21782                         members.push({
21783                             type: 'way',
21784                             id: member.id,
21785                             role: outer ? 'outer' : 'inner'
21786                         });
21787                     });
21788                 }
21789             });
21790             outer = !outer;
21791         }
21792
21793         // Move all tags to one relation
21794         var relation = entities.multipolygon[0] ||
21795             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
21796
21797         entities.multipolygon.slice(1).forEach(function(m) {
21798             relation = relation.mergeTags(m.tags);
21799             graph = graph.remove(m);
21800         });
21801
21802         entities.closedWay.forEach(function(way) {
21803             function isThisOuter(m) {
21804                 return m.id === way.id && m.role !== 'inner';
21805             }
21806             if (members.some(isThisOuter)) {
21807                 relation = relation.mergeTags(way.tags);
21808                 graph = graph.replace(way.update({ tags: {} }));
21809             }
21810         });
21811
21812         return graph.replace(relation.update({
21813             members: members,
21814             tags: _.omit(relation.tags, 'area')
21815         }));
21816     };
21817
21818     action.disabled = function(graph) {
21819         var entities = groupEntities(graph);
21820         if (entities.other.length > 0 ||
21821             entities.closedWay.length + entities.multipolygon.length < 2)
21822             return 'not_eligible';
21823         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
21824             return 'incomplete_relation';
21825     };
21826
21827     return action;
21828 };
21829 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
21830     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
21831         conflicts = [];
21832
21833     function user(d) {
21834         return _.isFunction(formatUser) ? formatUser(d) : d;
21835     }
21836
21837
21838     function mergeLocation(remote, target) {
21839         function pointEqual(a, b) {
21840             var epsilon = 1e-6;
21841             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
21842         }
21843
21844         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
21845             return target;
21846         }
21847         if (option === 'force_remote') {
21848             return target.update({loc: remote.loc});
21849         }
21850
21851         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
21852         return target;
21853     }
21854
21855
21856     function mergeNodes(base, remote, target) {
21857         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
21858             return target;
21859         }
21860         if (option === 'force_remote') {
21861             return target.update({nodes: remote.nodes});
21862         }
21863
21864         var ccount = conflicts.length,
21865             o = base.nodes || [],
21866             a = target.nodes || [],
21867             b = remote.nodes || [],
21868             nodes = [],
21869             hunks = Diff3.diff3_merge(a, o, b, true);
21870
21871         for (var i = 0; i < hunks.length; i++) {
21872             var hunk = hunks[i];
21873             if (hunk.ok) {
21874                 nodes.push.apply(nodes, hunk.ok);
21875             } else {
21876                 // for all conflicts, we can assume c.a !== c.b
21877                 // because `diff3_merge` called with `true` option to exclude false conflicts..
21878                 var c = hunk.conflict;
21879                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
21880                     nodes.push.apply(nodes, c.b);
21881                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
21882                     nodes.push.apply(nodes, c.a);
21883                 } else {       // changed both locally and remotely
21884                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
21885                     break;
21886                 }
21887             }
21888         }
21889
21890         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
21891     }
21892
21893
21894     function mergeChildren(targetWay, children, updates, graph) {
21895         function isUsed(node, targetWay) {
21896             var parentWays = _.pluck(graph.parentWays(node), 'id');
21897             return node.hasInterestingTags() ||
21898                 _.without(parentWays, targetWay.id).length > 0 ||
21899                 graph.parentRelations(node).length > 0;
21900         }
21901
21902         var ccount = conflicts.length;
21903
21904         for (var i = 0; i < children.length; i++) {
21905             var id = children[i],
21906                 node = graph.hasEntity(id);
21907
21908             // remove unused childNodes..
21909             if (targetWay.nodes.indexOf(id) === -1) {
21910                 if (node && !isUsed(node, targetWay)) {
21911                     updates.removeIds.push(id);
21912                 }
21913                 continue;
21914             }
21915
21916             // restore used childNodes..
21917             var local = localGraph.hasEntity(id),
21918                 remote = remoteGraph.hasEntity(id),
21919                 target;
21920
21921             if (option === 'force_remote' && remote && remote.visible) {
21922                 updates.replacements.push(remote);
21923
21924             } else if (option === 'force_local' && local) {
21925                 target = iD.Entity(local);
21926                 if (remote) {
21927                     target = target.update({ version: remote.version });
21928                 }
21929                 updates.replacements.push(target);
21930
21931             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
21932                 target = iD.Entity(local, { version: remote.version });
21933                 if (remote.visible) {
21934                     target = mergeLocation(remote, target);
21935                 } else {
21936                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
21937                 }
21938
21939                 if (conflicts.length !== ccount) break;
21940                 updates.replacements.push(target);
21941             }
21942         }
21943
21944         return targetWay;
21945     }
21946
21947
21948     function updateChildren(updates, graph) {
21949         for (var i = 0; i < updates.replacements.length; i++) {
21950             graph = graph.replace(updates.replacements[i]);
21951         }
21952         if (updates.removeIds.length) {
21953             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
21954         }
21955         return graph;
21956     }
21957
21958
21959     function mergeMembers(remote, target) {
21960         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
21961             return target;
21962         }
21963         if (option === 'force_remote') {
21964             return target.update({members: remote.members});
21965         }
21966
21967         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
21968         return target;
21969     }
21970
21971
21972     function mergeTags(base, remote, target) {
21973         function ignoreKey(k) {
21974             return _.contains(iD.data.discarded, k);
21975         }
21976
21977         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
21978             return target;
21979         }
21980         if (option === 'force_remote') {
21981             return target.update({tags: remote.tags});
21982         }
21983
21984         var ccount = conflicts.length,
21985             o = base.tags || {},
21986             a = target.tags || {},
21987             b = remote.tags || {},
21988             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
21989             tags = _.clone(a),
21990             changed = false;
21991
21992         for (var i = 0; i < keys.length; i++) {
21993             var k = keys[i];
21994
21995             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
21996                 if (o[k] !== a[k]) {      // changed locally..
21997                     conflicts.push(t('merge_remote_changes.conflict.tags',
21998                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
21999
22000                 } else {                  // unchanged locally, accept remote change..
22001                     if (b.hasOwnProperty(k)) {
22002                         tags[k] = b[k];
22003                     } else {
22004                         delete tags[k];
22005                     }
22006                     changed = true;
22007                 }
22008             }
22009         }
22010
22011         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
22012     }
22013
22014
22015     //  `graph.base()` is the common ancestor of the two graphs.
22016     //  `localGraph` contains user's edits up to saving
22017     //  `remoteGraph` contains remote edits to modified nodes
22018     //  `graph` must be a descendent of `localGraph` and may include
22019     //      some conflict resolution actions performed on it.
22020     //
22021     //                  --- ... --- `localGraph` -- ... -- `graph`
22022     //                 /
22023     //  `graph.base()` --- ... --- `remoteGraph`
22024     //
22025     var action = function(graph) {
22026         var updates = { replacements: [], removeIds: [] },
22027             base = graph.base().entities[id],
22028             local = localGraph.entity(id),
22029             remote = remoteGraph.entity(id),
22030             target = iD.Entity(local, { version: remote.version });
22031
22032         // delete/undelete
22033         if (!remote.visible) {
22034             if (option === 'force_remote') {
22035                 return iD.actions.DeleteMultiple([id])(graph);
22036
22037             } else if (option === 'force_local') {
22038                 if (target.type === 'way') {
22039                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
22040                     graph = updateChildren(updates, graph);
22041                 }
22042                 return graph.replace(target);
22043
22044             } else {
22045                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22046                 return graph;  // do nothing
22047             }
22048         }
22049
22050         // merge
22051         if (target.type === 'node') {
22052             target = mergeLocation(remote, target);
22053
22054         } else if (target.type === 'way') {
22055             // pull in any child nodes that may not be present locally..
22056             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
22057             target = mergeNodes(base, remote, target);
22058             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
22059
22060         } else if (target.type === 'relation') {
22061             target = mergeMembers(remote, target);
22062         }
22063
22064         target = mergeTags(base, remote, target);
22065
22066         if (!conflicts.length) {
22067             graph = updateChildren(updates, graph).replace(target);
22068         }
22069
22070         return graph;
22071     };
22072
22073     action.withOption = function(opt) {
22074         option = opt;
22075         return action;
22076     };
22077
22078     action.conflicts = function() {
22079         return conflicts;
22080     };
22081
22082     return action;
22083 };
22084 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22085 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22086 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
22087     var delta = tryDelta;
22088
22089     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
22090     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
22091
22092     function setupCache(graph) {
22093         function canMove(nodeId) {
22094             var parents = _.pluck(graph.parentWays(graph.entity(nodeId)), 'id');
22095             if (parents.length < 3) return true;
22096
22097             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
22098             var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
22099             if (!parentsMoving) delete cache.moving[nodeId];
22100
22101             return parentsMoving;
22102         }
22103
22104         function cacheEntities(ids) {
22105             _.each(ids, function(id) {
22106                 if (cache.moving[id]) return;
22107                 cache.moving[id] = true;
22108
22109                 var entity = graph.hasEntity(id);
22110                 if (!entity) return;
22111
22112                 if (entity.type === 'node') {
22113                     cache.nodes.push(id);
22114                     cache.startLoc[id] = entity.loc;
22115                 } else if (entity.type === 'way') {
22116                     cache.ways.push(id);
22117                     cacheEntities(entity.nodes);
22118                 } else {
22119                     cacheEntities(_.pluck(entity.members, 'id'));
22120                 }
22121             });
22122         }
22123
22124         function cacheIntersections(ids) {
22125             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
22126
22127             _.each(ids, function(id) {
22128                 // consider only intersections with 1 moved and 1 unmoved way.
22129                 _.each(graph.childNodes(graph.entity(id)), function(node) {
22130                     var parents = graph.parentWays(node);
22131                     if (parents.length !== 2) return;
22132
22133                     var moved = graph.entity(id),
22134                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
22135                     if (!unmoved) return;
22136
22137                     // exclude ways that are overly connected..
22138                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
22139
22140                     if (moved.isArea() || unmoved.isArea()) return;
22141
22142                     cache.intersection[node.id] = {
22143                         nodeId: node.id,
22144                         movedId: moved.id,
22145                         unmovedId: unmoved.id,
22146                         movedIsEP: isEndpoint(moved, node.id),
22147                         unmovedIsEP: isEndpoint(unmoved, node.id)
22148                     };
22149                 });
22150             });
22151         }
22152
22153
22154         if (!cache) {
22155             cache = {};
22156         }
22157         if (!cache.ok) {
22158             cache.moving = {};
22159             cache.intersection = {};
22160             cache.replacedVertex = {};
22161             cache.startLoc = {};
22162             cache.nodes = [];
22163             cache.ways = [];
22164
22165             cacheEntities(moveIds);
22166             cacheIntersections(cache.ways);
22167             cache.nodes = _.filter(cache.nodes, canMove);
22168
22169             cache.ok = true;
22170         }
22171     }
22172
22173
22174     // Place a vertex where the moved vertex used to be, to preserve way shape..
22175     function replaceMovedVertex(nodeId, wayId, graph, delta) {
22176         var way = graph.entity(wayId),
22177             moved = graph.entity(nodeId),
22178             movedIndex = way.nodes.indexOf(nodeId),
22179             len, prevIndex, nextIndex;
22180
22181         if (way.isClosed()) {
22182             len = way.nodes.length - 1;
22183             prevIndex = (movedIndex + len - 1) % len;
22184             nextIndex = (movedIndex + len + 1) % len;
22185         } else {
22186             len = way.nodes.length;
22187             prevIndex = movedIndex - 1;
22188             nextIndex = movedIndex + 1;
22189         }
22190
22191         var prev = graph.hasEntity(way.nodes[prevIndex]),
22192             next = graph.hasEntity(way.nodes[nextIndex]);
22193
22194         // Don't add orig vertex at endpoint..
22195         if (!prev || !next) return graph;
22196
22197         var key = wayId + '_' + nodeId,
22198             orig = cache.replacedVertex[key];
22199         if (!orig) {
22200             orig = iD.Node();
22201             cache.replacedVertex[key] = orig;
22202             cache.startLoc[orig.id] = cache.startLoc[nodeId];
22203         }
22204
22205         var start, end;
22206         if (delta) {
22207             start = projection(cache.startLoc[nodeId]);
22208             end = projection.invert(vecAdd(start, delta));
22209         } else {
22210             end = cache.startLoc[nodeId];
22211         }
22212         orig = orig.move(end);
22213
22214         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
22215                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
22216
22217         // Don't add orig vertex if it would just make a straight line..
22218         if (angle > 175 && angle < 185) return graph;
22219
22220         // Don't add orig vertex if another point is already nearby (within 10m)
22221         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
22222             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
22223
22224         // moving forward or backward along way?
22225         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
22226             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
22227             d1 = iD.geo.pathLength(p1),
22228             d2 = iD.geo.pathLength(p2),
22229             insertAt = (d1 < d2) ? movedIndex : nextIndex;
22230
22231         // moving around closed loop?
22232         if (way.isClosed() && insertAt === 0) insertAt = len;
22233
22234         way = way.addNode(orig.id, insertAt);
22235         return graph.replace(orig).replace(way);
22236     }
22237
22238     // Reorder nodes around intersections that have moved..
22239     function unZorroIntersection(intersection, graph) {
22240         var vertex = graph.entity(intersection.nodeId),
22241             way1 = graph.entity(intersection.movedId),
22242             way2 = graph.entity(intersection.unmovedId),
22243             isEP1 = intersection.movedIsEP,
22244             isEP2 = intersection.unmovedIsEP;
22245
22246         // don't move the vertex if it is the endpoint of both ways.
22247         if (isEP1 && isEP2) return graph;
22248
22249         var nodes1 = _.without(graph.childNodes(way1), vertex),
22250             nodes2 = _.without(graph.childNodes(way2), vertex);
22251
22252         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
22253         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
22254
22255         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
22256             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
22257             loc;
22258
22259         // snap vertex to nearest edge (or some point between them)..
22260         if (!isEP1 && !isEP2) {
22261             var epsilon = 1e-4, maxIter = 10;
22262             for (var i = 0; i < maxIter; i++) {
22263                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
22264                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
22265                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
22266                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
22267             }
22268         } else if (!isEP1) {
22269             loc = edge1.loc;
22270         } else {
22271             loc = edge2.loc;
22272         }
22273
22274         graph = graph.replace(vertex.move(loc));
22275
22276         // if zorro happened, reorder nodes..
22277         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
22278             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
22279             graph = graph.replace(way1);
22280         }
22281         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
22282             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
22283             graph = graph.replace(way2);
22284         }
22285
22286         return graph;
22287     }
22288
22289
22290     function cleanupIntersections(graph) {
22291         _.each(cache.intersection, function(obj) {
22292             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
22293             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
22294             graph = unZorroIntersection(obj, graph);
22295         });
22296
22297         return graph;
22298     }
22299
22300     // check if moving way endpoint can cross an unmoved way, if so limit delta..
22301     function limitDelta(graph) {
22302         _.each(cache.intersection, function(obj) {
22303             if (!obj.movedIsEP) return;
22304
22305             var node = graph.entity(obj.nodeId),
22306                 start = projection(node.loc),
22307                 end = vecAdd(start, delta),
22308                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
22309                 movedPath = _.map(_.pluck(movedNodes, 'loc'),
22310                     function(loc) { return vecAdd(projection(loc), delta); }),
22311                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
22312                 unmovedPath = _.map(_.pluck(unmovedNodes, 'loc'), projection),
22313                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
22314
22315             for (var i = 0; i < hits.length; i++) {
22316                 if (_.isEqual(hits[i], end)) continue;
22317                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
22318                 delta = vecSub(projection(edge.loc), start);
22319             }
22320         });
22321     }
22322
22323
22324     var action = function(graph) {
22325         if (delta[0] === 0 && delta[1] === 0) return graph;
22326
22327         setupCache(graph);
22328
22329         if (!_.isEmpty(cache.intersection)) {
22330             limitDelta(graph);
22331         }
22332
22333         _.each(cache.nodes, function(id) {
22334             var node = graph.entity(id),
22335                 start = projection(node.loc),
22336                 end = vecAdd(start, delta);
22337             graph = graph.replace(node.move(projection.invert(end)));
22338         });
22339
22340         if (!_.isEmpty(cache.intersection)) {
22341             graph = cleanupIntersections(graph);
22342         }
22343
22344         return graph;
22345     };
22346
22347     action.disabled = function(graph) {
22348         function incompleteRelation(id) {
22349             var entity = graph.entity(id);
22350             return entity.type === 'relation' && !entity.isComplete(graph);
22351         }
22352
22353         if (_.any(moveIds, incompleteRelation))
22354             return 'incomplete_relation';
22355     };
22356
22357     action.delta = function() {
22358         return delta;
22359     };
22360
22361     return action;
22362 };
22363 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22364 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22365 iD.actions.MoveNode = function(nodeId, loc) {
22366     return function(graph) {
22367         return graph.replace(graph.entity(nodeId).move(loc));
22368     };
22369 };
22370 iD.actions.Noop = function() {
22371     return function(graph) {
22372         return graph;
22373     };
22374 };
22375 /*
22376  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
22377  */
22378
22379 iD.actions.Orthogonalize = function(wayId, projection) {
22380     var threshold = 12, // degrees within right or straight to alter
22381         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
22382         upperThreshold = Math.cos(threshold * Math.PI / 180);
22383
22384     var action = function(graph) {
22385         var way = graph.entity(wayId),
22386             nodes = graph.childNodes(way),
22387             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
22388             corner = {i: 0, dotp: 1},
22389             epsilon = 1e-4,
22390             i, j, score, motions;
22391
22392         if (nodes.length === 4) {
22393             for (i = 0; i < 1000; i++) {
22394                 motions = points.map(calcMotion);
22395                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
22396                 score = corner.dotp;
22397                 if (score < epsilon) {
22398                     break;
22399                 }
22400             }
22401
22402             graph = graph.replace(graph.entity(nodes[corner.i].id)
22403                 .move(projection.invert(points[corner.i])));
22404         } else {
22405             var best,
22406                 originalPoints = _.clone(points);
22407             score = Infinity;
22408
22409             for (i = 0; i < 1000; i++) {
22410                 motions = points.map(calcMotion);
22411                 for (j = 0; j < motions.length; j++) {
22412                     points[j] = addPoints(points[j],motions[j]);
22413                 }
22414                 var newScore = squareness(points);
22415                 if (newScore < score) {
22416                     best = _.clone(points);
22417                     score = newScore;
22418                 }
22419                 if (score < epsilon) {
22420                     break;
22421                 }
22422             }
22423
22424             points = best;
22425
22426             for (i = 0; i < points.length; i++) {
22427                 // only move the points that actually moved
22428                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
22429                     graph = graph.replace(graph.entity(nodes[i].id)
22430                         .move(projection.invert(points[i])));
22431                 }
22432             }
22433
22434             // remove empty nodes on straight sections
22435             for (i = 0; i < points.length; i++) {
22436                 var node = nodes[i];
22437
22438                 if (graph.parentWays(node).length > 1 ||
22439                     graph.parentRelations(node).length ||
22440                     node.hasInterestingTags()) {
22441
22442                     continue;
22443                 }
22444
22445                 var dotp = normalizedDotProduct(i, points);
22446                 if (dotp < -1 + epsilon) {
22447                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
22448                 }
22449             }
22450         }
22451
22452         return graph;
22453
22454         function calcMotion(b, i, array) {
22455             var a = array[(i - 1 + array.length) % array.length],
22456                 c = array[(i + 1) % array.length],
22457                 p = subtractPoints(a, b),
22458                 q = subtractPoints(c, b),
22459                 scale, dotp;
22460
22461             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
22462             p = normalizePoint(p, 1.0);
22463             q = normalizePoint(q, 1.0);
22464
22465             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
22466
22467             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
22468             if (array.length > 3) {
22469                 if (dotp < -0.707106781186547) {
22470                     dotp += 1.0;
22471                 }
22472             } else if (dotp && Math.abs(dotp) < corner.dotp) {
22473                 corner.i = i;
22474                 corner.dotp = Math.abs(dotp);
22475             }
22476
22477             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
22478         }
22479     };
22480
22481     function squareness(points) {
22482         return points.reduce(function(sum, val, i, array) {
22483             var dotp = normalizedDotProduct(i, array);
22484
22485             dotp = filterDotProduct(dotp);
22486             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
22487         }, 0);
22488     }
22489
22490     function normalizedDotProduct(i, points) {
22491         var a = points[(i - 1 + points.length) % points.length],
22492             b = points[i],
22493             c = points[(i + 1) % points.length],
22494             p = subtractPoints(a, b),
22495             q = subtractPoints(c, b);
22496
22497         p = normalizePoint(p, 1.0);
22498         q = normalizePoint(q, 1.0);
22499
22500         return p[0] * q[0] + p[1] * q[1];
22501     }
22502
22503     function subtractPoints(a, b) {
22504         return [a[0] - b[0], a[1] - b[1]];
22505     }
22506
22507     function addPoints(a, b) {
22508         return [a[0] + b[0], a[1] + b[1]];
22509     }
22510
22511     function normalizePoint(point, scale) {
22512         var vector = [0, 0];
22513         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
22514         if (length !== 0) {
22515             vector[0] = point[0] / length;
22516             vector[1] = point[1] / length;
22517         }
22518
22519         vector[0] *= scale;
22520         vector[1] *= scale;
22521
22522         return vector;
22523     }
22524
22525     function filterDotProduct(dotp) {
22526         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
22527             return dotp;
22528         }
22529
22530         return 0;
22531     }
22532
22533     action.disabled = function(graph) {
22534         var way = graph.entity(wayId),
22535             nodes = graph.childNodes(way),
22536             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
22537
22538         if (squareness(points)) {
22539             return false;
22540         }
22541
22542         return 'not_squarish';
22543     };
22544
22545     return action;
22546 };
22547 // Create a restriction relation for `turn`, which must have the following structure:
22548 //
22549 //     {
22550 //         from: { node: <node ID>, way: <way ID> },
22551 //         via:  { node: <node ID> },
22552 //         to:   { node: <node ID>, way: <way ID> },
22553 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
22554 //     }
22555 //
22556 // This specifies a restriction of type `restriction` when traveling from
22557 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
22558 // (The action does not check that these entities form a valid intersection.)
22559 //
22560 // If `restriction` is not provided, it is automatically determined by
22561 // iD.geo.inferRestriction.
22562 //
22563 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
22564 // and `to.node` are used to determine which portion of the split ways become
22565 // members of the restriction.
22566 //
22567 // For testing convenience, accepts an ID to assign to the new relation.
22568 // Normally, this will be undefined and the relation will automatically
22569 // be assigned a new ID.
22570 //
22571 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
22572     return function(graph) {
22573         var from = graph.entity(turn.from.way),
22574             via  = graph.entity(turn.via.node),
22575             to   = graph.entity(turn.to.way);
22576
22577         function isClosingNode(way, nodeId) {
22578             return nodeId === way.first() && nodeId === way.last();
22579         }
22580
22581         function split(toOrFrom) {
22582             var newID = toOrFrom.newID || iD.Way().id;
22583             graph = iD.actions.Split(via.id, [newID])
22584                 .limitWays([toOrFrom.way])(graph);
22585
22586             var a = graph.entity(newID),
22587                 b = graph.entity(toOrFrom.way);
22588
22589             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
22590                 return [a, b];
22591             } else {
22592                 return [b, a];
22593             }
22594         }
22595
22596         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
22597             if (turn.from.node === turn.to.node) {
22598                 // U-turn
22599                 from = to = split(turn.from)[0];
22600             } else if (turn.from.way === turn.to.way) {
22601                 // Straight-on or circular
22602                 var s = split(turn.from);
22603                 from = s[0];
22604                 to   = s[1];
22605             } else {
22606                 // Other
22607                 from = split(turn.from)[0];
22608             }
22609         }
22610
22611         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
22612             to = split(turn.to)[0];
22613         }
22614
22615         return graph.replace(iD.Relation({
22616             id: restrictionId,
22617             tags: {
22618                 type: 'restriction',
22619                 restriction: turn.restriction ||
22620                     iD.geo.inferRestriction(
22621                         graph,
22622                         turn.from,
22623                         turn.via,
22624                         turn.to,
22625                         projection)
22626             },
22627             members: [
22628                 {id: from.id, type: 'way',  role: 'from'},
22629                 {id: via.id,  type: 'node', role: 'via'},
22630                 {id: to.id,   type: 'way',  role: 'to'}
22631             ]
22632         }));
22633     };
22634 };
22635 /*
22636   Order the nodes of a way in reverse order and reverse any direction dependent tags
22637   other than `oneway`. (We assume that correcting a backwards oneway is the primary
22638   reason for reversing a way.)
22639
22640   The following transforms are performed:
22641
22642     Keys:
22643           *:right=* ⟺ *:left=*
22644         *:forward=* ⟺ *:backward=*
22645        direction=up ⟺ direction=down
22646          incline=up ⟺ incline=down
22647             *=right ⟺ *=left
22648
22649     Relation members:
22650        role=forward ⟺ role=backward
22651          role=north ⟺ role=south
22652           role=east ⟺ role=west
22653
22654    In addition, numeric-valued `incline` tags are negated.
22655
22656    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
22657    or adjusted tags that don't seem to be used in practice were omitted.
22658
22659    References:
22660       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
22661       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
22662       http://wiki.openstreetmap.org/wiki/Key:incline
22663       http://wiki.openstreetmap.org/wiki/Route#Members
22664       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
22665  */
22666 iD.actions.Reverse = function(wayId, options) {
22667     var replacements = [
22668             [/:right$/, ':left'], [/:left$/, ':right'],
22669             [/:forward$/, ':backward'], [/:backward$/, ':forward']
22670         ],
22671         numeric = /^([+\-]?)(?=[\d.])/,
22672         roleReversals = {
22673             forward: 'backward',
22674             backward: 'forward',
22675             north: 'south',
22676             south: 'north',
22677             east: 'west',
22678             west: 'east'
22679         };
22680
22681     function reverseKey(key) {
22682         for (var i = 0; i < replacements.length; ++i) {
22683             var replacement = replacements[i];
22684             if (replacement[0].test(key)) {
22685                 return key.replace(replacement[0], replacement[1]);
22686             }
22687         }
22688         return key;
22689     }
22690
22691     function reverseValue(key, value) {
22692         if (key === 'incline' && numeric.test(value)) {
22693             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
22694         } else if (key === 'incline' || key === 'direction') {
22695             return {up: 'down', down: 'up'}[value] || value;
22696         } else if (options && options.reverseOneway && key === 'oneway') {
22697             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
22698         } else {
22699             return {left: 'right', right: 'left'}[value] || value;
22700         }
22701     }
22702
22703     return function(graph) {
22704         var way = graph.entity(wayId),
22705             nodes = way.nodes.slice().reverse(),
22706             tags = {}, key, role;
22707
22708         for (key in way.tags) {
22709             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
22710         }
22711
22712         graph.parentRelations(way).forEach(function(relation) {
22713             relation.members.forEach(function(member, index) {
22714                 if (member.id === way.id && (role = roleReversals[member.role])) {
22715                     relation = relation.updateMember({role: role}, index);
22716                     graph = graph.replace(relation);
22717                 }
22718             });
22719         });
22720
22721         return graph.replace(way.update({nodes: nodes, tags: tags}));
22722     };
22723 };
22724 iD.actions.Revert = function(id) {
22725
22726     var action = function(graph) {
22727         var entity = graph.hasEntity(id),
22728             base = graph.base().entities[id];
22729
22730         if (entity && !base) {    // entity will be removed..
22731             if (entity.type === 'node') {
22732                 graph.parentWays(entity)
22733                     .forEach(function(parent) {
22734                         parent = parent.removeNode(id);
22735                         graph = graph.replace(parent);
22736
22737                         if (parent.isDegenerate()) {
22738                             graph = iD.actions.DeleteWay(parent.id)(graph);
22739                         }
22740                     });
22741             }
22742
22743             graph.parentRelations(entity)
22744                 .forEach(function(parent) {
22745                     parent = parent.removeMembersWithID(id);
22746                     graph = graph.replace(parent);
22747
22748                     if (parent.isDegenerate()) {
22749                         graph = iD.actions.DeleteRelation(parent.id)(graph);
22750                     }
22751                 });
22752         }
22753
22754         return graph.revert(id);
22755     };
22756
22757     return action;
22758 };
22759 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
22760     return function(graph) {
22761         return graph.update(function(graph) {
22762             var way = graph.entity(wayId);
22763
22764             _.unique(way.nodes).forEach(function(id) {
22765
22766                 var node = graph.entity(id),
22767                     point = projection(node.loc),
22768                     radial = [0,0];
22769
22770                 radial[0] = point[0] - pivot[0];
22771                 radial[1] = point[1] - pivot[1];
22772
22773                 point = [
22774                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
22775                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
22776                 ];
22777
22778                 graph = graph.replace(node.move(projection.invert(point)));
22779
22780             });
22781
22782         });
22783     };
22784 };
22785 // Split a way at the given node.
22786 //
22787 // Optionally, split only the given ways, if multiple ways share
22788 // the given node.
22789 //
22790 // This is the inverse of `iD.actions.Join`.
22791 //
22792 // For testing convenience, accepts an ID to assign to the new way.
22793 // Normally, this will be undefined and the way will automatically
22794 // be assigned a new ID.
22795 //
22796 // Reference:
22797 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
22798 //
22799 iD.actions.Split = function(nodeId, newWayIds) {
22800     var wayIds;
22801
22802     // if the way is closed, we need to search for a partner node
22803     // to split the way at.
22804     //
22805     // The following looks for a node that is both far away from
22806     // the initial node in terms of way segment length and nearby
22807     // in terms of beeline-distance. This assures that areas get
22808     // split on the most "natural" points (independent of the number
22809     // of nodes).
22810     // For example: bone-shaped areas get split across their waist
22811     // line, circles across the diameter.
22812     function splitArea(nodes, idxA, graph) {
22813         var lengths = new Array(nodes.length),
22814             length,
22815             i,
22816             best = 0,
22817             idxB;
22818
22819         function wrap(index) {
22820             return iD.util.wrap(index, nodes.length);
22821         }
22822
22823         function dist(nA, nB) {
22824             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
22825         }
22826
22827         // calculate lengths
22828         length = 0;
22829         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
22830             length += dist(nodes[i], nodes[wrap(i-1)]);
22831             lengths[i] = length;
22832         }
22833
22834         length = 0;
22835         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
22836             length += dist(nodes[i], nodes[wrap(i+1)]);
22837             if (length < lengths[i])
22838                 lengths[i] = length;
22839         }
22840
22841         // determine best opposite node to split
22842         for (i = 0; i < nodes.length; i++) {
22843             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
22844             if (cost > best) {
22845                 idxB = i;
22846                 best = cost;
22847             }
22848         }
22849
22850         return idxB;
22851     }
22852
22853     function split(graph, wayA, newWayId) {
22854         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
22855             nodesA,
22856             nodesB,
22857             isArea = wayA.isArea(),
22858             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
22859
22860         if (wayA.isClosed()) {
22861             var nodes = wayA.nodes.slice(0, -1),
22862                 idxA = _.indexOf(nodes, nodeId),
22863                 idxB = splitArea(nodes, idxA, graph);
22864
22865             if (idxB < idxA) {
22866                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
22867                 nodesB = nodes.slice(idxB, idxA + 1);
22868             } else {
22869                 nodesA = nodes.slice(idxA, idxB + 1);
22870                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
22871             }
22872         } else {
22873             var idx = _.indexOf(wayA.nodes, nodeId, 1);
22874             nodesA = wayA.nodes.slice(0, idx + 1);
22875             nodesB = wayA.nodes.slice(idx);
22876         }
22877
22878         wayA = wayA.update({nodes: nodesA});
22879         wayB = wayB.update({nodes: nodesB});
22880
22881         graph = graph.replace(wayA);
22882         graph = graph.replace(wayB);
22883
22884         graph.parentRelations(wayA).forEach(function(relation) {
22885             if (relation.isRestriction()) {
22886                 var via = relation.memberByRole('via');
22887                 if (via && wayB.contains(via.id)) {
22888                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
22889                     graph = graph.replace(relation);
22890                 }
22891             } else {
22892                 if (relation === isOuter) {
22893                     graph = graph.replace(relation.mergeTags(wayA.tags));
22894                     graph = graph.replace(wayA.update({tags: {}}));
22895                     graph = graph.replace(wayB.update({tags: {}}));
22896                 }
22897
22898                 var member = {
22899                     id: wayB.id,
22900                     type: 'way',
22901                     role: relation.memberById(wayA.id).role
22902                 };
22903
22904                 graph = iD.actions.AddMember(relation.id, member)(graph);
22905             }
22906         });
22907
22908         if (!isOuter && isArea) {
22909             var multipolygon = iD.Relation({
22910                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
22911                 members: [
22912                     {id: wayA.id, role: 'outer', type: 'way'},
22913                     {id: wayB.id, role: 'outer', type: 'way'}
22914                 ]});
22915
22916             graph = graph.replace(multipolygon);
22917             graph = graph.replace(wayA.update({tags: {}}));
22918             graph = graph.replace(wayB.update({tags: {}}));
22919         }
22920
22921         return graph;
22922     }
22923
22924     var action = function(graph) {
22925         var candidates = action.ways(graph);
22926         for (var i = 0; i < candidates.length; i++) {
22927             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
22928         }
22929         return graph;
22930     };
22931
22932     action.ways = function(graph) {
22933         var node = graph.entity(nodeId),
22934             parents = graph.parentWays(node),
22935             hasLines = _.any(parents, function(parent) { return parent.geometry(graph) === 'line'; });
22936
22937         return parents.filter(function(parent) {
22938             if (wayIds && wayIds.indexOf(parent.id) === -1)
22939                 return false;
22940
22941             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
22942                 return false;
22943
22944             if (parent.isClosed()) {
22945                 return true;
22946             }
22947
22948             for (var i = 1; i < parent.nodes.length - 1; i++) {
22949                 if (parent.nodes[i] === nodeId) {
22950                     return true;
22951                 }
22952             }
22953
22954             return false;
22955         });
22956     };
22957
22958     action.disabled = function(graph) {
22959         var candidates = action.ways(graph);
22960         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
22961             return 'not_eligible';
22962     };
22963
22964     action.limitWays = function(_) {
22965         if (!arguments.length) return wayIds;
22966         wayIds = _;
22967         return action;
22968     };
22969
22970     return action;
22971 };
22972 /*
22973  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
22974  */
22975
22976 iD.actions.Straighten = function(wayId, projection) {
22977     function positionAlongWay(n, s, e) {
22978         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
22979                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
22980     }
22981
22982     var action = function(graph) {
22983         var way = graph.entity(wayId),
22984             nodes = graph.childNodes(way),
22985             points = nodes.map(function(n) { return projection(n.loc); }),
22986             startPoint = points[0],
22987             endPoint = points[points.length-1],
22988             toDelete = [],
22989             i;
22990
22991         for (i = 1; i < points.length-1; i++) {
22992             var node = nodes[i],
22993                 point = points[i];
22994
22995             if (graph.parentWays(node).length > 1 ||
22996                 graph.parentRelations(node).length ||
22997                 node.hasInterestingTags()) {
22998
22999                 var u = positionAlongWay(point, startPoint, endPoint),
23000                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23001                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
23002
23003                 graph = graph.replace(graph.entity(node.id)
23004                     .move(projection.invert([p0, p1])));
23005             } else {
23006                 // safe to delete
23007                 if (toDelete.indexOf(node) === -1) {
23008                     toDelete.push(node);
23009                 }
23010             }
23011         }
23012
23013         for (i = 0; i < toDelete.length; i++) {
23014             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
23015         }
23016
23017         return graph;
23018     };
23019     
23020     action.disabled = function(graph) {
23021         // check way isn't too bendy
23022         var way = graph.entity(wayId),
23023             nodes = graph.childNodes(way),
23024             points = nodes.map(function(n) { return projection(n.loc); }),
23025             startPoint = points[0],
23026             endPoint = points[points.length-1],
23027             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
23028             i;
23029
23030         for (i = 1; i < points.length-1; i++) {
23031             var point = points[i],
23032                 u = positionAlongWay(point, startPoint, endPoint),
23033                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23034                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
23035                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
23036
23037             // to bendy if point is off by 20% of total start/end distance in projected space
23038             if (dist > threshold) {
23039                 return 'too_bendy';
23040             }
23041         }
23042     };
23043
23044     return action;
23045 };
23046 // Remove the effects of `turn.restriction` on `turn`, which must have the
23047 // following structure:
23048 //
23049 //     {
23050 //         from: { node: <node ID>, way: <way ID> },
23051 //         via:  { node: <node ID> },
23052 //         to:   { node: <node ID>, way: <way ID> },
23053 //         restriction: <relation ID>
23054 //     }
23055 //
23056 // In the simple case, `restriction` is a reference to a `no_*` restriction
23057 // on the turn itself. In this case, it is simply deleted.
23058 //
23059 // The more complex case is where `restriction` references an `only_*`
23060 // restriction on a different turn in the same intersection. In that case,
23061 // that restriction is also deleted, but at the same time restrictions on
23062 // the turns other than the first two are created.
23063 //
23064 iD.actions.UnrestrictTurn = function(turn) {
23065     return function(graph) {
23066         return iD.actions.DeleteRelation(turn.restriction)(graph);
23067     };
23068 };
23069 iD.behavior = {};
23070 iD.behavior.AddWay = function(context) {
23071     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
23072         draw = iD.behavior.Draw(context);
23073
23074     var addWay = function(surface) {
23075         draw.on('click', event.start)
23076             .on('clickWay', event.startFromWay)
23077             .on('clickNode', event.startFromNode)
23078             .on('cancel', addWay.cancel)
23079             .on('finish', addWay.cancel);
23080
23081         context.map()
23082             .dblclickEnable(false);
23083
23084         surface.call(draw);
23085     };
23086
23087     addWay.off = function(surface) {
23088         surface.call(draw.off);
23089     };
23090
23091     addWay.cancel = function() {
23092         window.setTimeout(function() {
23093             context.map().dblclickEnable(true);
23094         }, 1000);
23095
23096         context.enter(iD.modes.Browse(context));
23097     };
23098
23099     addWay.tail = function(text) {
23100         draw.tail(text);
23101         return addWay;
23102     };
23103
23104     return d3.rebind(addWay, event, 'on');
23105 };
23106 iD.behavior.Breathe = function() {
23107     var duration = 800,
23108         selector = '.selected.shadow, .selected .shadow',
23109         selected = d3.select(null),
23110         classed = '',
23111         params = {},
23112         done;
23113
23114     function reset(selection) {
23115         selection
23116             .style('stroke-opacity', null)
23117             .style('stroke-width', null)
23118             .style('fill-opacity', null)
23119             .style('r', null);
23120     }
23121
23122     function setAnimationParams(transition, fromTo) {
23123         transition
23124             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
23125             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
23126             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
23127             .style('r', function(d) { return params[d.id][fromTo].width; });
23128     }
23129
23130     function calcAnimationParams(selection) {
23131         selection
23132             .call(reset)
23133             .each(function(d) {
23134                 var s = d3.select(this),
23135                     tag = s.node().tagName,
23136                     p = {'from': {}, 'to': {}},
23137                     opacity, width;
23138
23139                 // determine base opacity and width
23140                 if (tag === 'circle') {
23141                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
23142                     width = parseFloat(s.style('r') || 15.5);
23143                 } else {
23144                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
23145                     width = parseFloat(s.style('stroke-width') || 10);
23146                 }
23147
23148                 // calculate from/to interpolation params..
23149                 p.tag = tag;
23150                 p.from.opacity = opacity * 0.6;
23151                 p.to.opacity = opacity * 1.25;
23152                 p.from.width = width * 0.9;
23153                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
23154                 params[d.id] = p;
23155             });
23156     }
23157
23158     function run(surface, fromTo) {
23159         var toFrom = (fromTo === 'from' ? 'to': 'from'),
23160             currSelected = surface.selectAll(selector),
23161             currClassed = surface.attr('class'),
23162             n = 0;
23163
23164         if (done || currSelected.empty()) {
23165             selected.call(reset);
23166             return;
23167         }
23168
23169         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
23170             selected.call(reset);
23171             classed = currClassed;
23172             selected = currSelected.call(calcAnimationParams);
23173         }
23174
23175         selected
23176             .transition()
23177             .call(setAnimationParams, fromTo)
23178             .duration(duration)
23179             .each(function() { ++n; })
23180             .each('end', function() {
23181                 if (!--n) {  // call once
23182                     surface.call(run, toFrom);
23183                 }
23184             });
23185     }
23186
23187     var breathe = function(surface) {
23188         done = false;
23189         d3.timer(function() {
23190             if (done) return true;
23191
23192             var currSelected = surface.selectAll(selector);
23193             if (currSelected.empty()) return false;
23194
23195             surface.call(run, 'from');
23196             return true;
23197         }, 200);
23198     };
23199
23200     breathe.off = function() {
23201         done = true;
23202         d3.timer.flush();
23203         selected
23204             .transition()
23205             .call(reset)
23206             .duration(0);
23207     };
23208
23209     return breathe;
23210 };
23211 iD.behavior.Copy = function(context) {
23212     var keybinding = d3.keybinding('copy');
23213
23214     function groupEntities(ids, graph) {
23215         var entities = ids.map(function (id) { return graph.entity(id); });
23216         return _.extend({relation: [], way: [], node: []},
23217             _.groupBy(entities, function(entity) { return entity.type; }));
23218     }
23219
23220     function getDescendants(id, graph, descendants) {
23221         var entity = graph.entity(id),
23222             i, children;
23223
23224         descendants = descendants || {};
23225
23226         if (entity.type === 'relation') {
23227             children = _.pluck(entity.members, 'id');
23228         } else if (entity.type === 'way') {
23229             children = entity.nodes;
23230         } else {
23231             children = [];
23232         }
23233
23234         for (i = 0; i < children.length; i++) {
23235             if (!descendants[children[i]]) {
23236                 descendants[children[i]] = true;
23237                 descendants = getDescendants(children[i], graph, descendants);
23238             }
23239         }
23240
23241         return descendants;
23242     }
23243
23244     function doCopy() {
23245         d3.event.preventDefault();
23246         if (context.inIntro()) return;
23247
23248         var graph = context.graph(),
23249             selected = groupEntities(context.selectedIDs(), graph),
23250             canCopy = [],
23251             skip = {},
23252             i, entity;
23253
23254         for (i = 0; i < selected.relation.length; i++) {
23255             entity = selected.relation[i];
23256             if (!skip[entity.id] && entity.isComplete(graph)) {
23257                 canCopy.push(entity.id);
23258                 skip = getDescendants(entity.id, graph, skip);
23259             }
23260         }
23261         for (i = 0; i < selected.way.length; i++) {
23262             entity = selected.way[i];
23263             if (!skip[entity.id]) {
23264                 canCopy.push(entity.id);
23265                 skip = getDescendants(entity.id, graph, skip);
23266             }
23267         }
23268         for (i = 0; i < selected.node.length; i++) {
23269             entity = selected.node[i];
23270             if (!skip[entity.id]) {
23271                 canCopy.push(entity.id);
23272             }
23273         }
23274
23275         context.copyIDs(canCopy);
23276     }
23277
23278     function copy() {
23279         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
23280         d3.select(document).call(keybinding);
23281         return copy;
23282     }
23283
23284     copy.off = function() {
23285         d3.select(document).call(keybinding.off);
23286     };
23287
23288     return copy;
23289 };
23290 /*
23291     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
23292
23293     * The `origin` function is expected to return an [x, y] tuple rather than an
23294       {x, y} object.
23295     * The events are `start`, `move`, and `end`.
23296       (https://github.com/mbostock/d3/issues/563)
23297     * The `start` event is not dispatched until the first cursor movement occurs.
23298       (https://github.com/mbostock/d3/pull/368)
23299     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
23300       than `x`, `y`, `dx`, and `dy` properties.
23301     * The `end` event is not dispatched if no movement occurs.
23302     * An `off` function is available that unbinds the drag's internal event handlers.
23303     * Delegation is supported via the `delegate` function.
23304
23305  */
23306 iD.behavior.drag = function() {
23307     function d3_eventCancel() {
23308       d3.event.stopPropagation();
23309       d3.event.preventDefault();
23310     }
23311
23312     var event = d3.dispatch('start', 'move', 'end'),
23313         origin = null,
23314         selector = '',
23315         filter = null,
23316         event_, target, surface;
23317
23318     event.of = function(thiz, argumentz) {
23319       return function(e1) {
23320         var e0 = e1.sourceEvent = d3.event;
23321         e1.target = drag;
23322         d3.event = e1;
23323         try {
23324           event[e1.type].apply(thiz, argumentz);
23325         } finally {
23326           d3.event = e0;
23327         }
23328       };
23329     };
23330
23331     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
23332         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
23333             function () {
23334                 var selection = d3.selection(),
23335                     select = selection.style(d3_event_userSelectProperty);
23336                 selection.style(d3_event_userSelectProperty, 'none');
23337                 return function () {
23338                     selection.style(d3_event_userSelectProperty, select);
23339                 };
23340             } :
23341             function (type) {
23342                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
23343                 return function () {
23344                     w.on('selectstart.' + type, null);
23345                 };
23346             };
23347
23348     function mousedown() {
23349         target = this;
23350         event_ = event.of(target, arguments);
23351         var eventTarget = d3.event.target,
23352             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23353             offset,
23354             origin_ = point(),
23355             started = false,
23356             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
23357
23358         var w = d3.select(window)
23359             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
23360             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
23361
23362         if (origin) {
23363             offset = origin.apply(target, arguments);
23364             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
23365         } else {
23366             offset = [0, 0];
23367         }
23368
23369         if (touchId === null) d3.event.stopPropagation();
23370
23371         function point() {
23372             var p = target.parentNode || surface;
23373             return touchId !== null ? d3.touches(p).filter(function(p) {
23374                 return p.identifier === touchId;
23375             })[0] : d3.mouse(p);
23376         }
23377
23378         function dragmove() {
23379
23380             var p = point(),
23381                 dx = p[0] - origin_[0],
23382                 dy = p[1] - origin_[1];
23383             
23384             if (dx === 0 && dy === 0)
23385                 return;
23386
23387             if (!started) {
23388                 started = true;
23389                 event_({
23390                     type: 'start'
23391                 });
23392             }
23393
23394             origin_ = p;
23395             d3_eventCancel();
23396
23397             event_({
23398                 type: 'move',
23399                 point: [p[0] + offset[0],  p[1] + offset[1]],
23400                 delta: [dx, dy]
23401             });
23402         }
23403
23404         function dragend() {
23405             if (started) {
23406                 event_({
23407                     type: 'end'
23408                 });
23409
23410                 d3_eventCancel();
23411                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
23412             }
23413
23414             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
23415                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
23416             selectEnable();
23417         }
23418
23419         function click() {
23420             d3_eventCancel();
23421             w.on('click.drag', null);
23422         }
23423     }
23424
23425     function drag(selection) {
23426         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
23427             delegate = mousedown;
23428
23429         if (selector) {
23430             delegate = function() {
23431                 var root = this,
23432                     target = d3.event.target;
23433                 for (; target && target !== root; target = target.parentNode) {
23434                     if (target[matchesSelector](selector) &&
23435                             (!filter || filter(target.__data__))) {
23436                         return mousedown.call(target, target.__data__);
23437                     }
23438                 }
23439             };
23440         }
23441
23442         selection.on('mousedown.drag' + selector, delegate)
23443             .on('touchstart.drag' + selector, delegate);
23444     }
23445
23446     drag.off = function(selection) {
23447         selection.on('mousedown.drag' + selector, null)
23448             .on('touchstart.drag' + selector, null);
23449     };
23450
23451     drag.delegate = function(_) {
23452         if (!arguments.length) return selector;
23453         selector = _;
23454         return drag;
23455     };
23456
23457     drag.filter = function(_) {
23458         if (!arguments.length) return origin;
23459         filter = _;
23460         return drag;
23461     };
23462
23463     drag.origin = function (_) {
23464         if (!arguments.length) return origin;
23465         origin = _;
23466         return drag;
23467     };
23468
23469     drag.cancel = function() {
23470         d3.select(window)
23471             .on('mousemove.drag', null)
23472             .on('mouseup.drag', null);
23473         return drag;
23474     };
23475
23476     drag.target = function() {
23477         if (!arguments.length) return target;
23478         target = arguments[0];
23479         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
23480         return drag;
23481     };
23482
23483     drag.surface = function() {
23484         if (!arguments.length) return surface;
23485         surface = arguments[0];
23486         return drag;
23487     };
23488
23489     return d3.rebind(drag, event, 'on');
23490 };
23491 iD.behavior.Draw = function(context) {
23492     var event = d3.dispatch('move', 'click', 'clickWay',
23493             'clickNode', 'undo', 'cancel', 'finish'),
23494         keybinding = d3.keybinding('draw'),
23495         hover = iD.behavior.Hover(context)
23496             .altDisables(true)
23497             .on('hover', context.ui().sidebar.hover),
23498         tail = iD.behavior.Tail(),
23499         edit = iD.behavior.Edit(context),
23500         closeTolerance = 4,
23501         tolerance = 12;
23502
23503     function datum() {
23504         if (d3.event.altKey) return {};
23505         else return d3.event.target.__data__ || {};
23506     }
23507
23508     function mousedown() {
23509
23510         function point() {
23511             var p = context.container().node();
23512             return touchId !== null ? d3.touches(p).filter(function(p) {
23513                 return p.identifier === touchId;
23514             })[0] : d3.mouse(p);
23515         }
23516
23517         var element = d3.select(this),
23518             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23519             t1 = +new Date(),
23520             p1 = point();
23521
23522         element.on('mousemove.draw', null);
23523
23524         d3.select(window).on('mouseup.draw', function() {
23525             var t2 = +new Date(),
23526                 p2 = point(),
23527                 dist = iD.geo.euclideanDistance(p1, p2);
23528
23529             element.on('mousemove.draw', mousemove);
23530             d3.select(window).on('mouseup.draw', null);
23531
23532             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
23533                 // Prevent a quick second click
23534                 d3.select(window).on('click.draw-block', function() {
23535                     d3.event.stopPropagation();
23536                 }, true);
23537
23538                 context.map().dblclickEnable(false);
23539
23540                 window.setTimeout(function() {
23541                     context.map().dblclickEnable(true);
23542                     d3.select(window).on('click.draw-block', null);
23543                 }, 500);
23544
23545                 click();
23546             }
23547         });
23548     }
23549
23550     function mousemove() {
23551         event.move(datum());
23552     }
23553
23554     function click() {
23555         var d = datum();
23556         if (d.type === 'way') {
23557             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
23558                 edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
23559             event.clickWay(choice.loc, edge);
23560
23561         } else if (d.type === 'node') {
23562             event.clickNode(d);
23563
23564         } else {
23565             event.click(context.map().mouseCoordinates());
23566         }
23567     }
23568
23569     function backspace() {
23570         d3.event.preventDefault();
23571         event.undo();
23572     }
23573
23574     function del() {
23575         d3.event.preventDefault();
23576         event.cancel();
23577     }
23578
23579     function ret() {
23580         d3.event.preventDefault();
23581         event.finish();
23582     }
23583
23584     function draw(selection) {
23585         context.install(hover);
23586         context.install(edit);
23587
23588         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23589             context.install(tail);
23590         }
23591
23592         keybinding
23593             .on('⌫', backspace)
23594             .on('⌦', del)
23595             .on('⎋', ret)
23596             .on('↩', ret);
23597
23598         selection
23599             .on('mousedown.draw', mousedown)
23600             .on('mousemove.draw', mousemove);
23601
23602         d3.select(document)
23603             .call(keybinding);
23604
23605         return draw;
23606     }
23607
23608     draw.off = function(selection) {
23609         context.ui().sidebar.hover.cancel();
23610         context.uninstall(hover);
23611         context.uninstall(edit);
23612
23613         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23614             context.uninstall(tail);
23615             iD.behavior.Draw.usedTails[tail.text()] = true;
23616         }
23617
23618         selection
23619             .on('mousedown.draw', null)
23620             .on('mousemove.draw', null);
23621
23622         d3.select(window)
23623             .on('mouseup.draw', null);
23624
23625         d3.select(document)
23626             .call(keybinding.off);
23627     };
23628
23629     draw.tail = function(_) {
23630         tail.text(_);
23631         return draw;
23632     };
23633
23634     return d3.rebind(draw, event, 'on');
23635 };
23636
23637 iD.behavior.Draw.usedTails = {};
23638 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
23639     var way = context.entity(wayId),
23640         isArea = context.geometry(wayId) === 'area',
23641         finished = false,
23642         annotation = t((way.isDegenerate() ?
23643             'operations.start.annotation.' :
23644             'operations.continue.annotation.') + context.geometry(wayId)),
23645         draw = iD.behavior.Draw(context);
23646
23647     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
23648         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
23649         end = iD.Node({loc: context.map().mouseCoordinates()}),
23650         segment = iD.Way({
23651             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
23652             tags: _.clone(way.tags)
23653         });
23654
23655     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
23656     if (isArea) {
23657         f(iD.actions.AddEntity(end),
23658             iD.actions.AddVertex(wayId, end.id, index));
23659     } else {
23660         f(iD.actions.AddEntity(start),
23661             iD.actions.AddEntity(end),
23662             iD.actions.AddEntity(segment));
23663     }
23664
23665     function move(datum) {
23666         var loc;
23667
23668         if (datum.type === 'node' && datum.id !== end.id) {
23669             loc = datum.loc;
23670         } else if (datum.type === 'way' && datum.id !== segment.id) {
23671             loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
23672         } else {
23673             loc = context.map().mouseCoordinates();
23674         }
23675
23676         context.replace(iD.actions.MoveNode(end.id, loc));
23677     }
23678
23679     function undone() {
23680         finished = true;
23681         context.enter(iD.modes.Browse(context));
23682     }
23683
23684     function setActiveElements() {
23685         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
23686         context.surface().selectAll(iD.util.entitySelector(active))
23687             .classed('active', true);
23688     }
23689
23690     var drawWay = function(surface) {
23691         draw.on('move', move)
23692             .on('click', drawWay.add)
23693             .on('clickWay', drawWay.addWay)
23694             .on('clickNode', drawWay.addNode)
23695             .on('undo', context.undo)
23696             .on('cancel', drawWay.cancel)
23697             .on('finish', drawWay.finish);
23698
23699         context.map()
23700             .dblclickEnable(false)
23701             .on('drawn.draw', setActiveElements);
23702
23703         setActiveElements();
23704
23705         surface.call(draw);
23706
23707         context.history()
23708             .on('undone.draw', undone);
23709     };
23710
23711     drawWay.off = function(surface) {
23712         if (!finished)
23713             context.pop();
23714
23715         context.map()
23716             .on('drawn.draw', null);
23717
23718         surface.call(draw.off)
23719             .selectAll('.active')
23720             .classed('active', false);
23721
23722         context.history()
23723             .on('undone.draw', null);
23724     };
23725
23726     function ReplaceTemporaryNode(newNode) {
23727         return function(graph) {
23728             if (isArea) {
23729                 return graph
23730                     .replace(way.addNode(newNode.id, index))
23731                     .remove(end);
23732
23733             } else {
23734                 return graph
23735                     .replace(graph.entity(wayId).addNode(newNode.id, index))
23736                     .remove(end)
23737                     .remove(segment)
23738                     .remove(start);
23739             }
23740         };
23741     }
23742
23743     // Accept the current position of the temporary node and continue drawing.
23744     drawWay.add = function(loc) {
23745
23746         // prevent duplicate nodes
23747         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
23748         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
23749
23750         var newNode = iD.Node({loc: loc});
23751
23752         context.replace(
23753             iD.actions.AddEntity(newNode),
23754             ReplaceTemporaryNode(newNode),
23755             annotation);
23756
23757         finished = true;
23758         context.enter(mode);
23759     };
23760
23761     // Connect the way to an existing way.
23762     drawWay.addWay = function(loc, edge) {
23763         var previousEdge = startIndex ?
23764             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
23765             [way.nodes[0], way.nodes[1]];
23766
23767         // Avoid creating duplicate segments
23768         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
23769             return;
23770
23771         var newNode = iD.Node({ loc: loc });
23772
23773         context.perform(
23774             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
23775             ReplaceTemporaryNode(newNode),
23776             annotation);
23777
23778         finished = true;
23779         context.enter(mode);
23780     };
23781
23782     // Connect the way to an existing node and continue drawing.
23783     drawWay.addNode = function(node) {
23784
23785         // Avoid creating duplicate segments
23786         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
23787
23788         context.perform(
23789             ReplaceTemporaryNode(node),
23790             annotation);
23791
23792         finished = true;
23793         context.enter(mode);
23794     };
23795
23796     // Finish the draw operation, removing the temporary node. If the way has enough
23797     // nodes to be valid, it's selected. Otherwise, return to browse mode.
23798     drawWay.finish = function() {
23799         context.pop();
23800         finished = true;
23801
23802         window.setTimeout(function() {
23803             context.map().dblclickEnable(true);
23804         }, 1000);
23805
23806         if (context.hasEntity(wayId)) {
23807             context.enter(
23808                 iD.modes.Select(context, [wayId])
23809                     .suppressMenu(true)
23810                     .newFeature(true));
23811         } else {
23812             context.enter(iD.modes.Browse(context));
23813         }
23814     };
23815
23816     // Cancel the draw operation and return to browse, deleting everything drawn.
23817     drawWay.cancel = function() {
23818         context.perform(
23819             d3.functor(baseGraph),
23820             t('operations.cancel_draw.annotation'));
23821
23822         window.setTimeout(function() {
23823             context.map().dblclickEnable(true);
23824         }, 1000);
23825
23826         finished = true;
23827         context.enter(iD.modes.Browse(context));
23828     };
23829
23830     drawWay.tail = function(text) {
23831         draw.tail(text);
23832         return drawWay;
23833     };
23834
23835     return drawWay;
23836 };
23837 iD.behavior.Edit = function(context) {
23838     function edit() {
23839         context.map()
23840             .minzoom(context.minEditableZoom());
23841     }
23842
23843     edit.off = function() {
23844         context.map()
23845             .minzoom(0);
23846     };
23847
23848     return edit;
23849 };
23850 iD.behavior.Hash = function(context) {
23851     var s0 = null, // cached location.hash
23852         lat = 90 - 1e-8; // allowable latitude range
23853
23854     var parser = function(map, s) {
23855         var q = iD.util.stringQs(s);
23856         var args = (q.map || '').split('/').map(Number);
23857         if (args.length < 3 || args.some(isNaN)) {
23858             return true; // replace bogus hash
23859         } else if (s !== formatter(map).slice(1)) {
23860             map.centerZoom([args[1],
23861                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
23862         }
23863     };
23864
23865     var formatter = function(map) {
23866         var mode = context.mode(),
23867             center = map.center(),
23868             zoom = map.zoom(),
23869             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
23870             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
23871             newParams = {};
23872
23873         if (mode && mode.id === 'browse') {
23874             delete q.id;
23875         } else {
23876             var selected = context.selectedIDs().filter(function(id) {
23877                 return !context.entity(id).isNew();
23878             });
23879             if (selected.length) {
23880                 newParams.id = selected.join(',');
23881             }
23882         }
23883
23884         newParams.map = zoom.toFixed(2) +
23885                 '/' + center[0].toFixed(precision) +
23886                 '/' + center[1].toFixed(precision);
23887
23888         return '#' + iD.util.qsString(_.assign(q, newParams), true);
23889     };
23890
23891     function update() {
23892         if (context.inIntro()) return;
23893         var s1 = formatter(context.map());
23894         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
23895     }
23896
23897     var throttledUpdate = _.throttle(update, 500);
23898
23899     function hashchange() {
23900         if (location.hash === s0) return; // ignore spurious hashchange events
23901         if (parser(context.map(), (s0 = location.hash).substring(1))) {
23902             update(); // replace bogus hash
23903         }
23904     }
23905
23906     function hash() {
23907         context.map()
23908             .on('move.hash', throttledUpdate);
23909
23910         context
23911             .on('enter.hash', throttledUpdate);
23912
23913         d3.select(window)
23914             .on('hashchange.hash', hashchange);
23915
23916         if (location.hash) {
23917             var q = iD.util.stringQs(location.hash.substring(1));
23918             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
23919             if (q.comment) context.storage('comment', q.comment);
23920             hashchange();
23921             if (q.map) hash.hadHash = true;
23922         }
23923     }
23924
23925     hash.off = function() {
23926         throttledUpdate.cancel();
23927
23928         context.map()
23929             .on('move.hash', null);
23930
23931         context
23932             .on('enter.hash', null);
23933
23934         d3.select(window)
23935             .on('hashchange.hash', null);
23936
23937         location.hash = '';
23938     };
23939
23940     return hash;
23941 };
23942 /*
23943    The hover behavior adds the `.hover` class on mouseover to all elements to which
23944    the identical datum is bound, and removes it on mouseout.
23945
23946    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
23947    representation may consist of several elements scattered throughout the DOM hierarchy.
23948    Only one of these elements can have the :hover pseudo-class, but all of them will
23949    have the .hover class.
23950  */
23951 iD.behavior.Hover = function() {
23952     var dispatch = d3.dispatch('hover'),
23953         selection,
23954         altDisables,
23955         target;
23956
23957     function keydown() {
23958         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
23959             dispatch.hover(null);
23960             selection.selectAll('.hover')
23961                 .classed('hover-suppressed', true)
23962                 .classed('hover', false);
23963         }
23964     }
23965
23966     function keyup() {
23967         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
23968             dispatch.hover(target ? target.id : null);
23969             selection.selectAll('.hover-suppressed')
23970                 .classed('hover-suppressed', false)
23971                 .classed('hover', true);
23972         }
23973     }
23974
23975     var hover = function(__) {
23976         selection = __;
23977
23978         function enter(d) {
23979             if (d === target) return;
23980
23981             target = d;
23982
23983             selection.selectAll('.hover')
23984                 .classed('hover', false);
23985             selection.selectAll('.hover-suppressed')
23986                 .classed('hover-suppressed', false);
23987
23988             if (target instanceof iD.Entity) {
23989                 var selector = '.' + target.id;
23990
23991                 if (target.type === 'relation') {
23992                     target.members.forEach(function(member) {
23993                         selector += ', .' + member.id;
23994                     });
23995                 }
23996
23997                 var suppressed = altDisables && d3.event && d3.event.altKey;
23998
23999                 selection.selectAll(selector)
24000                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
24001
24002                 dispatch.hover(target.id);
24003             } else {
24004                 dispatch.hover(null);
24005             }
24006         }
24007
24008         var down;
24009
24010         function mouseover() {
24011             if (down) return;
24012             var target = d3.event.target;
24013             enter(target ? target.__data__ : null);
24014         }
24015
24016         function mouseout() {
24017             if (down) return;
24018             var target = d3.event.relatedTarget;
24019             enter(target ? target.__data__ : null);
24020         }
24021
24022         function mousedown() {
24023             down = true;
24024             d3.select(window)
24025                 .on('mouseup.hover', mouseup);
24026         }
24027
24028         function mouseup() {
24029             down = false;
24030         }
24031
24032         selection
24033             .on('mouseover.hover', mouseover)
24034             .on('mouseout.hover', mouseout)
24035             .on('mousedown.hover', mousedown)
24036             .on('mouseup.hover', mouseup);
24037
24038         d3.select(window)
24039             .on('keydown.hover', keydown)
24040             .on('keyup.hover', keyup);
24041     };
24042
24043     hover.off = function(selection) {
24044         selection.selectAll('.hover')
24045             .classed('hover', false);
24046         selection.selectAll('.hover-suppressed')
24047             .classed('hover-suppressed', false);
24048
24049         selection
24050             .on('mouseover.hover', null)
24051             .on('mouseout.hover', null)
24052             .on('mousedown.hover', null)
24053             .on('mouseup.hover', null);
24054
24055         d3.select(window)
24056             .on('keydown.hover', null)
24057             .on('keyup.hover', null)
24058             .on('mouseup.hover', null);
24059     };
24060
24061     hover.altDisables = function(_) {
24062         if (!arguments.length) return altDisables;
24063         altDisables = _;
24064         return hover;
24065     };
24066
24067     return d3.rebind(hover, dispatch, 'on');
24068 };
24069 iD.behavior.Lasso = function(context) {
24070
24071     var behavior = function(selection) {
24072         var mouse = null,
24073             lasso;
24074
24075         function mousedown() {
24076             if (d3.event.shiftKey === true) {
24077
24078                 mouse = context.mouse();
24079                 lasso = null;
24080
24081                 selection
24082                     .on('mousemove.lasso', mousemove)
24083                     .on('mouseup.lasso', mouseup);
24084
24085                 d3.event.stopPropagation();
24086             }
24087         }
24088
24089         function mousemove() {
24090             if (!lasso) {
24091                 lasso = iD.ui.Lasso(context).a(mouse);
24092                 context.surface().call(lasso);
24093             }
24094
24095             lasso.b(context.mouse());
24096         }
24097
24098         function normalize(a, b) {
24099             return [
24100                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
24101                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
24102         }
24103
24104         function mouseup() {
24105             selection
24106                 .on('mousemove.lasso', null)
24107                 .on('mouseup.lasso', null);
24108
24109             if (!lasso) return;
24110
24111             var graph = context.graph(),
24112                 extent = iD.geo.Extent(
24113                 normalize(context.projection.invert(lasso.a()),
24114                 context.projection.invert(lasso.b())));
24115
24116             lasso.close();
24117
24118             var selected = context.intersects(extent).filter(function(entity) {
24119                 return entity.type === 'node' &&
24120                     !context.features().isHidden(entity, graph, entity.geometry(graph));
24121             });
24122
24123             if (selected.length) {
24124                 context.enter(iD.modes.Select(context, _.pluck(selected, 'id')));
24125             }
24126         }
24127
24128         selection
24129             .on('mousedown.lasso', mousedown);
24130     };
24131
24132     behavior.off = function(selection) {
24133         selection.on('mousedown.lasso', null);
24134     };
24135
24136     return behavior;
24137 };
24138 iD.behavior.Paste = function(context) {
24139     var keybinding = d3.keybinding('paste');
24140
24141     function omitTag(v, k) {
24142         return (
24143             k === 'phone' ||
24144             k === 'fax' ||
24145             k === 'email' ||
24146             k === 'website' ||
24147             k === 'url' ||
24148             k === 'note' ||
24149             k === 'description' ||
24150             k.indexOf('name') !== -1 ||
24151             k.indexOf('wiki') === 0 ||
24152             k.indexOf('addr:') === 0 ||
24153             k.indexOf('contact:') === 0
24154         );
24155     }
24156
24157     function doPaste() {
24158         d3.event.preventDefault();
24159         if (context.inIntro()) return;
24160
24161         var baseGraph = context.graph(),
24162             mouse = context.mouse(),
24163             projection = context.projection,
24164             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
24165
24166         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
24167
24168         var extent = iD.geo.Extent(),
24169             oldIDs = context.copyIDs(),
24170             oldGraph = context.copyGraph(),
24171             newIDs = [];
24172
24173         if (!oldIDs.length) return;
24174
24175         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
24176         context.perform(action);
24177
24178         var copies = action.copies();
24179         for (var id in copies) {
24180             var oldEntity = oldGraph.entity(id),
24181                 newEntity = copies[id];
24182
24183             extent._extend(oldEntity.extent(oldGraph));
24184             newIDs.push(newEntity.id);
24185             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
24186         }
24187
24188         // Put pasted objects where mouse pointer is..
24189         var center = projection(extent.center()),
24190             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
24191
24192         context.perform(iD.actions.Move(newIDs, delta, projection));
24193         context.enter(iD.modes.Move(context, newIDs, baseGraph));
24194     }
24195
24196     function paste() {
24197         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
24198         d3.select(document).call(keybinding);
24199         return paste;
24200     }
24201
24202     paste.off = function() {
24203         d3.select(document).call(keybinding.off);
24204     };
24205
24206     return paste;
24207 };
24208 iD.behavior.Select = function(context) {
24209     function keydown() {
24210         if (d3.event && d3.event.shiftKey) {
24211             context.surface()
24212                 .classed('behavior-multiselect', true);
24213         }
24214     }
24215
24216     function keyup() {
24217         if (!d3.event || !d3.event.shiftKey) {
24218             context.surface()
24219                 .classed('behavior-multiselect', false);
24220         }
24221     }
24222
24223     function click() {
24224         var datum = d3.event.target.__data__,
24225             lasso = d3.select('#surface .lasso').node(),
24226             mode = context.mode();
24227
24228         if (!(datum instanceof iD.Entity)) {
24229             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
24230                 context.enter(iD.modes.Browse(context));
24231
24232         } else if (!d3.event.shiftKey && !lasso) {
24233             // Avoid re-entering Select mode with same entity.
24234             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
24235                 context.enter(iD.modes.Select(context, [datum.id]));
24236             } else {
24237                 mode.suppressMenu(false).reselect();
24238             }
24239         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
24240             var selectedIDs = _.without(context.selectedIDs(), datum.id);
24241             context.enter(selectedIDs.length ?
24242                 iD.modes.Select(context, selectedIDs) :
24243                 iD.modes.Browse(context));
24244
24245         } else {
24246             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
24247         }
24248     }
24249
24250     var behavior = function(selection) {
24251         d3.select(window)
24252             .on('keydown.select', keydown)
24253             .on('keyup.select', keyup);
24254
24255         selection.on('click.select', click);
24256
24257         keydown();
24258     };
24259
24260     behavior.off = function(selection) {
24261         d3.select(window)
24262             .on('keydown.select', null)
24263             .on('keyup.select', null);
24264
24265         selection.on('click.select', null);
24266
24267         keyup();
24268     };
24269
24270     return behavior;
24271 };
24272 iD.behavior.Tail = function() {
24273     var text,
24274         container,
24275         xmargin = 25,
24276         tooltipSize = [0, 0],
24277         selectionSize = [0, 0];
24278
24279     function tail(selection) {
24280         if (!text) return;
24281
24282         d3.select(window)
24283             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
24284
24285         function show() {
24286             container.style('display', 'block');
24287             tooltipSize = container.dimensions();
24288         }
24289
24290         function mousemove() {
24291             if (container.style('display') === 'none') show();
24292             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
24293                 -tooltipSize[0] - xmargin : xmargin;
24294             container.classed('left', xoffset > 0);
24295             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
24296         }
24297
24298         function mouseleave() {
24299             if (d3.event.relatedTarget !== container.node()) {
24300                 container.style('display', 'none');
24301             }
24302         }
24303
24304         function mouseenter() {
24305             if (d3.event.relatedTarget !== container.node()) {
24306                 show();
24307             }
24308         }
24309
24310         container = d3.select(document.body)
24311             .append('div')
24312             .style('display', 'none')
24313             .attr('class', 'tail tooltip-inner');
24314
24315         container.append('div')
24316             .text(text);
24317
24318         selection
24319             .on('mousemove.tail', mousemove)
24320             .on('mouseenter.tail', mouseenter)
24321             .on('mouseleave.tail', mouseleave);
24322
24323         container
24324             .on('mousemove.tail', mousemove);
24325
24326         tooltipSize = container.dimensions();
24327         selectionSize = selection.dimensions();
24328     }
24329
24330     tail.off = function(selection) {
24331         if (!text) return;
24332
24333         container
24334             .on('mousemove.tail', null)
24335             .remove();
24336
24337         selection
24338             .on('mousemove.tail', null)
24339             .on('mouseenter.tail', null)
24340             .on('mouseleave.tail', null);
24341
24342         d3.select(window)
24343             .on('resize.tail', null);
24344     };
24345
24346     tail.text = function(_) {
24347         if (!arguments.length) return text;
24348         text = _;
24349         return tail;
24350     };
24351
24352     return tail;
24353 };
24354 iD.modes = {};
24355 iD.modes.AddArea = function(context) {
24356     var mode = {
24357         id: 'add-area',
24358         button: 'area',
24359         title: t('modes.add_area.title'),
24360         description: t('modes.add_area.description'),
24361         key: '3'
24362     };
24363
24364     var behavior = iD.behavior.AddWay(context)
24365             .tail(t('modes.add_area.tail'))
24366             .on('start', start)
24367             .on('startFromWay', startFromWay)
24368             .on('startFromNode', startFromNode),
24369         defaultTags = {area: 'yes'};
24370
24371     function start(loc) {
24372         var graph = context.graph(),
24373             node = iD.Node({loc: loc}),
24374             way = iD.Way({tags: defaultTags});
24375
24376         context.perform(
24377             iD.actions.AddEntity(node),
24378             iD.actions.AddEntity(way),
24379             iD.actions.AddVertex(way.id, node.id),
24380             iD.actions.AddVertex(way.id, node.id));
24381
24382         context.enter(iD.modes.DrawArea(context, way.id, graph));
24383     }
24384
24385     function startFromWay(loc, edge) {
24386         var graph = context.graph(),
24387             node = iD.Node({loc: loc}),
24388             way = iD.Way({tags: defaultTags});
24389
24390         context.perform(
24391             iD.actions.AddEntity(node),
24392             iD.actions.AddEntity(way),
24393             iD.actions.AddVertex(way.id, node.id),
24394             iD.actions.AddVertex(way.id, node.id),
24395             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24396
24397         context.enter(iD.modes.DrawArea(context, way.id, graph));
24398     }
24399
24400     function startFromNode(node) {
24401         var graph = context.graph(),
24402             way = iD.Way({tags: defaultTags});
24403
24404         context.perform(
24405             iD.actions.AddEntity(way),
24406             iD.actions.AddVertex(way.id, node.id),
24407             iD.actions.AddVertex(way.id, node.id));
24408
24409         context.enter(iD.modes.DrawArea(context, way.id, graph));
24410     }
24411
24412     mode.enter = function() {
24413         context.install(behavior);
24414     };
24415
24416     mode.exit = function() {
24417         context.uninstall(behavior);
24418     };
24419
24420     return mode;
24421 };
24422 iD.modes.AddLine = function(context) {
24423     var mode = {
24424         id: 'add-line',
24425         button: 'line',
24426         title: t('modes.add_line.title'),
24427         description: t('modes.add_line.description'),
24428         key: '2'
24429     };
24430
24431     var behavior = iD.behavior.AddWay(context)
24432         .tail(t('modes.add_line.tail'))
24433         .on('start', start)
24434         .on('startFromWay', startFromWay)
24435         .on('startFromNode', startFromNode);
24436
24437     function start(loc) {
24438         var baseGraph = context.graph(),
24439             node = iD.Node({loc: loc}),
24440             way = iD.Way();
24441
24442         context.perform(
24443             iD.actions.AddEntity(node),
24444             iD.actions.AddEntity(way),
24445             iD.actions.AddVertex(way.id, node.id));
24446
24447         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24448     }
24449
24450     function startFromWay(loc, edge) {
24451         var baseGraph = context.graph(),
24452             node = iD.Node({loc: loc}),
24453             way = iD.Way();
24454
24455         context.perform(
24456             iD.actions.AddEntity(node),
24457             iD.actions.AddEntity(way),
24458             iD.actions.AddVertex(way.id, node.id),
24459             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24460
24461         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24462     }
24463
24464     function startFromNode(node) {
24465         var baseGraph = context.graph(),
24466             way = iD.Way();
24467
24468         context.perform(
24469             iD.actions.AddEntity(way),
24470             iD.actions.AddVertex(way.id, node.id));
24471
24472         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24473     }
24474
24475     mode.enter = function() {
24476         context.install(behavior);
24477     };
24478
24479     mode.exit = function() {
24480         context.uninstall(behavior);
24481     };
24482
24483     return mode;
24484 };
24485 iD.modes.AddPoint = function(context) {
24486     var mode = {
24487         id: 'add-point',
24488         button: 'point',
24489         title: t('modes.add_point.title'),
24490         description: t('modes.add_point.description'),
24491         key: '1'
24492     };
24493
24494     var behavior = iD.behavior.Draw(context)
24495         .tail(t('modes.add_point.tail'))
24496         .on('click', add)
24497         .on('clickWay', addWay)
24498         .on('clickNode', addNode)
24499         .on('cancel', cancel)
24500         .on('finish', cancel);
24501
24502     function add(loc) {
24503         var node = iD.Node({loc: loc});
24504
24505         context.perform(
24506             iD.actions.AddEntity(node),
24507             t('operations.add.annotation.point'));
24508
24509         context.enter(
24510             iD.modes.Select(context, [node.id])
24511                 .suppressMenu(true)
24512                 .newFeature(true));
24513     }
24514
24515     function addWay(loc) {
24516         add(loc);
24517     }
24518
24519     function addNode(node) {
24520         add(node.loc);
24521     }
24522
24523     function cancel() {
24524         context.enter(iD.modes.Browse(context));
24525     }
24526
24527     mode.enter = function() {
24528         context.install(behavior);
24529     };
24530
24531     mode.exit = function() {
24532         context.uninstall(behavior);
24533     };
24534
24535     return mode;
24536 };
24537 iD.modes.Browse = function(context) {
24538     var mode = {
24539         button: 'browse',
24540         id: 'browse',
24541         title: t('modes.browse.title'),
24542         description: t('modes.browse.description')
24543     }, sidebar;
24544
24545     var behaviors = [
24546         iD.behavior.Paste(context),
24547         iD.behavior.Hover(context)
24548             .on('hover', context.ui().sidebar.hover),
24549         iD.behavior.Select(context),
24550         iD.behavior.Lasso(context),
24551         iD.modes.DragNode(context).behavior];
24552
24553     mode.enter = function() {
24554         behaviors.forEach(function(behavior) {
24555             context.install(behavior);
24556         });
24557
24558         // Get focus on the body.
24559         if (document.activeElement && document.activeElement.blur) {
24560             document.activeElement.blur();
24561         }
24562
24563         if (sidebar) {
24564             context.ui().sidebar.show(sidebar);
24565         } else {
24566             context.ui().sidebar.select(null);
24567         }
24568     };
24569
24570     mode.exit = function() {
24571         context.ui().sidebar.hover.cancel();
24572         behaviors.forEach(function(behavior) {
24573             context.uninstall(behavior);
24574         });
24575
24576         if (sidebar) {
24577             context.ui().sidebar.hide();
24578         }
24579     };
24580
24581     mode.sidebar = function(_) {
24582         if (!arguments.length) return sidebar;
24583         sidebar = _;
24584         return mode;
24585     };
24586
24587     return mode;
24588 };
24589 iD.modes.DragNode = function(context) {
24590     var mode = {
24591         id: 'drag-node',
24592         button: 'browse'
24593     };
24594
24595     var nudgeInterval,
24596         activeIDs,
24597         wasMidpoint,
24598         cancelled,
24599         selectedIDs = [],
24600         hover = iD.behavior.Hover(context)
24601             .altDisables(true)
24602             .on('hover', context.ui().sidebar.hover),
24603         edit = iD.behavior.Edit(context);
24604
24605     function edge(point, size) {
24606         var pad = [30, 100, 30, 100];
24607         if (point[0] > size[0] - pad[0]) return [-10, 0];
24608         else if (point[0] < pad[2]) return [10, 0];
24609         else if (point[1] > size[1] - pad[1]) return [0, -10];
24610         else if (point[1] < pad[3]) return [0, 10];
24611         return null;
24612     }
24613
24614     function startNudge(nudge) {
24615         if (nudgeInterval) window.clearInterval(nudgeInterval);
24616         nudgeInterval = window.setInterval(function() {
24617             context.pan(nudge);
24618         }, 50);
24619     }
24620
24621     function stopNudge() {
24622         if (nudgeInterval) window.clearInterval(nudgeInterval);
24623         nudgeInterval = null;
24624     }
24625
24626     function moveAnnotation(entity) {
24627         return t('operations.move.annotation.' + entity.geometry(context.graph()));
24628     }
24629
24630     function connectAnnotation(entity) {
24631         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24632     }
24633
24634     function origin(entity) {
24635         return context.projection(entity.loc);
24636     }
24637
24638     function start(entity) {
24639         cancelled = d3.event.sourceEvent.shiftKey ||
24640             context.features().hasHiddenConnections(entity, context.graph());
24641
24642         if (cancelled) return behavior.cancel();
24643
24644         wasMidpoint = entity.type === 'midpoint';
24645         if (wasMidpoint) {
24646             var midpoint = entity;
24647             entity = iD.Node();
24648             context.perform(iD.actions.AddMidpoint(midpoint, entity));
24649
24650              var vertex = context.surface()
24651                 .selectAll('.' + entity.id);
24652              behavior.target(vertex.node(), entity);
24653
24654         } else {
24655             context.perform(
24656                 iD.actions.Noop());
24657         }
24658
24659         activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24660         activeIDs.push(entity.id);
24661
24662         context.enter(mode);
24663     }
24664
24665     function datum() {
24666         if (d3.event.sourceEvent.altKey) {
24667             return {};
24668         }
24669
24670         return d3.event.sourceEvent.target.__data__ || {};
24671     }
24672
24673     // via https://gist.github.com/shawnbot/4166283
24674     function childOf(p, c) {
24675         if (p === c) return false;
24676         while (c && c !== p) c = c.parentNode;
24677         return c === p;
24678     }
24679
24680     function move(entity) {
24681         if (cancelled) return;
24682         d3.event.sourceEvent.stopPropagation();
24683
24684         var nudge = childOf(context.container().node(),
24685             d3.event.sourceEvent.toElement) &&
24686             edge(d3.event.point, context.map().dimensions());
24687
24688         if (nudge) startNudge(nudge);
24689         else stopNudge();
24690
24691         var loc = context.map().mouseCoordinates();
24692
24693         var d = datum();
24694         if (d.type === 'node' && d.id !== entity.id) {
24695             loc = d.loc;
24696         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24697             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24698         }
24699
24700         context.replace(
24701             iD.actions.MoveNode(entity.id, loc),
24702             moveAnnotation(entity));
24703     }
24704
24705     function end(entity) {
24706         if (cancelled) return;
24707
24708         var d = datum();
24709
24710         if (d.type === 'way') {
24711             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24712             context.replace(
24713                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24714                 connectAnnotation(d));
24715
24716         } else if (d.type === 'node' && d.id !== entity.id) {
24717             context.replace(
24718                 iD.actions.Connect([d.id, entity.id]),
24719                 connectAnnotation(d));
24720
24721         } else if (wasMidpoint) {
24722             context.replace(
24723                 iD.actions.Noop(),
24724                 t('operations.add.annotation.vertex'));
24725
24726         } else {
24727             context.replace(
24728                 iD.actions.Noop(),
24729                 moveAnnotation(entity));
24730         }
24731
24732         var reselection = selectedIDs.filter(function(id) {
24733             return context.graph().hasEntity(id);
24734         });
24735
24736         if (reselection.length) {
24737             context.enter(
24738                 iD.modes.Select(context, reselection)
24739                     .suppressMenu(true));
24740         } else {
24741             context.enter(iD.modes.Browse(context));
24742         }
24743     }
24744
24745     function cancel() {
24746         behavior.cancel();
24747         context.enter(iD.modes.Browse(context));
24748     }
24749
24750     function setActiveElements() {
24751         context.surface().selectAll(iD.util.entitySelector(activeIDs))
24752             .classed('active', true);
24753     }
24754
24755     var behavior = iD.behavior.drag()
24756         .delegate('g.node, g.point, g.midpoint')
24757         .surface(context.surface().node())
24758         .origin(origin)
24759         .on('start', start)
24760         .on('move', move)
24761         .on('end', end);
24762
24763     mode.enter = function() {
24764         context.install(hover);
24765         context.install(edit);
24766
24767         context.history()
24768             .on('undone.drag-node', cancel);
24769
24770         context.map()
24771             .on('drawn.drag-node', setActiveElements);
24772
24773         setActiveElements();
24774     };
24775
24776     mode.exit = function() {
24777         context.ui().sidebar.hover.cancel();
24778         context.uninstall(hover);
24779         context.uninstall(edit);
24780
24781         context.history()
24782             .on('undone.drag-node', null);
24783
24784         context.map()
24785             .on('drawn.drag-node', null);
24786
24787         context.surface()
24788             .selectAll('.active')
24789             .classed('active', false);
24790
24791         stopNudge();
24792     };
24793
24794     mode.selectedIDs = function(_) {
24795         if (!arguments.length) return selectedIDs;
24796         selectedIDs = _;
24797         return mode;
24798     };
24799
24800     mode.behavior = behavior;
24801
24802     return mode;
24803 };
24804 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24805     var mode = {
24806         button: 'area',
24807         id: 'draw-area'
24808     };
24809
24810     var behavior;
24811
24812     mode.enter = function() {
24813         var way = context.entity(wayId),
24814             headId = way.nodes[way.nodes.length - 2],
24815             tailId = way.first();
24816
24817         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24818             .tail(t('modes.draw_area.tail'));
24819
24820         var addNode = behavior.addNode;
24821
24822         behavior.addNode = function(node) {
24823             if (node.id === headId || node.id === tailId) {
24824                 behavior.finish();
24825             } else {
24826                 addNode(node);
24827             }
24828         };
24829
24830         context.install(behavior);
24831     };
24832
24833     mode.exit = function() {
24834         context.uninstall(behavior);
24835     };
24836
24837     mode.selectedIDs = function() {
24838         return [wayId];
24839     };
24840
24841     return mode;
24842 };
24843 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24844     var mode = {
24845         button: 'line',
24846         id: 'draw-line'
24847     };
24848
24849     var behavior;
24850
24851     mode.enter = function() {
24852         var way = context.entity(wayId),
24853             index = (affix === 'prefix') ? 0 : undefined,
24854             headId = (affix === 'prefix') ? way.first() : way.last();
24855
24856         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24857             .tail(t('modes.draw_line.tail'));
24858
24859         var addNode = behavior.addNode;
24860
24861         behavior.addNode = function(node) {
24862             if (node.id === headId) {
24863                 behavior.finish();
24864             } else {
24865                 addNode(node);
24866             }
24867         };
24868
24869         context.install(behavior);
24870     };
24871
24872     mode.exit = function() {
24873         context.uninstall(behavior);
24874     };
24875
24876     mode.selectedIDs = function() {
24877         return [wayId];
24878     };
24879
24880     return mode;
24881 };
24882 iD.modes.Move = function(context, entityIDs, baseGraph) {
24883     var mode = {
24884         id: 'move',
24885         button: 'browse'
24886     };
24887
24888     var keybinding = d3.keybinding('move'),
24889         edit = iD.behavior.Edit(context),
24890         annotation = entityIDs.length === 1 ?
24891             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24892             t('operations.move.annotation.multiple'),
24893         cache,
24894         origin,
24895         nudgeInterval;
24896
24897     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
24898
24899     function edge(point, size) {
24900         var pad = [30, 100, 30, 100];
24901         if (point[0] > size[0] - pad[0]) return [-10, 0];
24902         else if (point[0] < pad[2]) return [10, 0];
24903         else if (point[1] > size[1] - pad[1]) return [0, -10];
24904         else if (point[1] < pad[3]) return [0, 10];
24905         return null;
24906     }
24907
24908     function startNudge(nudge) {
24909         if (nudgeInterval) window.clearInterval(nudgeInterval);
24910         nudgeInterval = window.setInterval(function() {
24911             context.pan(nudge);
24912
24913             var currMouse = context.mouse(),
24914                 origMouse = context.projection(origin),
24915                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
24916                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24917
24918             context.overwrite(action, annotation);
24919
24920         }, 50);
24921     }
24922
24923     function stopNudge() {
24924         if (nudgeInterval) window.clearInterval(nudgeInterval);
24925         nudgeInterval = null;
24926     }
24927
24928     function move() {
24929         var currMouse = context.mouse(),
24930             origMouse = context.projection(origin),
24931             delta = vecSub(currMouse, origMouse),
24932             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24933
24934         context.overwrite(action, annotation);
24935
24936         var nudge = edge(currMouse, context.map().dimensions());
24937         if (nudge) startNudge(nudge);
24938         else stopNudge();
24939     }
24940
24941     function finish() {
24942         d3.event.stopPropagation();
24943         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24944         stopNudge();
24945     }
24946
24947     function cancel() {
24948         if (baseGraph) {
24949             while (context.graph() !== baseGraph) context.pop();
24950             context.enter(iD.modes.Browse(context));
24951         } else {
24952             context.pop();
24953             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24954         }
24955         stopNudge();
24956     }
24957
24958     function undone() {
24959         context.enter(iD.modes.Browse(context));
24960     }
24961
24962     mode.enter = function() {
24963         origin = context.map().mouseCoordinates();
24964         cache = {};
24965
24966         context.install(edit);
24967
24968         context.perform(
24969             iD.actions.Noop(),
24970             annotation);
24971
24972         context.surface()
24973             .on('mousemove.move', move)
24974             .on('click.move', finish);
24975
24976         context.history()
24977             .on('undone.move', undone);
24978
24979         keybinding
24980             .on('⎋', cancel)
24981             .on('↩', finish);
24982
24983         d3.select(document)
24984             .call(keybinding);
24985     };
24986
24987     mode.exit = function() {
24988         stopNudge();
24989
24990         context.uninstall(edit);
24991
24992         context.surface()
24993             .on('mousemove.move', null)
24994             .on('click.move', null);
24995
24996         context.history()
24997             .on('undone.move', null);
24998
24999         keybinding.off();
25000     };
25001
25002     return mode;
25003 };
25004 iD.modes.RotateWay = function(context, wayId) {
25005     var mode = {
25006         id: 'rotate-way',
25007         button: 'browse'
25008     };
25009
25010     var keybinding = d3.keybinding('rotate-way'),
25011         edit = iD.behavior.Edit(context);
25012
25013     mode.enter = function() {
25014         context.install(edit);
25015
25016         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
25017             way = context.graph().entity(wayId),
25018             nodes = _.uniq(context.graph().childNodes(way)),
25019             points = nodes.map(function(n) { return context.projection(n.loc); }),
25020             pivot = d3.geom.polygon(points).centroid(),
25021             angle;
25022
25023         context.perform(
25024             iD.actions.Noop(),
25025             annotation);
25026
25027         function rotate() {
25028
25029             var mousePoint = context.mouse(),
25030                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
25031
25032             if (typeof angle === 'undefined') angle = newAngle;
25033
25034             context.replace(
25035                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
25036                 annotation);
25037
25038             angle = newAngle;
25039         }
25040
25041         function finish() {
25042             d3.event.stopPropagation();
25043             context.enter(iD.modes.Select(context, [wayId])
25044                 .suppressMenu(true));
25045         }
25046
25047         function cancel() {
25048             context.pop();
25049             context.enter(iD.modes.Select(context, [wayId])
25050                 .suppressMenu(true));
25051         }
25052
25053         function undone() {
25054             context.enter(iD.modes.Browse(context));
25055         }
25056
25057         context.surface()
25058             .on('mousemove.rotate-way', rotate)
25059             .on('click.rotate-way', finish);
25060
25061         context.history()
25062             .on('undone.rotate-way', undone);
25063
25064         keybinding
25065             .on('⎋', cancel)
25066             .on('↩', finish);
25067
25068         d3.select(document)
25069             .call(keybinding);
25070     };
25071
25072     mode.exit = function() {
25073         context.uninstall(edit);
25074
25075         context.surface()
25076             .on('mousemove.rotate-way', null)
25077             .on('click.rotate-way', null);
25078
25079         context.history()
25080             .on('undone.rotate-way', null);
25081
25082         keybinding.off();
25083     };
25084
25085     return mode;
25086 };
25087 iD.modes.Save = function(context) {
25088     var ui = iD.ui.Commit(context)
25089             .on('cancel', cancel)
25090             .on('save', save);
25091
25092     function cancel() {
25093         context.enter(iD.modes.Browse(context));
25094     }
25095
25096     function save(e, tryAgain) {
25097         function withChildNodes(ids, graph) {
25098             return _.uniq(_.reduce(ids, function(result, id) {
25099                 var e = graph.entity(id);
25100                 if (e.type === 'way') {
25101                     try {
25102                         var cn = graph.childNodes(e);
25103                         result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
25104                     } catch(err) {
25105                         /* eslint-disable no-console */
25106                         if (typeof console !== 'undefined') console.error(err);
25107                         /* eslint-enable no-console */
25108                     }
25109                 }
25110                 return result;
25111             }, _.clone(ids)));
25112         }
25113
25114         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
25115             history = context.history(),
25116             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
25117             localGraph = context.graph(),
25118             remoteGraph = iD.Graph(history.base(), true),
25119             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
25120             toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
25121             toLoad = withChildNodes(toCheck, localGraph),
25122             conflicts = [],
25123             errors = [];
25124
25125         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
25126         context.container().call(loading);
25127
25128         if (toCheck.length) {
25129             context.connection().loadMultiple(toLoad, loaded);
25130         } else {
25131             finalize();
25132         }
25133
25134
25135         // Reload modified entities into an alternate graph and check for conflicts..
25136         function loaded(err, result) {
25137             if (errors.length) return;
25138
25139             if (err) {
25140                 errors.push({
25141                     msg: err.responseText,
25142                     details: [ t('save.status_code', { code: err.status }) ]
25143                 });
25144                 showErrors();
25145
25146             } else {
25147                 var loadMore = [];
25148                 _.each(result.data, function(entity) {
25149                     remoteGraph.replace(entity);
25150                     toLoad = _.without(toLoad, entity.id);
25151
25152                     // Because loadMultiple doesn't download /full like loadEntity,
25153                     // need to also load children that aren't already being checked..
25154                     if (!entity.visible) return;
25155                     if (entity.type === 'way') {
25156                         loadMore.push.apply(loadMore,
25157                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
25158                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
25159                         loadMore.push.apply(loadMore,
25160                             _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
25161                     }
25162                 });
25163
25164                 if (loadMore.length) {
25165                     toLoad.push.apply(toLoad, loadMore);
25166                     context.connection().loadMultiple(loadMore, loaded);
25167                 }
25168
25169                 if (!toLoad.length) {
25170                     checkConflicts();
25171                 }
25172             }
25173         }
25174
25175
25176         function checkConflicts() {
25177             function choice(id, text, action) {
25178                 return { id: id, text: text, action: function() { history.replace(action); } };
25179             }
25180             function formatUser(d) {
25181                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
25182             }
25183             function entityName(entity) {
25184                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
25185             }
25186
25187             function compareVersions(local, remote) {
25188                 if (local.version !== remote.version) return false;
25189
25190                 if (local.type === 'way') {
25191                     var children = _.union(local.nodes, remote.nodes);
25192
25193                     for (var i = 0; i < children.length; i++) {
25194                         var a = localGraph.hasEntity(children[i]),
25195                             b = remoteGraph.hasEntity(children[i]);
25196
25197                         if (a && b && a.version !== b.version) return false;
25198                     }
25199                 }
25200
25201                 return true;
25202             }
25203
25204             _.each(toCheck, function(id) {
25205                 var local = localGraph.entity(id),
25206                     remote = remoteGraph.entity(id);
25207
25208                 if (compareVersions(local, remote)) return;
25209
25210                 var action = iD.actions.MergeRemoteChanges,
25211                     merge = action(id, localGraph, remoteGraph, formatUser);
25212
25213                 history.replace(merge);
25214
25215                 var mergeConflicts = merge.conflicts();
25216                 if (!mergeConflicts.length) return;  // merged safely
25217
25218                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
25219                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
25220                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
25221                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
25222
25223                 conflicts.push({
25224                     id: id,
25225                     name: entityName(local),
25226                     details: mergeConflicts,
25227                     chosen: 1,
25228                     choices: [
25229                         choice(id, keepMine, forceLocal),
25230                         choice(id, keepTheirs, forceRemote)
25231                     ]
25232                 });
25233             });
25234
25235             finalize();
25236         }
25237
25238
25239         function finalize() {
25240             if (conflicts.length) {
25241                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
25242                 showConflicts();
25243             } else if (errors.length) {
25244                 showErrors();
25245             } else {
25246                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
25247                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
25248                     context.connection().putChangeset(
25249                         changes,
25250                         e.comment,
25251                         history.imageryUsed(),
25252                         function(err, changeset_id) {
25253                             if (err) {
25254                                 errors.push({
25255                                     msg: err.responseText,
25256                                     details: [ t('save.status_code', { code: err.status }) ]
25257                                 });
25258                                 showErrors();
25259                             } else {
25260                                 history.clearSaved();
25261                                 success(e, changeset_id);
25262                                 // Add delay to allow for postgres replication #1646 #2678
25263                                 window.setTimeout(function() {
25264                                     loading.close();
25265                                     context.flush();
25266                                 }, 2500);
25267                             }
25268                         });
25269                 } else {        // changes were insignificant or reverted by user
25270                     loading.close();
25271                     context.flush();
25272                     cancel();
25273                 }
25274             }
25275         }
25276
25277
25278         function showConflicts() {
25279             var selection = context.container()
25280                 .select('#sidebar')
25281                 .append('div')
25282                 .attr('class','sidebar-component');
25283
25284             loading.close();
25285
25286             selection.call(iD.ui.Conflicts(context)
25287                 .list(conflicts)
25288                 .on('download', function() {
25289                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
25290                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
25291                     win.focus();
25292                 })
25293                 .on('cancel', function() {
25294                     history.pop();
25295                     selection.remove();
25296                 })
25297                 .on('save', function() {
25298                     for (var i = 0; i < conflicts.length; i++) {
25299                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
25300                             var entity = context.hasEntity(conflicts[i].id);
25301                             if (entity && entity.type === 'way') {
25302                                 var children = _.uniq(entity.nodes);
25303                                 for (var j = 0; j < children.length; j++) {
25304                                     history.replace(iD.actions.Revert(children[j]));
25305                                 }
25306                             }
25307                             history.replace(iD.actions.Revert(conflicts[i].id));
25308                         }
25309                     }
25310
25311                     selection.remove();
25312                     save(e, true);
25313                 })
25314             );
25315         }
25316
25317
25318         function showErrors() {
25319             var selection = iD.ui.confirm(context.container());
25320
25321             history.pop();
25322             loading.close();
25323
25324             selection
25325                 .select('.modal-section.header')
25326                 .append('h3')
25327                 .text(t('save.error'));
25328
25329             addErrors(selection, errors);
25330             selection.okButton();
25331         }
25332
25333
25334         function addErrors(selection, data) {
25335             var message = selection
25336                 .select('.modal-section.message-text');
25337
25338             var items = message
25339                 .selectAll('.error-container')
25340                 .data(data);
25341
25342             var enter = items.enter()
25343                 .append('div')
25344                 .attr('class', 'error-container');
25345
25346             enter
25347                 .append('a')
25348                 .attr('class', 'error-description')
25349                 .attr('href', '#')
25350                 .classed('hide-toggle', true)
25351                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25352                 .on('click', function() {
25353                     var error = d3.select(this),
25354                         detail = d3.select(this.nextElementSibling),
25355                         exp = error.classed('expanded');
25356
25357                     detail.style('display', exp ? 'none' : 'block');
25358                     error.classed('expanded', !exp);
25359
25360                     d3.event.preventDefault();
25361                 });
25362
25363             var details = enter
25364                 .append('div')
25365                 .attr('class', 'error-detail-container')
25366                 .style('display', 'none');
25367
25368             details
25369                 .append('ul')
25370                 .attr('class', 'error-detail-list')
25371                 .selectAll('li')
25372                 .data(function(d) { return d.details || []; })
25373                 .enter()
25374                 .append('li')
25375                 .attr('class', 'error-detail-item')
25376                 .text(function(d) { return d; });
25377
25378             items.exit()
25379                 .remove();
25380         }
25381
25382     }
25383
25384
25385     function success(e, changeset_id) {
25386         context.enter(iD.modes.Browse(context)
25387             .sidebar(iD.ui.Success(context)
25388                 .changeset({
25389                     id: changeset_id,
25390                     comment: e.comment
25391                 })
25392                 .on('cancel', function() {
25393                     context.ui().sidebar.hide();
25394                 })));
25395     }
25396
25397     var mode = {
25398         id: 'save'
25399     };
25400
25401     mode.enter = function() {
25402         context.connection().authenticate(function(err) {
25403             if (err) {
25404                 cancel();
25405             } else {
25406                 context.ui().sidebar.show(ui);
25407             }
25408         });
25409     };
25410
25411     mode.exit = function() {
25412         context.ui().sidebar.hide();
25413     };
25414
25415     return mode;
25416 };
25417 iD.modes.Select = function(context, selectedIDs) {
25418     var mode = {
25419         id: 'select',
25420         button: 'browse'
25421     };
25422
25423     var keybinding = d3.keybinding('select'),
25424         timeout = null,
25425         behaviors = [
25426             iD.behavior.Copy(context),
25427             iD.behavior.Paste(context),
25428             iD.behavior.Breathe(context),
25429             iD.behavior.Hover(context),
25430             iD.behavior.Select(context),
25431             iD.behavior.Lasso(context),
25432             iD.modes.DragNode(context)
25433                 .selectedIDs(selectedIDs)
25434                 .behavior],
25435         inspector,
25436         radialMenu,
25437         newFeature = false,
25438         suppressMenu = false;
25439
25440     var wrap = context.container()
25441         .select('.inspector-wrap');
25442
25443
25444     function singular() {
25445         if (selectedIDs.length === 1) {
25446             return context.hasEntity(selectedIDs[0]);
25447         }
25448     }
25449
25450     function closeMenu() {
25451         if (radialMenu) {
25452             context.surface().call(radialMenu.close);
25453         }
25454     }
25455
25456     function positionMenu() {
25457         if (suppressMenu || !radialMenu) { return; }
25458
25459         var entity = singular();
25460         if (entity && context.geometry(entity.id) === 'relation') {
25461             suppressMenu = true;
25462         } else if (entity && entity.type === 'node') {
25463             radialMenu.center(context.projection(entity.loc));
25464         } else {
25465             var point = context.mouse(),
25466                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25467             if (iD.geo.pointInPolygon(point, viewport)) {
25468                 radialMenu.center(point);
25469             } else {
25470                 suppressMenu = true;
25471             }
25472         }
25473     }
25474
25475     function showMenu() {
25476         closeMenu();
25477         if (!suppressMenu && radialMenu) {
25478             context.surface().call(radialMenu);
25479         }
25480     }
25481
25482     function toggleMenu() {
25483         if (d3.select('.radial-menu').empty()) {
25484             showMenu();
25485         } else {
25486             closeMenu();
25487         }
25488     }
25489
25490     mode.selectedIDs = function() {
25491         return selectedIDs;
25492     };
25493
25494     mode.reselect = function() {
25495         var surfaceNode = context.surface().node();
25496         if (surfaceNode.focus) { // FF doesn't support it
25497             surfaceNode.focus();
25498         }
25499
25500         positionMenu();
25501         showMenu();
25502     };
25503
25504     mode.newFeature = function(_) {
25505         if (!arguments.length) return newFeature;
25506         newFeature = _;
25507         return mode;
25508     };
25509
25510     mode.suppressMenu = function(_) {
25511         if (!arguments.length) return suppressMenu;
25512         suppressMenu = _;
25513         return mode;
25514     };
25515
25516     mode.enter = function() {
25517         function update() {
25518             closeMenu();
25519             if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25520                 // Exit mode if selected entity gets undone
25521                 context.enter(iD.modes.Browse(context));
25522             }
25523         }
25524
25525         function dblclick() {
25526             var target = d3.select(d3.event.target),
25527                 datum = target.datum();
25528
25529             if (datum instanceof iD.Way && !target.classed('fill')) {
25530                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25531                     node = iD.Node();
25532
25533                 var prev = datum.nodes[choice.index - 1],
25534                     next = datum.nodes[choice.index];
25535
25536                 context.perform(
25537                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25538                     t('operations.add.annotation.vertex'));
25539
25540                 d3.event.preventDefault();
25541                 d3.event.stopPropagation();
25542             }
25543         }
25544
25545         function selectElements(drawn) {
25546             var entity = singular();
25547             if (entity && context.geometry(entity.id) === 'relation') {
25548                 suppressMenu = true;
25549                 return;
25550             }
25551
25552             var selection = context.surface()
25553                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25554
25555             if (selection.empty()) {
25556                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
25557                     context.enter(iD.modes.Browse(context));
25558                 }
25559             } else {
25560                 selection
25561                     .classed('selected', true);
25562             }
25563         }
25564
25565         function esc() {
25566             if (!context.inIntro()) {
25567                 context.enter(iD.modes.Browse(context));
25568             }
25569         }
25570
25571
25572         behaviors.forEach(function(behavior) {
25573             context.install(behavior);
25574         });
25575
25576         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25577                 .map(function(o) { return o(selectedIDs, context); })
25578                 .filter(function(o) { return o.available(); });
25579
25580         operations.unshift(iD.operations.Delete(selectedIDs, context));
25581
25582         keybinding
25583             .on('⎋', esc, true)
25584             .on('space', toggleMenu);
25585
25586         operations.forEach(function(operation) {
25587             operation.keys.forEach(function(key) {
25588                 keybinding.on(key, function() {
25589                     if (!(context.inIntro() || operation.disabled())) {
25590                         operation();
25591                     }
25592                 });
25593             });
25594         });
25595
25596         d3.select(document)
25597             .call(keybinding);
25598
25599         radialMenu = iD.ui.RadialMenu(context, operations);
25600
25601         context.ui().sidebar
25602             .select(singular() ? singular().id : null, newFeature);
25603
25604         context.history()
25605             .on('undone.select', update)
25606             .on('redone.select', update);
25607
25608         context.map()
25609             .on('move.select', closeMenu)
25610             .on('drawn.select', selectElements);
25611
25612         selectElements();
25613
25614         var show = d3.event && !suppressMenu;
25615
25616         if (show) {
25617             positionMenu();
25618         }
25619
25620         timeout = window.setTimeout(function() {
25621             if (show) {
25622                 showMenu();
25623             }
25624
25625             context.surface()
25626                 .on('dblclick.select', dblclick);
25627         }, 200);
25628
25629         if (selectedIDs.length > 1) {
25630             var entities = iD.ui.SelectionList(context, selectedIDs);
25631             context.ui().sidebar.show(entities);
25632         }
25633     };
25634
25635     mode.exit = function() {
25636         if (timeout) window.clearTimeout(timeout);
25637
25638         if (inspector) wrap.call(inspector.close);
25639
25640         behaviors.forEach(function(behavior) {
25641             context.uninstall(behavior);
25642         });
25643
25644         keybinding.off();
25645         closeMenu();
25646         radialMenu = undefined;
25647
25648         context.history()
25649             .on('undone.select', null)
25650             .on('redone.select', null);
25651
25652         context.surface()
25653             .on('dblclick.select', null)
25654             .selectAll('.selected')
25655             .classed('selected', false);
25656
25657         context.map().on('drawn.select', null);
25658         context.ui().sidebar.hide();
25659     };
25660
25661     return mode;
25662 };
25663 iD.operations = {};
25664 iD.operations.Circularize = function(selectedIDs, context) {
25665     var entityId = selectedIDs[0],
25666         entity = context.entity(entityId),
25667         extent = entity.extent(context.graph()),
25668         geometry = context.geometry(entityId),
25669         action = iD.actions.Circularize(entityId, context.projection);
25670
25671     var operation = function() {
25672         var annotation = t('operations.circularize.annotation.' + geometry);
25673         context.perform(action, annotation);
25674     };
25675
25676     operation.available = function() {
25677         return selectedIDs.length === 1 &&
25678             entity.type === 'way' &&
25679             _.uniq(entity.nodes).length > 1;
25680     };
25681
25682     operation.disabled = function() {
25683         var reason;
25684         if (extent.percentContainedIn(context.extent()) < 0.8) {
25685             reason = 'too_large';
25686         } else if (context.hasHiddenConnections(entityId)) {
25687             reason = 'connected_to_hidden';
25688         }
25689         return action.disabled(context.graph()) || reason;
25690     };
25691
25692     operation.tooltip = function() {
25693         var disable = operation.disabled();
25694         return disable ?
25695             t('operations.circularize.' + disable) :
25696             t('operations.circularize.description.' + geometry);
25697     };
25698
25699     operation.id = 'circularize';
25700     operation.keys = [t('operations.circularize.key')];
25701     operation.title = t('operations.circularize.title');
25702
25703     return operation;
25704 };
25705 iD.operations.Continue = function(selectedIDs, context) {
25706     var graph = context.graph(),
25707         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25708         geometries = _.extend({line: [], vertex: []},
25709             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25710         vertex = geometries.vertex[0];
25711
25712     function candidateWays() {
25713         return graph.parentWays(vertex).filter(function(parent) {
25714             return parent.geometry(graph) === 'line' &&
25715                 parent.affix(vertex.id) &&
25716                 (geometries.line.length === 0 || geometries.line[0] === parent);
25717         });
25718     }
25719
25720     var operation = function() {
25721         var candidate = candidateWays()[0];
25722         context.enter(iD.modes.DrawLine(
25723             context,
25724             candidate.id,
25725             context.graph(),
25726             candidate.affix(vertex.id)));
25727     };
25728
25729     operation.available = function() {
25730         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25731             !context.features().hasHiddenConnections(vertex, context.graph());
25732     };
25733
25734     operation.disabled = function() {
25735         var candidates = candidateWays();
25736         if (candidates.length === 0)
25737             return 'not_eligible';
25738         if (candidates.length > 1)
25739             return 'multiple';
25740     };
25741
25742     operation.tooltip = function() {
25743         var disable = operation.disabled();
25744         return disable ?
25745             t('operations.continue.' + disable) :
25746             t('operations.continue.description');
25747     };
25748
25749     operation.id = 'continue';
25750     operation.keys = [t('operations.continue.key')];
25751     operation.title = t('operations.continue.title');
25752
25753     return operation;
25754 };
25755 iD.operations.Delete = function(selectedIDs, context) {
25756     var action = iD.actions.DeleteMultiple(selectedIDs);
25757
25758     var operation = function() {
25759         var annotation,
25760             nextSelectedID;
25761
25762         if (selectedIDs.length > 1) {
25763             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25764
25765         } else {
25766             var id = selectedIDs[0],
25767                 entity = context.entity(id),
25768                 geometry = context.geometry(id),
25769                 parents = context.graph().parentWays(entity),
25770                 parent = parents[0];
25771
25772             annotation = t('operations.delete.annotation.' + geometry);
25773
25774             // Select the next closest node in the way.
25775             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25776                 var nodes = parent.nodes,
25777                     i = nodes.indexOf(id);
25778
25779                 if (i === 0) {
25780                     i++;
25781                 } else if (i === nodes.length - 1) {
25782                     i--;
25783                 } else {
25784                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25785                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25786                     i = a < b ? i - 1 : i + 1;
25787                 }
25788
25789                 nextSelectedID = nodes[i];
25790             }
25791         }
25792
25793         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25794             context.enter(iD.modes.Select(context, [nextSelectedID]));
25795         } else {
25796             context.enter(iD.modes.Browse(context));
25797         }
25798
25799         context.perform(
25800             action,
25801             annotation);
25802     };
25803
25804     operation.available = function() {
25805         return true;
25806     };
25807
25808     operation.disabled = function() {
25809         var reason;
25810         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25811             reason = 'connected_to_hidden';
25812         }
25813         return action.disabled(context.graph()) || reason;
25814     };
25815
25816     operation.tooltip = function() {
25817         var disable = operation.disabled();
25818         return disable ?
25819             t('operations.delete.' + disable) :
25820             t('operations.delete.description');
25821     };
25822
25823     operation.id = 'delete';
25824     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25825     operation.title = t('operations.delete.title');
25826
25827     return operation;
25828 };
25829 iD.operations.Disconnect = function(selectedIDs, context) {
25830     var vertices = _.filter(selectedIDs, function vertex(entityId) {
25831         return context.geometry(entityId) === 'vertex';
25832     });
25833
25834     var entityId = vertices[0],
25835         action = iD.actions.Disconnect(entityId);
25836
25837     if (selectedIDs.length > 1) {
25838         action.limitWays(_.without(selectedIDs, entityId));
25839     }
25840
25841     var operation = function() {
25842         context.perform(action, t('operations.disconnect.annotation'));
25843     };
25844
25845     operation.available = function() {
25846         return vertices.length === 1;
25847     };
25848
25849     operation.disabled = function() {
25850         var reason;
25851         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25852             reason = 'connected_to_hidden';
25853         }
25854         return action.disabled(context.graph()) || reason;
25855     };
25856
25857     operation.tooltip = function() {
25858         var disable = operation.disabled();
25859         return disable ?
25860             t('operations.disconnect.' + disable) :
25861             t('operations.disconnect.description');
25862     };
25863
25864     operation.id = 'disconnect';
25865     operation.keys = [t('operations.disconnect.key')];
25866     operation.title = t('operations.disconnect.title');
25867
25868     return operation;
25869 };
25870 iD.operations.Merge = function(selectedIDs, context) {
25871     var join = iD.actions.Join(selectedIDs),
25872         merge = iD.actions.Merge(selectedIDs),
25873         mergePolygon = iD.actions.MergePolygon(selectedIDs);
25874
25875     var operation = function() {
25876         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25877             action;
25878
25879         if (!join.disabled(context.graph())) {
25880             action = join;
25881         } else if (!merge.disabled(context.graph())) {
25882             action = merge;
25883         } else {
25884             action = mergePolygon;
25885         }
25886
25887         context.perform(action, annotation);
25888         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25889             .suppressMenu(true));
25890     };
25891
25892     operation.available = function() {
25893         return selectedIDs.length >= 2;
25894     };
25895
25896     operation.disabled = function() {
25897         return join.disabled(context.graph()) &&
25898             merge.disabled(context.graph()) &&
25899             mergePolygon.disabled(context.graph());
25900     };
25901
25902     operation.tooltip = function() {
25903         var j = join.disabled(context.graph()),
25904             m = merge.disabled(context.graph()),
25905             p = mergePolygon.disabled(context.graph());
25906
25907         if (j === 'restriction' && m && p)
25908             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
25909
25910         if (p === 'incomplete_relation' && j && m)
25911             return t('operations.merge.incomplete_relation');
25912
25913         if (j && m && p)
25914             return t('operations.merge.' + j);
25915
25916         return t('operations.merge.description');
25917     };
25918
25919     operation.id = 'merge';
25920     operation.keys = [t('operations.merge.key')];
25921     operation.title = t('operations.merge.title');
25922
25923     return operation;
25924 };
25925 iD.operations.Move = function(selectedIDs, context) {
25926     var extent = selectedIDs.reduce(function(extent, id) {
25927             return extent.extend(context.entity(id).extent(context.graph()));
25928         }, iD.geo.Extent());
25929
25930     var operation = function() {
25931         context.enter(iD.modes.Move(context, selectedIDs));
25932     };
25933
25934     operation.available = function() {
25935         return selectedIDs.length > 1 ||
25936             context.entity(selectedIDs[0]).type !== 'node';
25937     };
25938
25939     operation.disabled = function() {
25940         var reason;
25941         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
25942             reason = 'too_large';
25943         } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
25944             reason = 'connected_to_hidden';
25945         }
25946         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
25947     };
25948
25949     operation.tooltip = function() {
25950         var disable = operation.disabled();
25951         return disable ?
25952             t('operations.move.' + disable) :
25953             t('operations.move.description');
25954     };
25955
25956     operation.id = 'move';
25957     operation.keys = [t('operations.move.key')];
25958     operation.title = t('operations.move.title');
25959
25960     return operation;
25961 };
25962 iD.operations.Orthogonalize = function(selectedIDs, context) {
25963     var entityId = selectedIDs[0],
25964         entity = context.entity(entityId),
25965         extent = entity.extent(context.graph()),
25966         geometry = context.geometry(entityId),
25967         action = iD.actions.Orthogonalize(entityId, context.projection);
25968
25969     var operation = function() {
25970         var annotation = t('operations.orthogonalize.annotation.' + geometry);
25971         context.perform(action, annotation);
25972     };
25973
25974     operation.available = function() {
25975         return selectedIDs.length === 1 &&
25976             entity.type === 'way' &&
25977             entity.isClosed() &&
25978             _.uniq(entity.nodes).length > 2;
25979     };
25980
25981     operation.disabled = function() {
25982         var reason;
25983         if (extent.percentContainedIn(context.extent()) < 0.8) {
25984             reason = 'too_large';
25985         } else if (context.hasHiddenConnections(entityId)) {
25986             reason = 'connected_to_hidden';
25987         }
25988         return action.disabled(context.graph()) || reason;
25989     };
25990
25991     operation.tooltip = function() {
25992         var disable = operation.disabled();
25993         return disable ?
25994             t('operations.orthogonalize.' + disable) :
25995             t('operations.orthogonalize.description.' + geometry);
25996     };
25997
25998     operation.id = 'orthogonalize';
25999     operation.keys = [t('operations.orthogonalize.key')];
26000     operation.title = t('operations.orthogonalize.title');
26001
26002     return operation;
26003 };
26004 iD.operations.Reverse = function(selectedIDs, context) {
26005     var entityId = selectedIDs[0];
26006
26007     var operation = function() {
26008         context.perform(
26009             iD.actions.Reverse(entityId),
26010             t('operations.reverse.annotation'));
26011     };
26012
26013     operation.available = function() {
26014         return selectedIDs.length === 1 &&
26015             context.geometry(entityId) === 'line';
26016     };
26017
26018     operation.disabled = function() {
26019         return false;
26020     };
26021
26022     operation.tooltip = function() {
26023         return t('operations.reverse.description');
26024     };
26025
26026     operation.id = 'reverse';
26027     operation.keys = [t('operations.reverse.key')];
26028     operation.title = t('operations.reverse.title');
26029
26030     return operation;
26031 };
26032 iD.operations.Rotate = function(selectedIDs, context) {
26033     var entityId = selectedIDs[0],
26034         entity = context.entity(entityId),
26035         extent = entity.extent(context.graph()),
26036         geometry = context.geometry(entityId);
26037
26038     var operation = function() {
26039         context.enter(iD.modes.RotateWay(context, entityId));
26040     };
26041
26042     operation.available = function() {
26043         if (selectedIDs.length !== 1 || entity.type !== 'way')
26044             return false;
26045         if (geometry === 'area')
26046             return true;
26047         if (entity.isClosed() &&
26048             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
26049             return true;
26050         return false;
26051     };
26052
26053     operation.disabled = function() {
26054         if (extent.percentContainedIn(context.extent()) < 0.8) {
26055             return 'too_large';
26056         } else if (context.hasHiddenConnections(entityId)) {
26057             return 'connected_to_hidden';
26058         } else {
26059             return false;
26060         }
26061     };
26062
26063     operation.tooltip = function() {
26064         var disable = operation.disabled();
26065         return disable ?
26066             t('operations.rotate.' + disable) :
26067             t('operations.rotate.description');
26068     };
26069
26070     operation.id = 'rotate';
26071     operation.keys = [t('operations.rotate.key')];
26072     operation.title = t('operations.rotate.title');
26073
26074     return operation;
26075 };
26076 iD.operations.Split = function(selectedIDs, context) {
26077     var vertices = _.filter(selectedIDs, function vertex(entityId) {
26078         return context.geometry(entityId) === 'vertex';
26079     });
26080
26081     var entityId = vertices[0],
26082         action = iD.actions.Split(entityId);
26083
26084     if (selectedIDs.length > 1) {
26085         action.limitWays(_.without(selectedIDs, entityId));
26086     }
26087
26088     var operation = function() {
26089         var annotation;
26090
26091         var ways = action.ways(context.graph());
26092         if (ways.length === 1) {
26093             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
26094         } else {
26095             annotation = t('operations.split.annotation.multiple', {n: ways.length});
26096         }
26097
26098         var difference = context.perform(action, annotation);
26099         context.enter(iD.modes.Select(context, difference.extantIDs()));
26100     };
26101
26102     operation.available = function() {
26103         return vertices.length === 1;
26104     };
26105
26106     operation.disabled = function() {
26107         var reason;
26108         if (_.any(selectedIDs, context.hasHiddenConnections)) {
26109             reason = 'connected_to_hidden';
26110         }
26111         return action.disabled(context.graph()) || reason;
26112     };
26113
26114     operation.tooltip = function() {
26115         var disable = operation.disabled();
26116         if (disable) {
26117             return t('operations.split.' + disable);
26118         }
26119
26120         var ways = action.ways(context.graph());
26121         if (ways.length === 1) {
26122             return t('operations.split.description.' + context.geometry(ways[0].id));
26123         } else {
26124             return t('operations.split.description.multiple');
26125         }
26126     };
26127
26128     operation.id = 'split';
26129     operation.keys = [t('operations.split.key')];
26130     operation.title = t('operations.split.title');
26131
26132     return operation;
26133 };
26134 iD.operations.Straighten = function(selectedIDs, context) {
26135     var entityId = selectedIDs[0],
26136         action = iD.actions.Straighten(entityId, context.projection);
26137
26138     function operation() {
26139         var annotation = t('operations.straighten.annotation');
26140         context.perform(action, annotation);
26141     }
26142
26143     operation.available = function() {
26144         var entity = context.entity(entityId);
26145         return selectedIDs.length === 1 &&
26146             entity.type === 'way' &&
26147             !entity.isClosed() &&
26148             _.uniq(entity.nodes).length > 2;
26149     };
26150
26151     operation.disabled = function() {
26152         var reason;
26153         if (context.hasHiddenConnections(entityId)) {
26154             reason = 'connected_to_hidden';
26155         }
26156         return action.disabled(context.graph()) || reason;
26157     };
26158
26159     operation.tooltip = function() {
26160         var disable = operation.disabled();
26161         return disable ?
26162             t('operations.straighten.' + disable) :
26163             t('operations.straighten.description');
26164     };
26165
26166     operation.id = 'straighten';
26167     operation.keys = [t('operations.straighten.key')];
26168     operation.title = t('operations.straighten.title');
26169
26170     return operation;
26171 };
26172 iD.Connection = function(useHttps) {
26173     if (typeof useHttps !== 'boolean') {
26174       useHttps = window.location.protocol === 'https:';
26175     }
26176
26177     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
26178         protocol = useHttps ? 'https:' : 'http:',
26179         url = protocol + '//www.openstreetmap.org',
26180         connection = {},
26181         inflight = {},
26182         loadedTiles = {},
26183         tileZoom = 16,
26184         oauth = osmAuth({
26185             url: protocol + '//www.openstreetmap.org',
26186             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
26187             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
26188             loading: authenticating,
26189             done: authenticated
26190         }),
26191         ndStr = 'nd',
26192         tagStr = 'tag',
26193         memberStr = 'member',
26194         nodeStr = 'node',
26195         wayStr = 'way',
26196         relationStr = 'relation',
26197         userDetails,
26198         off;
26199
26200
26201     connection.changesetURL = function(changesetId) {
26202         return url + '/changeset/' + changesetId;
26203     };
26204
26205     connection.changesetsURL = function(center, zoom) {
26206         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
26207         return url + '/history#map=' +
26208             Math.floor(zoom) + '/' +
26209             center[1].toFixed(precision) + '/' +
26210             center[0].toFixed(precision);
26211     };
26212
26213     connection.entityURL = function(entity) {
26214         return url + '/' + entity.type + '/' + entity.osmId();
26215     };
26216
26217     connection.userURL = function(username) {
26218         return url + '/user/' + username;
26219     };
26220
26221     connection.loadFromURL = function(url, callback) {
26222         function done(err, dom) {
26223             return callback(err, parse(dom));
26224         }
26225         return d3.xml(url).get(done);
26226     };
26227
26228     connection.loadEntity = function(id, callback) {
26229         var type = iD.Entity.id.type(id),
26230             osmID = iD.Entity.id.toOSM(id);
26231
26232         connection.loadFromURL(
26233             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
26234             function(err, entities) {
26235                 if (callback) callback(err, {data: entities});
26236             });
26237     };
26238
26239     connection.loadEntityVersion = function(id, version, callback) {
26240         var type = iD.Entity.id.type(id),
26241             osmID = iD.Entity.id.toOSM(id);
26242
26243         connection.loadFromURL(
26244             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
26245             function(err, entities) {
26246                 if (callback) callback(err, {data: entities});
26247             });
26248     };
26249
26250     connection.loadMultiple = function(ids, callback) {
26251         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
26252             var type = k + 's',
26253                 osmIDs = _.map(v, iD.Entity.id.toOSM);
26254
26255             _.each(_.chunk(osmIDs, 150), function(arr) {
26256                 connection.loadFromURL(
26257                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
26258                     function(err, entities) {
26259                         if (callback) callback(err, {data: entities});
26260                     });
26261             });
26262         });
26263     };
26264
26265     function authenticating() {
26266         event.authenticating();
26267     }
26268
26269     function authenticated() {
26270         event.authenticated();
26271     }
26272
26273     function getLoc(attrs) {
26274         var lon = attrs.lon && attrs.lon.value,
26275             lat = attrs.lat && attrs.lat.value;
26276         return [parseFloat(lon), parseFloat(lat)];
26277     }
26278
26279     function getNodes(obj) {
26280         var elems = obj.getElementsByTagName(ndStr),
26281             nodes = new Array(elems.length);
26282         for (var i = 0, l = elems.length; i < l; i++) {
26283             nodes[i] = 'n' + elems[i].attributes.ref.value;
26284         }
26285         return nodes;
26286     }
26287
26288     function getTags(obj) {
26289         var elems = obj.getElementsByTagName(tagStr),
26290             tags = {};
26291         for (var i = 0, l = elems.length; i < l; i++) {
26292             var attrs = elems[i].attributes;
26293             tags[attrs.k.value] = attrs.v.value;
26294         }
26295         return tags;
26296     }
26297
26298     function getMembers(obj) {
26299         var elems = obj.getElementsByTagName(memberStr),
26300             members = new Array(elems.length);
26301         for (var i = 0, l = elems.length; i < l; i++) {
26302             var attrs = elems[i].attributes;
26303             members[i] = {
26304                 id: attrs.type.value[0] + attrs.ref.value,
26305                 type: attrs.type.value,
26306                 role: attrs.role.value
26307             };
26308         }
26309         return members;
26310     }
26311
26312     function getVisible(attrs) {
26313         return (!attrs.visible || attrs.visible.value !== 'false');
26314     }
26315
26316     var parsers = {
26317         node: function nodeData(obj) {
26318             var attrs = obj.attributes;
26319             return new iD.Node({
26320                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26321                 loc: getLoc(attrs),
26322                 version: attrs.version.value,
26323                 user: attrs.user && attrs.user.value,
26324                 tags: getTags(obj),
26325                 visible: getVisible(attrs)
26326             });
26327         },
26328
26329         way: function wayData(obj) {
26330             var attrs = obj.attributes;
26331             return new iD.Way({
26332                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26333                 version: attrs.version.value,
26334                 user: attrs.user && attrs.user.value,
26335                 tags: getTags(obj),
26336                 nodes: getNodes(obj),
26337                 visible: getVisible(attrs)
26338             });
26339         },
26340
26341         relation: function relationData(obj) {
26342             var attrs = obj.attributes;
26343             return new iD.Relation({
26344                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26345                 version: attrs.version.value,
26346                 user: attrs.user && attrs.user.value,
26347                 tags: getTags(obj),
26348                 members: getMembers(obj),
26349                 visible: getVisible(attrs)
26350             });
26351         }
26352     };
26353
26354     function parse(dom) {
26355         if (!dom || !dom.childNodes) return;
26356
26357         var root = dom.childNodes[0],
26358             children = root.childNodes,
26359             entities = [];
26360
26361         for (var i = 0, l = children.length; i < l; i++) {
26362             var child = children[i],
26363                 parser = parsers[child.nodeName];
26364             if (parser) {
26365                 entities.push(parser(child));
26366             }
26367         }
26368
26369         return entities;
26370     }
26371
26372     connection.authenticated = function() {
26373         return oauth.authenticated();
26374     };
26375
26376     // Generate Changeset XML. Returns a string.
26377     connection.changesetJXON = function(tags) {
26378         return {
26379             osm: {
26380                 changeset: {
26381                     tag: _.map(tags, function(value, key) {
26382                         return { '@k': key, '@v': value };
26383                     }),
26384                     '@version': 0.6,
26385                     '@generator': 'iD'
26386                 }
26387             }
26388         };
26389     };
26390
26391     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26392     // XML. Returns a string.
26393     connection.osmChangeJXON = function(changeset_id, changes) {
26394         function nest(x, order) {
26395             var groups = {};
26396             for (var i = 0; i < x.length; i++) {
26397                 var tagName = Object.keys(x[i])[0];
26398                 if (!groups[tagName]) groups[tagName] = [];
26399                 groups[tagName].push(x[i][tagName]);
26400             }
26401             var ordered = {};
26402             order.forEach(function(o) {
26403                 if (groups[o]) ordered[o] = groups[o];
26404             });
26405             return ordered;
26406         }
26407
26408         function rep(entity) {
26409             return entity.asJXON(changeset_id);
26410         }
26411
26412         return {
26413             osmChange: {
26414                 '@version': 0.6,
26415                 '@generator': 'iD',
26416                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26417                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26418                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26419             }
26420         };
26421     };
26422
26423     connection.changesetTags = function(comment, imageryUsed) {
26424         var detected = iD.detect(),
26425             tags = {
26426                 created_by: 'iD ' + iD.version,
26427                 imagery_used: imageryUsed.join(';').substr(0, 255),
26428                 host: (window.location.origin + window.location.pathname).substr(0, 255),
26429                 locale: detected.locale
26430             };
26431
26432         if (comment) {
26433             tags.comment = comment.substr(0, 255);
26434         }
26435
26436         return tags;
26437     };
26438
26439     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26440         oauth.xhr({
26441                 method: 'PUT',
26442                 path: '/api/0.6/changeset/create',
26443                 options: { header: { 'Content-Type': 'text/xml' } },
26444                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26445             }, function(err, changeset_id) {
26446                 if (err) return callback(err);
26447                 oauth.xhr({
26448                     method: 'POST',
26449                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
26450                     options: { header: { 'Content-Type': 'text/xml' } },
26451                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26452                 }, function(err) {
26453                     if (err) return callback(err);
26454                     // POST was successful, safe to call the callback.
26455                     // Still attempt to close changeset, but ignore response because #2667
26456                     // Add delay to allow for postgres replication #1646 #2678
26457                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26458                     oauth.xhr({
26459                         method: 'PUT',
26460                         path: '/api/0.6/changeset/' + changeset_id + '/close',
26461                         options: { header: { 'Content-Type': 'text/xml' } }
26462                     }, d3.functor(true));
26463                 });
26464             });
26465     };
26466
26467     connection.userDetails = function(callback) {
26468         if (userDetails) {
26469             callback(undefined, userDetails);
26470             return;
26471         }
26472
26473         function done(err, user_details) {
26474             if (err) return callback(err);
26475
26476             var u = user_details.getElementsByTagName('user')[0],
26477                 img = u.getElementsByTagName('img'),
26478                 image_url = '';
26479
26480             if (img && img[0] && img[0].getAttribute('href')) {
26481                 image_url = img[0].getAttribute('href');
26482             }
26483
26484             userDetails = {
26485                 display_name: u.attributes.display_name.value,
26486                 image_url: image_url,
26487                 id: u.attributes.id.value
26488             };
26489
26490             callback(undefined, userDetails);
26491         }
26492
26493         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26494     };
26495
26496     connection.userChangesets = function(callback) {
26497         connection.userDetails(function(err, user) {
26498             if (err) return callback(err);
26499
26500             function done(changesets) {
26501                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
26502                     function (changeset) {
26503                         return { tags: getTags(changeset) };
26504                     }));
26505             }
26506
26507             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
26508                 .on('load', done)
26509                 .on('error', callback);
26510         });
26511     };
26512
26513     connection.status = function(callback) {
26514         function done(capabilities) {
26515             var apiStatus = capabilities.getElementsByTagName('status');
26516             callback(undefined, apiStatus[0].getAttribute('api'));
26517         }
26518         d3.xml(url + '/api/capabilities').get()
26519             .on('load', done)
26520             .on('error', callback);
26521     };
26522
26523     function abortRequest(i) { i.abort(); }
26524
26525     connection.tileZoom = function(_) {
26526         if (!arguments.length) return tileZoom;
26527         tileZoom = _;
26528         return connection;
26529     };
26530
26531     connection.loadTiles = function(projection, dimensions, callback) {
26532
26533         if (off) return;
26534
26535         var s = projection.scale() * 2 * Math.PI,
26536             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26537             ts = 256 * Math.pow(2, z - tileZoom),
26538             origin = [
26539                 s / 2 - projection.translate()[0],
26540                 s / 2 - projection.translate()[1]];
26541
26542         var tiles = d3.geo.tile()
26543             .scaleExtent([tileZoom, tileZoom])
26544             .scale(s)
26545             .size(dimensions)
26546             .translate(projection.translate())()
26547             .map(function(tile) {
26548                 var x = tile[0] * ts - origin[0],
26549                     y = tile[1] * ts - origin[1];
26550
26551                 return {
26552                     id: tile.toString(),
26553                     extent: iD.geo.Extent(
26554                         projection.invert([x, y + ts]),
26555                         projection.invert([x + ts, y]))
26556                 };
26557             });
26558
26559         function bboxUrl(tile) {
26560             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26561         }
26562
26563         _.filter(inflight, function(v, i) {
26564             var wanted = _.find(tiles, function(tile) {
26565                 return i === tile.id;
26566             });
26567             if (!wanted) delete inflight[i];
26568             return !wanted;
26569         }).map(abortRequest);
26570
26571         tiles.forEach(function(tile) {
26572             var id = tile.id;
26573
26574             if (loadedTiles[id] || inflight[id]) return;
26575
26576             if (_.isEmpty(inflight)) {
26577                 event.loading();
26578             }
26579
26580             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26581                 loadedTiles[id] = true;
26582                 delete inflight[id];
26583
26584                 if (callback) callback(err, _.extend({data: parsed}, tile));
26585
26586                 if (_.isEmpty(inflight)) {
26587                     event.loaded();
26588                 }
26589             });
26590         });
26591     };
26592
26593     connection.switch = function(options) {
26594         url = options.url;
26595         oauth.options(_.extend({
26596             loading: authenticating,
26597             done: authenticated
26598         }, options));
26599         event.auth();
26600         connection.flush();
26601         return connection;
26602     };
26603
26604     connection.toggle = function(_) {
26605         off = !_;
26606         return connection;
26607     };
26608
26609     connection.flush = function() {
26610         userDetails = undefined;
26611         _.forEach(inflight, abortRequest);
26612         loadedTiles = {};
26613         inflight = {};
26614         return connection;
26615     };
26616
26617     connection.loadedTiles = function(_) {
26618         if (!arguments.length) return loadedTiles;
26619         loadedTiles = _;
26620         return connection;
26621     };
26622
26623     connection.logout = function() {
26624         userDetails = undefined;
26625         oauth.logout();
26626         event.auth();
26627         return connection;
26628     };
26629
26630     connection.authenticate = function(callback) {
26631         userDetails = undefined;
26632         function done(err, res) {
26633             event.auth();
26634             if (callback) callback(err, res);
26635         }
26636         return oauth.authenticate(done);
26637     };
26638
26639     return d3.rebind(connection, event, 'on');
26640 };
26641 /*
26642     iD.Difference represents the difference between two graphs.
26643     It knows how to calculate the set of entities that were
26644     created, modified, or deleted, and also contains the logic
26645     for recursively extending a difference to the complete set
26646     of entities that will require a redraw, taking into account
26647     child and parent relationships.
26648  */
26649 iD.Difference = function(base, head) {
26650     var changes = {}, length = 0;
26651
26652     function changed(h, b) {
26653         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26654     }
26655
26656     _.each(head.entities, function(h, id) {
26657         var b = base.entities[id];
26658         if (changed(h, b)) {
26659             changes[id] = {base: b, head: h};
26660             length++;
26661         }
26662     });
26663
26664     _.each(base.entities, function(b, id) {
26665         var h = head.entities[id];
26666         if (!changes[id] && changed(h, b)) {
26667             changes[id] = {base: b, head: h};
26668             length++;
26669         }
26670     });
26671
26672     function addParents(parents, result) {
26673         for (var i = 0; i < parents.length; i++) {
26674             var parent = parents[i];
26675
26676             if (parent.id in result)
26677                 continue;
26678
26679             result[parent.id] = parent;
26680             addParents(head.parentRelations(parent), result);
26681         }
26682     }
26683
26684     var difference = {};
26685
26686     difference.length = function() {
26687         return length;
26688     };
26689
26690     difference.changes = function() {
26691         return changes;
26692     };
26693
26694     difference.extantIDs = function() {
26695         var result = [];
26696         _.each(changes, function(change, id) {
26697             if (change.head) result.push(id);
26698         });
26699         return result;
26700     };
26701
26702     difference.modified = function() {
26703         var result = [];
26704         _.each(changes, function(change) {
26705             if (change.base && change.head) result.push(change.head);
26706         });
26707         return result;
26708     };
26709
26710     difference.created = function() {
26711         var result = [];
26712         _.each(changes, function(change) {
26713             if (!change.base && change.head) result.push(change.head);
26714         });
26715         return result;
26716     };
26717
26718     difference.deleted = function() {
26719         var result = [];
26720         _.each(changes, function(change) {
26721             if (change.base && !change.head) result.push(change.base);
26722         });
26723         return result;
26724     };
26725
26726     difference.summary = function() {
26727         var relevant = {};
26728
26729         function addEntity(entity, graph, changeType) {
26730             relevant[entity.id] = {
26731                 entity: entity,
26732                 graph: graph,
26733                 changeType: changeType
26734             };
26735         }
26736
26737         function addParents(entity) {
26738             var parents = head.parentWays(entity);
26739             for (var j = parents.length - 1; j >= 0; j--) {
26740                 var parent = parents[j];
26741                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26742             }
26743         }
26744
26745         _.each(changes, function(change) {
26746             if (change.head && change.head.geometry(head) !== 'vertex') {
26747                 addEntity(change.head, head, change.base ? 'modified' : 'created');
26748
26749             } else if (change.base && change.base.geometry(base) !== 'vertex') {
26750                 addEntity(change.base, base, 'deleted');
26751
26752             } else if (change.base && change.head) { // modified vertex
26753                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
26754                     retagged = !_.isEqual(change.base.tags, change.head.tags);
26755
26756                 if (moved) {
26757                     addParents(change.head);
26758                 }
26759
26760                 if (retagged || (moved && change.head.hasInterestingTags())) {
26761                     addEntity(change.head, head, 'modified');
26762                 }
26763
26764             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26765                 addEntity(change.head, head, 'created');
26766
26767             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26768                 addEntity(change.base, base, 'deleted');
26769             }
26770         });
26771
26772         return d3.values(relevant);
26773     };
26774
26775     difference.complete = function(extent) {
26776         var result = {}, id, change;
26777
26778         for (id in changes) {
26779             change = changes[id];
26780
26781             var h = change.head,
26782                 b = change.base,
26783                 entity = h || b;
26784
26785             if (extent &&
26786                 (!h || !h.intersects(extent, head)) &&
26787                 (!b || !b.intersects(extent, base)))
26788                 continue;
26789
26790             result[id] = h;
26791
26792             if (entity.type === 'way') {
26793                 var nh = h ? h.nodes : [],
26794                     nb = b ? b.nodes : [],
26795                     diff, i;
26796
26797                 diff = _.difference(nh, nb);
26798                 for (i = 0; i < diff.length; i++) {
26799                     result[diff[i]] = head.hasEntity(diff[i]);
26800                 }
26801
26802                 diff = _.difference(nb, nh);
26803                 for (i = 0; i < diff.length; i++) {
26804                     result[diff[i]] = head.hasEntity(diff[i]);
26805                 }
26806             }
26807
26808             addParents(head.parentWays(entity), result);
26809             addParents(head.parentRelations(entity), result);
26810         }
26811
26812         return result;
26813     };
26814
26815     return difference;
26816 };
26817 iD.Entity = function(attrs) {
26818     // For prototypal inheritance.
26819     if (this instanceof iD.Entity) return;
26820
26821     // Create the appropriate subtype.
26822     if (attrs && attrs.type) {
26823         return iD.Entity[attrs.type].apply(this, arguments);
26824     } else if (attrs && attrs.id) {
26825         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26826     }
26827
26828     // Initialize a generic Entity (used only in tests).
26829     return (new iD.Entity()).initialize(arguments);
26830 };
26831
26832 iD.Entity.id = function(type) {
26833     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26834 };
26835
26836 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26837
26838 iD.Entity.id.fromOSM = function(type, id) {
26839     return type[0] + id;
26840 };
26841
26842 iD.Entity.id.toOSM = function(id) {
26843     return id.slice(1);
26844 };
26845
26846 iD.Entity.id.type = function(id) {
26847     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26848 };
26849
26850 // A function suitable for use as the second argument to d3.selection#data().
26851 iD.Entity.key = function(entity) {
26852     return entity.id + 'v' + (entity.v || 0);
26853 };
26854
26855 iD.Entity.prototype = {
26856     tags: {},
26857
26858     initialize: function(sources) {
26859         for (var i = 0; i < sources.length; ++i) {
26860             var source = sources[i];
26861             for (var prop in source) {
26862                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26863                     if (source[prop] === undefined) {
26864                         delete this[prop];
26865                     } else {
26866                         this[prop] = source[prop];
26867                     }
26868                 }
26869             }
26870         }
26871
26872         if (!this.id && this.type) {
26873             this.id = iD.Entity.id(this.type);
26874         }
26875         if (!this.hasOwnProperty('visible')) {
26876             this.visible = true;
26877         }
26878
26879         if (iD.debug) {
26880             Object.freeze(this);
26881             Object.freeze(this.tags);
26882
26883             if (this.loc) Object.freeze(this.loc);
26884             if (this.nodes) Object.freeze(this.nodes);
26885             if (this.members) Object.freeze(this.members);
26886         }
26887
26888         return this;
26889     },
26890
26891     copy: function(resolver, copies) {
26892         if (copies[this.id])
26893             return copies[this.id];
26894
26895         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
26896         copies[this.id] = copy;
26897
26898         return copy;
26899     },
26900
26901     osmId: function() {
26902         return iD.Entity.id.toOSM(this.id);
26903     },
26904
26905     isNew: function() {
26906         return this.osmId() < 0;
26907     },
26908
26909     update: function(attrs) {
26910         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
26911     },
26912
26913     mergeTags: function(tags) {
26914         var merged = _.clone(this.tags), changed = false;
26915         for (var k in tags) {
26916             var t1 = merged[k],
26917                 t2 = tags[k];
26918             if (!t1) {
26919                 changed = true;
26920                 merged[k] = t2;
26921             } else if (t1 !== t2) {
26922                 changed = true;
26923                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
26924             }
26925         }
26926         return changed ? this.update({tags: merged}) : this;
26927     },
26928
26929     intersects: function(extent, resolver) {
26930         return this.extent(resolver).intersects(extent);
26931     },
26932
26933     isUsed: function(resolver) {
26934         return _.without(Object.keys(this.tags), 'area').length > 0 ||
26935             resolver.parentRelations(this).length > 0;
26936     },
26937
26938     hasInterestingTags: function() {
26939         return _.keys(this.tags).some(iD.interestingTag);
26940     },
26941
26942     isHighwayIntersection: function() {
26943         return false;
26944     },
26945
26946     deprecatedTags: function() {
26947         var tags = _.pairs(this.tags);
26948         var deprecated = {};
26949
26950         iD.data.deprecated.forEach(function(d) {
26951             var match = _.pairs(d.old)[0];
26952             tags.forEach(function(t) {
26953                 if (t[0] === match[0] &&
26954                     (t[1] === match[1] || match[1] === '*')) {
26955                     deprecated[t[0]] = t[1];
26956                 }
26957             });
26958         });
26959
26960         return deprecated;
26961     }
26962 };
26963 iD.Graph = function(other, mutable) {
26964     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
26965
26966     if (other instanceof iD.Graph) {
26967         var base = other.base();
26968         this.entities = _.assign(Object.create(base.entities), other.entities);
26969         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
26970         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
26971
26972     } else {
26973         this.entities = Object.create({});
26974         this._parentWays = Object.create({});
26975         this._parentRels = Object.create({});
26976         this.rebase(other || [], [this]);
26977     }
26978
26979     this.transients = {};
26980     this._childNodes = {};
26981     this.frozen = !mutable;
26982 };
26983
26984 iD.Graph.prototype = {
26985     hasEntity: function(id) {
26986         return this.entities[id];
26987     },
26988
26989     entity: function(id) {
26990         var entity = this.entities[id];
26991         if (!entity) {
26992             throw new Error('entity ' + id + ' not found');
26993         }
26994         return entity;
26995     },
26996
26997     transient: function(entity, key, fn) {
26998         var id = entity.id,
26999             transients = this.transients[id] ||
27000             (this.transients[id] = {});
27001
27002         if (transients[key] !== undefined) {
27003             return transients[key];
27004         }
27005
27006         transients[key] = fn.call(entity);
27007
27008         return transients[key];
27009     },
27010
27011     parentWays: function(entity) {
27012         var parents = this._parentWays[entity.id],
27013             result = [];
27014
27015         if (parents) {
27016             for (var i = 0; i < parents.length; i++) {
27017                 result.push(this.entity(parents[i]));
27018             }
27019         }
27020         return result;
27021     },
27022
27023     isPoi: function(entity) {
27024         var parentWays = this._parentWays[entity.id];
27025         return !parentWays || parentWays.length === 0;
27026     },
27027
27028     isShared: function(entity) {
27029         var parentWays = this._parentWays[entity.id];
27030         return parentWays && parentWays.length > 1;
27031     },
27032
27033     parentRelations: function(entity) {
27034         var parents = this._parentRels[entity.id],
27035             result = [];
27036
27037         if (parents) {
27038             for (var i = 0; i < parents.length; i++) {
27039                 result.push(this.entity(parents[i]));
27040             }
27041         }
27042         return result;
27043     },
27044
27045     childNodes: function(entity) {
27046         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27047         if (!entity.nodes) return [];
27048
27049         var nodes = [];
27050         for (var i = 0; i < entity.nodes.length; i++) {
27051             nodes[i] = this.entity(entity.nodes[i]);
27052         }
27053
27054         if (iD.debug) Object.freeze(nodes);
27055
27056         this._childNodes[entity.id] = nodes;
27057         return this._childNodes[entity.id];
27058     },
27059
27060     base: function() {
27061         return {
27062             'entities': iD.util.getPrototypeOf(this.entities),
27063             'parentWays': iD.util.getPrototypeOf(this._parentWays),
27064             'parentRels': iD.util.getPrototypeOf(this._parentRels)
27065         };
27066     },
27067
27068     // Unlike other graph methods, rebase mutates in place. This is because it
27069     // is used only during the history operation that merges newly downloaded
27070     // data into each state. To external consumers, it should appear as if the
27071     // graph always contained the newly downloaded data.
27072     rebase: function(entities, stack, force) {
27073         var base = this.base(),
27074             i, j, k, id;
27075
27076         for (i = 0; i < entities.length; i++) {
27077             var entity = entities[i];
27078
27079             if (!entity.visible || (!force && base.entities[entity.id]))
27080                 continue;
27081
27082             // Merging data into the base graph
27083             base.entities[entity.id] = entity;
27084             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
27085
27086             // Restore provisionally-deleted nodes that are discovered to have an extant parent
27087             if (entity.type === 'way') {
27088                 for (j = 0; j < entity.nodes.length; j++) {
27089                     id = entity.nodes[j];
27090                     for (k = 1; k < stack.length; k++) {
27091                         var ents = stack[k].entities;
27092                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27093                             delete ents[id];
27094                         }
27095                     }
27096                 }
27097             }
27098         }
27099
27100         for (i = 0; i < stack.length; i++) {
27101             stack[i]._updateRebased();
27102         }
27103     },
27104
27105     _updateRebased: function() {
27106         var base = this.base(),
27107             i, k, child, id, keys;
27108
27109         keys = Object.keys(this._parentWays);
27110         for (i = 0; i < keys.length; i++) {
27111             child = keys[i];
27112             if (base.parentWays[child]) {
27113                 for (k = 0; k < base.parentWays[child].length; k++) {
27114                     id = base.parentWays[child][k];
27115                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
27116                         this._parentWays[child].push(id);
27117                     }
27118                 }
27119             }
27120         }
27121
27122         keys = Object.keys(this._parentRels);
27123         for (i = 0; i < keys.length; i++) {
27124             child = keys[i];
27125             if (base.parentRels[child]) {
27126                 for (k = 0; k < base.parentRels[child].length; k++) {
27127                     id = base.parentRels[child][k];
27128                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
27129                         this._parentRels[child].push(id);
27130                     }
27131                 }
27132             }
27133         }
27134
27135         this.transients = {};
27136
27137         // this._childNodes is not updated, under the assumption that
27138         // ways are always downloaded with their child nodes.
27139     },
27140
27141     // Updates calculated properties (parentWays, parentRels) for the specified change
27142     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
27143
27144         parentWays = parentWays || this._parentWays;
27145         parentRels = parentRels || this._parentRels;
27146
27147         var type = entity && entity.type || oldentity && oldentity.type,
27148             removed, added, ways, rels, i;
27149
27150
27151         if (type === 'way') {
27152
27153             // Update parentWays
27154             if (oldentity && entity) {
27155                 removed = _.difference(oldentity.nodes, entity.nodes);
27156                 added = _.difference(entity.nodes, oldentity.nodes);
27157             } else if (oldentity) {
27158                 removed = oldentity.nodes;
27159                 added = [];
27160             } else if (entity) {
27161                 removed = [];
27162                 added = entity.nodes;
27163             }
27164             for (i = 0; i < removed.length; i++) {
27165                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
27166             }
27167             for (i = 0; i < added.length; i++) {
27168                 ways = _.without(parentWays[added[i]], entity.id);
27169                 ways.push(entity.id);
27170                 parentWays[added[i]] = ways;
27171             }
27172
27173         } else if (type === 'relation') {
27174
27175             // Update parentRels
27176             if (oldentity && entity) {
27177                 removed = _.difference(oldentity.members, entity.members);
27178                 added = _.difference(entity.members, oldentity);
27179             } else if (oldentity) {
27180                 removed = oldentity.members;
27181                 added = [];
27182             } else if (entity) {
27183                 removed = [];
27184                 added = entity.members;
27185             }
27186             for (i = 0; i < removed.length; i++) {
27187                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
27188             }
27189             for (i = 0; i < added.length; i++) {
27190                 rels = _.without(parentRels[added[i].id], entity.id);
27191                 rels.push(entity.id);
27192                 parentRels[added[i].id] = rels;
27193             }
27194         }
27195     },
27196
27197     replace: function(entity) {
27198         if (this.entities[entity.id] === entity)
27199             return this;
27200
27201         return this.update(function() {
27202             this._updateCalculated(this.entities[entity.id], entity);
27203             this.entities[entity.id] = entity;
27204         });
27205     },
27206
27207     remove: function(entity) {
27208         return this.update(function() {
27209             this._updateCalculated(entity, undefined);
27210             this.entities[entity.id] = undefined;
27211         });
27212     },
27213
27214     revert: function(id) {
27215         var baseEntity = this.base().entities[id],
27216             headEntity = this.entities[id];
27217
27218         if (headEntity === baseEntity)
27219             return this;
27220
27221         return this.update(function() {
27222             this._updateCalculated(headEntity, baseEntity);
27223             delete this.entities[id];
27224         });
27225     },
27226
27227     update: function() {
27228         var graph = this.frozen ? iD.Graph(this, true) : this;
27229
27230         for (var i = 0; i < arguments.length; i++) {
27231             arguments[i].call(graph, graph);
27232         }
27233
27234         if (this.frozen) graph.frozen = true;
27235
27236         return graph;
27237     },
27238
27239     // Obliterates any existing entities
27240     load: function(entities) {
27241         var base = this.base();
27242         this.entities = Object.create(base.entities);
27243
27244         for (var i in entities) {
27245             this.entities[i] = entities[i];
27246             this._updateCalculated(base.entities[i], this.entities[i]);
27247         }
27248
27249         return this;
27250     }
27251 };
27252 iD.History = function(context) {
27253     var stack, index, tree,
27254         imageryUsed = ['Bing'],
27255         dispatch = d3.dispatch('change', 'undone', 'redone'),
27256         lock = iD.util.SessionMutex('lock');
27257
27258     function perform(actions) {
27259         actions = Array.prototype.slice.call(actions);
27260
27261         var annotation;
27262
27263         if (!_.isFunction(_.last(actions))) {
27264             annotation = actions.pop();
27265         }
27266
27267         var graph = stack[index].graph;
27268         for (var i = 0; i < actions.length; i++) {
27269             graph = actions[i](graph);
27270         }
27271
27272         return {
27273             graph: graph,
27274             annotation: annotation,
27275             imageryUsed: imageryUsed
27276         };
27277     }
27278
27279     function change(previous) {
27280         var difference = iD.Difference(previous, history.graph());
27281         dispatch.change(difference);
27282         return difference;
27283     }
27284
27285     // iD uses namespaced keys so multiple installations do not conflict
27286     function getKey(n) {
27287         return 'iD_' + window.location.origin + '_' + n;
27288     }
27289
27290     var history = {
27291         graph: function() {
27292             return stack[index].graph;
27293         },
27294
27295         base: function() {
27296             return stack[0].graph;
27297         },
27298
27299         merge: function(entities, extent) {
27300             stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
27301             tree.rebase(entities, false);
27302
27303             dispatch.change(undefined, extent);
27304         },
27305
27306         perform: function() {
27307             var previous = stack[index].graph;
27308
27309             stack = stack.slice(0, index + 1);
27310             stack.push(perform(arguments));
27311             index++;
27312
27313             return change(previous);
27314         },
27315
27316         replace: function() {
27317             var previous = stack[index].graph;
27318
27319             // assert(index == stack.length - 1)
27320             stack[index] = perform(arguments);
27321
27322             return change(previous);
27323         },
27324
27325         pop: function() {
27326             var previous = stack[index].graph;
27327
27328             if (index > 0) {
27329                 index--;
27330                 stack.pop();
27331                 return change(previous);
27332             }
27333         },
27334
27335         // Same as calling pop and then perform
27336         overwrite: function() {
27337             var previous = stack[index].graph;
27338
27339             if (index > 0) {
27340                 index--;
27341                 stack.pop();
27342             }
27343             stack = stack.slice(0, index + 1);
27344             stack.push(perform(arguments));
27345             index++;
27346
27347             return change(previous);
27348         },
27349
27350         undo: function() {
27351             var previous = stack[index].graph;
27352
27353             // Pop to the next annotated state.
27354             while (index > 0) {
27355                 index--;
27356                 if (stack[index].annotation) break;
27357             }
27358
27359             dispatch.undone();
27360             return change(previous);
27361         },
27362
27363         redo: function() {
27364             var previous = stack[index].graph;
27365
27366             while (index < stack.length - 1) {
27367                 index++;
27368                 if (stack[index].annotation) break;
27369             }
27370
27371             dispatch.redone();
27372             return change(previous);
27373         },
27374
27375         undoAnnotation: function() {
27376             var i = index;
27377             while (i >= 0) {
27378                 if (stack[i].annotation) return stack[i].annotation;
27379                 i--;
27380             }
27381         },
27382
27383         redoAnnotation: function() {
27384             var i = index + 1;
27385             while (i <= stack.length - 1) {
27386                 if (stack[i].annotation) return stack[i].annotation;
27387                 i++;
27388             }
27389         },
27390
27391         intersects: function(extent) {
27392             return tree.intersects(extent, stack[index].graph);
27393         },
27394
27395         difference: function() {
27396             var base = stack[0].graph,
27397                 head = stack[index].graph;
27398             return iD.Difference(base, head);
27399         },
27400
27401         changes: function(action) {
27402             var base = stack[0].graph,
27403                 head = stack[index].graph;
27404
27405             if (action) {
27406                 head = action(head);
27407             }
27408
27409             var difference = iD.Difference(base, head);
27410
27411             return {
27412                 modified: difference.modified(),
27413                 created: difference.created(),
27414                 deleted: difference.deleted()
27415             };
27416         },
27417
27418         validate: function(changes) {
27419             return _(iD.validations)
27420                 .map(function(fn) { return fn()(changes, stack[index].graph); })
27421                 .flatten()
27422                 .value();
27423         },
27424
27425         hasChanges: function() {
27426             return this.difference().length() > 0;
27427         },
27428
27429         imageryUsed: function(sources) {
27430             if (sources) {
27431                 imageryUsed = sources;
27432                 return history;
27433             } else {
27434                 return _(stack.slice(1, index + 1))
27435                     .pluck('imageryUsed')
27436                     .flatten()
27437                     .unique()
27438                     .without(undefined, 'Custom')
27439                     .value();
27440             }
27441         },
27442
27443         reset: function() {
27444             stack = [{graph: iD.Graph()}];
27445             index = 0;
27446             tree = iD.Tree(stack[0].graph);
27447             dispatch.change();
27448             return history;
27449         },
27450
27451         toJSON: function() {
27452             if (!this.hasChanges()) return;
27453
27454             var allEntities = {},
27455                 baseEntities = {},
27456                 base = stack[0];
27457
27458             var s = stack.map(function(i) {
27459                 var modified = [], deleted = [];
27460
27461                 _.forEach(i.graph.entities, function(entity, id) {
27462                     if (entity) {
27463                         var key = iD.Entity.key(entity);
27464                         allEntities[key] = entity;
27465                         modified.push(key);
27466                     } else {
27467                         deleted.push(id);
27468                     }
27469
27470                     // make sure that the originals of changed or deleted entities get merged
27471                     // into the base of the stack after restoring the data from JSON.
27472                     if (id in base.graph.entities) {
27473                         baseEntities[id] = base.graph.entities[id];
27474                     }
27475                     // get originals of parent entities too
27476                     _.forEach(base.graph._parentWays[id], function(parentId) {
27477                         if (parentId in base.graph.entities) {
27478                             baseEntities[parentId] = base.graph.entities[parentId];
27479                         }
27480                     });
27481                 });
27482
27483                 var x = {};
27484
27485                 if (modified.length) x.modified = modified;
27486                 if (deleted.length) x.deleted = deleted;
27487                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27488                 if (i.annotation) x.annotation = i.annotation;
27489
27490                 return x;
27491             });
27492
27493             return JSON.stringify({
27494                 version: 3,
27495                 entities: _.values(allEntities),
27496                 baseEntities: _.values(baseEntities),
27497                 stack: s,
27498                 nextIDs: iD.Entity.id.next,
27499                 index: index
27500             });
27501         },
27502
27503         fromJSON: function(json, loadChildNodes) {
27504             var h = JSON.parse(json),
27505                 loadComplete = true;
27506
27507             iD.Entity.id.next = h.nextIDs;
27508             index = h.index;
27509
27510             if (h.version === 2 || h.version === 3) {
27511                 var allEntities = {};
27512
27513                 h.entities.forEach(function(entity) {
27514                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27515                 });
27516
27517                 if (h.version === 3) {
27518                     // This merges originals for changed entities into the base of
27519                     // the stack even if the current stack doesn't have them (for
27520                     // example when iD has been restarted in a different region)
27521                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27522                     stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27523                     tree.rebase(baseEntities, true);
27524
27525                     // When we restore a modified way, we also need to fetch any missing
27526                     // childnodes that would normally have been downloaded with it.. #2142
27527                     if (loadChildNodes) {
27528                         var missing =  _(baseEntities)
27529                                 .filter('type', 'way')
27530                                 .pluck('nodes')
27531                                 .flatten()
27532                                 .uniq()
27533                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27534                                 .value();
27535
27536                         if (!_.isEmpty(missing)) {
27537                             loadComplete = false;
27538                             context.redrawEnable(false);
27539
27540                             var loading = iD.ui.Loading(context).blocking(true);
27541                             context.container().call(loading);
27542
27543                             var childNodesLoaded = function(err, result) {
27544                                 if (!err) {
27545                                     var visible = _.groupBy(result.data, 'visible');
27546                                     if (!_.isEmpty(visible.true)) {
27547                                         missing = _.difference(missing, _.pluck(visible.true, 'id'));
27548                                         stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27549                                         tree.rebase(visible.true, true);
27550                                     }
27551
27552                                     // fetch older versions of nodes that were deleted..
27553                                     _.each(visible.false, function(entity) {
27554                                         context.connection()
27555                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27556                                     });
27557                                 }
27558
27559                                 if (err || _.isEmpty(missing)) {
27560                                     loading.close();
27561                                     context.redrawEnable(true);
27562                                     dispatch.change();
27563                                 }
27564                             };
27565
27566                             context.connection().loadMultiple(missing, childNodesLoaded);
27567                         }
27568                     }
27569                 }
27570
27571                 stack = h.stack.map(function(d) {
27572                     var entities = {}, entity;
27573
27574                     if (d.modified) {
27575                         d.modified.forEach(function(key) {
27576                             entity = allEntities[key];
27577                             entities[entity.id] = entity;
27578                         });
27579                     }
27580
27581                     if (d.deleted) {
27582                         d.deleted.forEach(function(id) {
27583                             entities[id] = undefined;
27584                         });
27585                     }
27586
27587                     return {
27588                         graph: iD.Graph(stack[0].graph).load(entities),
27589                         annotation: d.annotation,
27590                         imageryUsed: d.imageryUsed
27591                     };
27592                 });
27593
27594             } else { // original version
27595                 stack = h.stack.map(function(d) {
27596                     var entities = {};
27597
27598                     for (var i in d.entities) {
27599                         var entity = d.entities[i];
27600                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27601                     }
27602
27603                     d.graph = iD.Graph(stack[0].graph).load(entities);
27604                     return d;
27605                 });
27606             }
27607
27608             if (loadComplete) {
27609                 dispatch.change();
27610             }
27611
27612             return history;
27613         },
27614
27615         save: function() {
27616             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27617             return history;
27618         },
27619
27620         clearSaved: function() {
27621             context.debouncedSave.cancel();
27622             if (lock.locked()) context.storage(getKey('saved_history'), null);
27623             return history;
27624         },
27625
27626         lock: function() {
27627             return lock.lock();
27628         },
27629
27630         unlock: function() {
27631             lock.unlock();
27632         },
27633
27634         // is iD not open in another window and it detects that
27635         // there's a history stored in localStorage that's recoverable?
27636         restorableChanges: function() {
27637             return lock.locked() && !!context.storage(getKey('saved_history'));
27638         },
27639
27640         // load history from a version stored in localStorage
27641         restore: function() {
27642             if (!lock.locked()) return;
27643
27644             var json = context.storage(getKey('saved_history'));
27645             if (json) history.fromJSON(json, true);
27646         },
27647
27648         _getKey: getKey
27649
27650     };
27651
27652     history.reset();
27653
27654     return d3.rebind(history, dispatch, 'on');
27655 };
27656 iD.Node = iD.Entity.node = function iD_Node() {
27657     if (!(this instanceof iD_Node)) {
27658         return (new iD_Node()).initialize(arguments);
27659     } else if (arguments.length) {
27660         this.initialize(arguments);
27661     }
27662 };
27663
27664 iD.Node.prototype = Object.create(iD.Entity.prototype);
27665
27666 _.extend(iD.Node.prototype, {
27667     type: 'node',
27668
27669     extent: function() {
27670         return new iD.geo.Extent(this.loc);
27671     },
27672
27673     geometry: function(graph) {
27674         return graph.transient(this, 'geometry', function() {
27675             return graph.isPoi(this) ? 'point' : 'vertex';
27676         });
27677     },
27678
27679     move: function(loc) {
27680         return this.update({loc: loc});
27681     },
27682
27683     isIntersection: function(resolver) {
27684         return resolver.transient(this, 'isIntersection', function() {
27685             return resolver.parentWays(this).filter(function(parent) {
27686                 return (parent.tags.highway ||
27687                     parent.tags.waterway ||
27688                     parent.tags.railway ||
27689                     parent.tags.aeroway) &&
27690                     parent.geometry(resolver) === 'line';
27691             }).length > 1;
27692         });
27693     },
27694
27695     isHighwayIntersection: function(resolver) {
27696         return resolver.transient(this, 'isHighwayIntersection', function() {
27697             return resolver.parentWays(this).filter(function(parent) {
27698                 return parent.tags.highway && parent.geometry(resolver) === 'line';
27699             }).length > 1;
27700         });
27701     },
27702
27703     asJXON: function(changeset_id) {
27704         var r = {
27705             node: {
27706                 '@id': this.osmId(),
27707                 '@lon': this.loc[0],
27708                 '@lat': this.loc[1],
27709                 '@version': (this.version || 0),
27710                 tag: _.map(this.tags, function(v, k) {
27711                     return { keyAttributes: { k: k, v: v } };
27712                 })
27713             }
27714         };
27715         if (changeset_id) r.node['@changeset'] = changeset_id;
27716         return r;
27717     },
27718
27719     asGeoJSON: function() {
27720         return {
27721             type: 'Point',
27722             coordinates: this.loc
27723         };
27724     }
27725 });
27726 iD.Relation = iD.Entity.relation = function iD_Relation() {
27727     if (!(this instanceof iD_Relation)) {
27728         return (new iD_Relation()).initialize(arguments);
27729     } else if (arguments.length) {
27730         this.initialize(arguments);
27731     }
27732 };
27733
27734 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27735
27736 iD.Relation.creationOrder = function(a, b) {
27737     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27738     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27739
27740     if (aId < 0 || bId < 0) return aId - bId;
27741     return bId - aId;
27742 };
27743
27744 _.extend(iD.Relation.prototype, {
27745     type: 'relation',
27746     members: [],
27747
27748     copy: function(resolver, copies) {
27749         if (copies[this.id])
27750             return copies[this.id];
27751
27752         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
27753
27754         var members = this.members.map(function(member) {
27755             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
27756         });
27757
27758         copy = copy.update({members: members});
27759         copies[this.id] = copy;
27760
27761         return copy;
27762     },
27763
27764     extent: function(resolver, memo) {
27765         return resolver.transient(this, 'extent', function() {
27766             if (memo && memo[this.id]) return iD.geo.Extent();
27767             memo = memo || {};
27768             memo[this.id] = true;
27769
27770             var extent = iD.geo.Extent();
27771             for (var i = 0; i < this.members.length; i++) {
27772                 var member = resolver.hasEntity(this.members[i].id);
27773                 if (member) {
27774                     extent._extend(member.extent(resolver, memo));
27775                 }
27776             }
27777             return extent;
27778         });
27779     },
27780
27781     geometry: function(graph) {
27782         return graph.transient(this, 'geometry', function() {
27783             return this.isMultipolygon() ? 'area' : 'relation';
27784         });
27785     },
27786
27787     isDegenerate: function() {
27788         return this.members.length === 0;
27789     },
27790
27791     // Return an array of members, each extended with an 'index' property whose value
27792     // is the member index.
27793     indexedMembers: function() {
27794         var result = new Array(this.members.length);
27795         for (var i = 0; i < this.members.length; i++) {
27796             result[i] = _.extend({}, this.members[i], {index: i});
27797         }
27798         return result;
27799     },
27800
27801     // Return the first member with the given role. A copy of the member object
27802     // is returned, extended with an 'index' property whose value is the member index.
27803     memberByRole: function(role) {
27804         for (var i = 0; i < this.members.length; i++) {
27805             if (this.members[i].role === role) {
27806                 return _.extend({}, this.members[i], {index: i});
27807             }
27808         }
27809     },
27810
27811     // Return the first member with the given id. A copy of the member object
27812     // is returned, extended with an 'index' property whose value is the member index.
27813     memberById: function(id) {
27814         for (var i = 0; i < this.members.length; i++) {
27815             if (this.members[i].id === id) {
27816                 return _.extend({}, this.members[i], {index: i});
27817             }
27818         }
27819     },
27820
27821     // Return the first member with the given id and role. A copy of the member object
27822     // is returned, extended with an 'index' property whose value is the member index.
27823     memberByIdAndRole: function(id, role) {
27824         for (var i = 0; i < this.members.length; i++) {
27825             if (this.members[i].id === id && this.members[i].role === role) {
27826                 return _.extend({}, this.members[i], {index: i});
27827             }
27828         }
27829     },
27830
27831     addMember: function(member, index) {
27832         var members = this.members.slice();
27833         members.splice(index === undefined ? members.length : index, 0, member);
27834         return this.update({members: members});
27835     },
27836
27837     updateMember: function(member, index) {
27838         var members = this.members.slice();
27839         members.splice(index, 1, _.extend({}, members[index], member));
27840         return this.update({members: members});
27841     },
27842
27843     removeMember: function(index) {
27844         var members = this.members.slice();
27845         members.splice(index, 1);
27846         return this.update({members: members});
27847     },
27848
27849     removeMembersWithID: function(id) {
27850         var members = _.reject(this.members, function(m) { return m.id === id; });
27851         return this.update({members: members});
27852     },
27853
27854     // Wherever a member appears with id `needle.id`, replace it with a member
27855     // with id `replacement.id`, type `replacement.type`, and the original role,
27856     // unless a member already exists with that id and role. Return an updated
27857     // relation.
27858     replaceMember: function(needle, replacement) {
27859         if (!this.memberById(needle.id))
27860             return this;
27861
27862         var members = [];
27863
27864         for (var i = 0; i < this.members.length; i++) {
27865             var member = this.members[i];
27866             if (member.id !== needle.id) {
27867                 members.push(member);
27868             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27869                 members.push({id: replacement.id, type: replacement.type, role: member.role});
27870             }
27871         }
27872
27873         return this.update({members: members});
27874     },
27875
27876     asJXON: function(changeset_id) {
27877         var r = {
27878             relation: {
27879                 '@id': this.osmId(),
27880                 '@version': this.version || 0,
27881                 member: _.map(this.members, function(member) {
27882                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27883                 }),
27884                 tag: _.map(this.tags, function(v, k) {
27885                     return { keyAttributes: { k: k, v: v } };
27886                 })
27887             }
27888         };
27889         if (changeset_id) r.relation['@changeset'] = changeset_id;
27890         return r;
27891     },
27892
27893     asGeoJSON: function(resolver) {
27894         return resolver.transient(this, 'GeoJSON', function () {
27895             if (this.isMultipolygon()) {
27896                 return {
27897                     type: 'MultiPolygon',
27898                     coordinates: this.multipolygon(resolver)
27899                 };
27900             } else {
27901                 return {
27902                     type: 'FeatureCollection',
27903                     properties: this.tags,
27904                     features: this.members.map(function (member) {
27905                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
27906                     })
27907                 };
27908             }
27909         });
27910     },
27911
27912     area: function(resolver) {
27913         return resolver.transient(this, 'area', function() {
27914             return d3.geo.area(this.asGeoJSON(resolver));
27915         });
27916     },
27917
27918     isMultipolygon: function() {
27919         return this.tags.type === 'multipolygon';
27920     },
27921
27922     isComplete: function(resolver) {
27923         for (var i = 0; i < this.members.length; i++) {
27924             if (!resolver.hasEntity(this.members[i].id)) {
27925                 return false;
27926             }
27927         }
27928         return true;
27929     },
27930
27931     isRestriction: function() {
27932         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27933     },
27934
27935     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27936     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27937     //
27938     // This corresponds to the structure needed for rendering a multipolygon path using a
27939     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27940     //
27941     // In the case of invalid geometries, this function will still return a result which
27942     // includes the nodes of all way members, but some Nds may be unclosed and some inner
27943     // rings not matched with the intended outer ring.
27944     //
27945     multipolygon: function(resolver) {
27946         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
27947             inners = this.members.filter(function(m) { return 'inner' === m.role; });
27948
27949         outers = iD.geo.joinWays(outers, resolver);
27950         inners = iD.geo.joinWays(inners, resolver);
27951
27952         outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
27953         inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
27954
27955         var result = outers.map(function(o) {
27956             // Heuristic for detecting counterclockwise winding order. Assumes
27957             // that OpenStreetMap polygons are not hemisphere-spanning.
27958             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
27959         });
27960
27961         function findOuter(inner) {
27962             var o, outer;
27963
27964             for (o = 0; o < outers.length; o++) {
27965                 outer = outers[o];
27966                 if (iD.geo.polygonContainsPolygon(outer, inner))
27967                     return o;
27968             }
27969
27970             for (o = 0; o < outers.length; o++) {
27971                 outer = outers[o];
27972                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
27973                     return o;
27974             }
27975         }
27976
27977         for (var i = 0; i < inners.length; i++) {
27978             var inner = inners[i];
27979
27980             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
27981                 inner = inner.reverse();
27982             }
27983
27984             var o = findOuter(inners[i]);
27985             if (o !== undefined)
27986                 result[o].push(inners[i]);
27987             else
27988                 result.push([inners[i]]); // Invalid geometry
27989         }
27990
27991         return result;
27992     }
27993 });
27994 iD.oneWayTags = {
27995     'aerialway': {
27996         'chair_lift': true,
27997         'mixed_lift': true,
27998         't-bar': true,
27999         'j-bar': true,
28000         'platter': true,
28001         'rope_tow': true,
28002         'magic_carpet': true,
28003         'yes': true
28004     },
28005     'highway': {
28006         'motorway': true,
28007         'motorway_link': true
28008     },
28009     'junction': {
28010         'roundabout': true
28011     },
28012     'man_made': {
28013         'piste:halfpipe': true
28014     },
28015     'piste:type': {
28016         'downhill': true,
28017         'sled': true,
28018         'yes': true
28019     },
28020     'waterway': {
28021         'river': true,
28022         'stream': true
28023     }
28024 };
28025
28026 iD.pavedTags = {
28027     'surface': {
28028         'paved': true,
28029         'asphalt': true,
28030         'concrete': true
28031     },
28032     'tracktype': {
28033         'grade1': true
28034     }
28035 };
28036
28037 iD.interestingTag = function (key) {
28038     return key !== 'attribution' &&
28039         key !== 'created_by' &&
28040         key !== 'source' &&
28041         key !== 'odbl' &&
28042         key.indexOf('tiger:') !== 0;
28043
28044 };
28045 iD.Tree = function(head) {
28046     var rtree = rbush(),
28047         rectangles = {};
28048
28049     function entityRectangle(entity) {
28050         var rect = entity.extent(head).rectangle();
28051         rect.id = entity.id;
28052         rectangles[entity.id] = rect;
28053         return rect;
28054     }
28055
28056     function updateParents(entity, insertions, memo) {
28057         head.parentWays(entity).forEach(function(way) {
28058             if (rectangles[way.id]) {
28059                 rtree.remove(rectangles[way.id]);
28060                 insertions[way.id] = way;
28061             }
28062             updateParents(way, insertions, memo);
28063         });
28064
28065         head.parentRelations(entity).forEach(function(relation) {
28066             if (memo[entity.id]) return;
28067             memo[entity.id] = true;
28068             if (rectangles[relation.id]) {
28069                 rtree.remove(rectangles[relation.id]);
28070                 insertions[relation.id] = relation;
28071             }
28072             updateParents(relation, insertions, memo);
28073         });
28074     }
28075
28076     var tree = {};
28077
28078     tree.rebase = function(entities, force) {
28079         var insertions = {};
28080
28081         for (var i = 0; i < entities.length; i++) {
28082             var entity = entities[i];
28083
28084             if (!entity.visible)
28085                 continue;
28086
28087             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
28088                 if (!force) {
28089                     continue;
28090                 } else if (rectangles[entity.id]) {
28091                     rtree.remove(rectangles[entity.id]);
28092                 }
28093             }
28094
28095             insertions[entity.id] = entity;
28096             updateParents(entity, insertions, {});
28097         }
28098
28099         rtree.load(_.map(insertions, entityRectangle));
28100
28101         return tree;
28102     };
28103
28104     tree.intersects = function(extent, graph) {
28105         if (graph !== head) {
28106             var diff = iD.Difference(head, graph),
28107                 insertions = {};
28108
28109             head = graph;
28110
28111             diff.deleted().forEach(function(entity) {
28112                 rtree.remove(rectangles[entity.id]);
28113                 delete rectangles[entity.id];
28114             });
28115
28116             diff.modified().forEach(function(entity) {
28117                 rtree.remove(rectangles[entity.id]);
28118                 insertions[entity.id] = entity;
28119                 updateParents(entity, insertions, {});
28120             });
28121
28122             diff.created().forEach(function(entity) {
28123                 insertions[entity.id] = entity;
28124             });
28125
28126             rtree.load(_.map(insertions, entityRectangle));
28127         }
28128
28129         return rtree.search(extent.rectangle()).map(function(rect) {
28130             return head.entity(rect.id);
28131         });
28132     };
28133
28134     return tree;
28135 };
28136 iD.Way = iD.Entity.way = function iD_Way() {
28137     if (!(this instanceof iD_Way)) {
28138         return (new iD_Way()).initialize(arguments);
28139     } else if (arguments.length) {
28140         this.initialize(arguments);
28141     }
28142 };
28143
28144 iD.Way.prototype = Object.create(iD.Entity.prototype);
28145
28146 _.extend(iD.Way.prototype, {
28147     type: 'way',
28148     nodes: [],
28149
28150     copy: function(resolver, copies) {
28151         if (copies[this.id])
28152             return copies[this.id];
28153
28154         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
28155
28156         var nodes = this.nodes.map(function(id) {
28157             return resolver.entity(id).copy(resolver, copies).id;
28158         });
28159
28160         copy = copy.update({nodes: nodes});
28161         copies[this.id] = copy;
28162
28163         return copy;
28164     },
28165
28166     extent: function(resolver) {
28167         return resolver.transient(this, 'extent', function() {
28168             var extent = iD.geo.Extent();
28169             for (var i = 0; i < this.nodes.length; i++) {
28170                 var node = resolver.hasEntity(this.nodes[i]);
28171                 if (node) {
28172                     extent._extend(node.extent());
28173                 }
28174             }
28175             return extent;
28176         });
28177     },
28178
28179     first: function() {
28180         return this.nodes[0];
28181     },
28182
28183     last: function() {
28184         return this.nodes[this.nodes.length - 1];
28185     },
28186
28187     contains: function(node) {
28188         return this.nodes.indexOf(node) >= 0;
28189     },
28190
28191     affix: function(node) {
28192         if (this.nodes[0] === node) return 'prefix';
28193         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
28194     },
28195
28196     layer: function() {
28197         // explicit layer tag, clamp between -10, 10..
28198         if (this.tags.layer !== undefined) {
28199             return Math.max(-10, Math.min(+(this.tags.layer), 10));
28200         }
28201
28202         // implied layer tag..
28203         if (this.tags.location === 'overground') return 1;
28204         if (this.tags.location === 'underground') return -1;
28205         if (this.tags.location === 'underwater') return -10;
28206
28207         if (this.tags.power === 'line') return 10;
28208         if (this.tags.power === 'minor_line') return 10;
28209         if (this.tags.aerialway) return 10;
28210         if (this.tags.bridge) return 1;
28211         if (this.tags.cutting) return -1;
28212         if (this.tags.tunnel) return -1;
28213         if (this.tags.waterway) return -1;
28214         if (this.tags.man_made === 'pipeline') return -10;
28215         if (this.tags.boundary) return -10;
28216         return 0;
28217     },
28218
28219     isOneWay: function() {
28220         // explicit oneway tag..
28221         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
28222         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
28223
28224         // implied oneway tag..
28225         for (var key in this.tags) {
28226             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
28227                 return true;
28228         }
28229         return false;
28230     },
28231
28232     isClosed: function() {
28233         return this.nodes.length > 0 && this.first() === this.last();
28234     },
28235
28236     isConvex: function(resolver) {
28237         if (!this.isClosed() || this.isDegenerate()) return null;
28238
28239         var nodes = _.uniq(resolver.childNodes(this)),
28240             coords = _.pluck(nodes, 'loc'),
28241             curr = 0, prev = 0;
28242
28243         for (var i = 0; i < coords.length; i++) {
28244             var o = coords[(i+1) % coords.length],
28245                 a = coords[i],
28246                 b = coords[(i+2) % coords.length],
28247                 res = iD.geo.cross(o, a, b);
28248
28249             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
28250             if (curr === 0) {
28251                 continue;
28252             } else if (prev && curr !== prev) {
28253                 return false;
28254             }
28255             prev = curr;
28256         }
28257         return true;
28258     },
28259
28260     isArea: function() {
28261         if (this.tags.area === 'yes')
28262             return true;
28263         if (!this.isClosed() || this.tags.area === 'no')
28264             return false;
28265         for (var key in this.tags)
28266             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
28267                 return true;
28268         return false;
28269     },
28270
28271     isDegenerate: function() {
28272         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
28273     },
28274
28275     areAdjacent: function(n1, n2) {
28276         for (var i = 0; i < this.nodes.length; i++) {
28277             if (this.nodes[i] === n1) {
28278                 if (this.nodes[i - 1] === n2) return true;
28279                 if (this.nodes[i + 1] === n2) return true;
28280             }
28281         }
28282         return false;
28283     },
28284
28285     geometry: function(graph) {
28286         return graph.transient(this, 'geometry', function() {
28287             return this.isArea() ? 'area' : 'line';
28288         });
28289     },
28290
28291     addNode: function(id, index) {
28292         var nodes = this.nodes.slice();
28293         nodes.splice(index === undefined ? nodes.length : index, 0, id);
28294         return this.update({nodes: nodes});
28295     },
28296
28297     updateNode: function(id, index) {
28298         var nodes = this.nodes.slice();
28299         nodes.splice(index, 1, id);
28300         return this.update({nodes: nodes});
28301     },
28302
28303     replaceNode: function(needle, replacement) {
28304         if (this.nodes.indexOf(needle) < 0)
28305             return this;
28306
28307         var nodes = this.nodes.slice();
28308         for (var i = 0; i < nodes.length; i++) {
28309             if (nodes[i] === needle) {
28310                 nodes[i] = replacement;
28311             }
28312         }
28313         return this.update({nodes: nodes});
28314     },
28315
28316     removeNode: function(id) {
28317         var nodes = [];
28318
28319         for (var i = 0; i < this.nodes.length; i++) {
28320             var node = this.nodes[i];
28321             if (node !== id && nodes[nodes.length - 1] !== node) {
28322                 nodes.push(node);
28323             }
28324         }
28325
28326         // Preserve circularity
28327         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28328             nodes.push(nodes[0]);
28329         }
28330
28331         return this.update({nodes: nodes});
28332     },
28333
28334     asJXON: function(changeset_id) {
28335         var r = {
28336             way: {
28337                 '@id': this.osmId(),
28338                 '@version': this.version || 0,
28339                 nd: _.map(this.nodes, function(id) {
28340                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28341                 }),
28342                 tag: _.map(this.tags, function(v, k) {
28343                     return { keyAttributes: { k: k, v: v } };
28344                 })
28345             }
28346         };
28347         if (changeset_id) r.way['@changeset'] = changeset_id;
28348         return r;
28349     },
28350
28351     asGeoJSON: function(resolver) {
28352         return resolver.transient(this, 'GeoJSON', function() {
28353             var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28354             if (this.isArea() && this.isClosed()) {
28355                 return {
28356                     type: 'Polygon',
28357                     coordinates: [coordinates]
28358                 };
28359             } else {
28360                 return {
28361                     type: 'LineString',
28362                     coordinates: coordinates
28363                 };
28364             }
28365         });
28366     },
28367
28368     area: function(resolver) {
28369         return resolver.transient(this, 'area', function() {
28370             var nodes = resolver.childNodes(this);
28371
28372             var json = {
28373                 type: 'Polygon',
28374                 coordinates: [_.pluck(nodes, 'loc')]
28375             };
28376
28377             if (!this.isClosed() && nodes.length) {
28378                 json.coordinates[0].push(nodes[0].loc);
28379             }
28380
28381             var area = d3.geo.area(json);
28382
28383             // Heuristic for detecting counterclockwise winding order. Assumes
28384             // that OpenStreetMap polygons are not hemisphere-spanning.
28385             if (area > 2 * Math.PI) {
28386                 json.coordinates[0] = json.coordinates[0].reverse();
28387                 area = d3.geo.area(json);
28388             }
28389
28390             return isNaN(area) ? 0 : area;
28391         });
28392     }
28393 });
28394 iD.Background = function(context) {
28395     var dispatch = d3.dispatch('change'),
28396         baseLayer = iD.TileLayer().projection(context.projection),
28397         overlayLayers = [];
28398
28399     var backgroundSources;
28400
28401     function findSource(id) {
28402         return _.find(backgroundSources, function(d) {
28403             return d.id && d.id === id;
28404         });
28405     }
28406
28407     function updateImagery() {
28408         var b = background.baseLayerSource(),
28409             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28410             q = iD.util.stringQs(location.hash.substring(1));
28411
28412         var id = b.id;
28413         if (id === 'custom') {
28414             id = 'custom:' + b.template;
28415         }
28416
28417         if (id) {
28418             q.background = id;
28419         } else {
28420             delete q.background;
28421         }
28422
28423         if (o) {
28424             q.overlays = o;
28425         } else {
28426             delete q.overlays;
28427         }
28428
28429         location.replace('#' + iD.util.qsString(q, true));
28430
28431         var imageryUsed = [b.imageryUsed()];
28432
28433         overlayLayers.forEach(function (d) {
28434             var source = d.source();
28435             if (!source.isLocatorOverlay()) {
28436                 imageryUsed.push(source.imageryUsed());
28437             }
28438         });
28439
28440         var gpx = context.layers().layer('gpx');
28441         if (gpx && gpx.enabled() && gpx.hasGpx()) {
28442             imageryUsed.push('Local GPX');
28443         }
28444
28445         context.history().imageryUsed(imageryUsed);
28446     }
28447
28448     function background(selection) {
28449         var base = selection.selectAll('.layer-background')
28450             .data([0]);
28451
28452         base.enter()
28453             .insert('div', '.layer-data')
28454             .attr('class', 'layer layer-background');
28455
28456         base.call(baseLayer);
28457
28458         var overlays = selection.selectAll('.layer-overlay')
28459             .data(overlayLayers, function(d) { return d.source().name(); });
28460
28461         overlays.enter()
28462             .insert('div', '.layer-data')
28463             .attr('class', 'layer layer-overlay');
28464
28465         overlays.each(function(layer) {
28466             d3.select(this).call(layer);
28467         });
28468
28469         overlays.exit()
28470             .remove();
28471     }
28472
28473     background.sources = function(extent) {
28474         return backgroundSources.filter(function(source) {
28475             return source.intersects(extent);
28476         });
28477     };
28478
28479     background.dimensions = function(_) {
28480         baseLayer.dimensions(_);
28481
28482         overlayLayers.forEach(function(layer) {
28483             layer.dimensions(_);
28484         });
28485     };
28486
28487     background.baseLayerSource = function(d) {
28488         if (!arguments.length) return baseLayer.source();
28489
28490         baseLayer.source(d);
28491         dispatch.change();
28492         updateImagery();
28493
28494         return background;
28495     };
28496
28497     background.bing = function() {
28498         background.baseLayerSource(findSource('Bing'));
28499     };
28500
28501     background.showsLayer = function(d) {
28502         return d === baseLayer.source() ||
28503             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28504             overlayLayers.some(function(l) { return l.source() === d; });
28505     };
28506
28507     background.overlayLayerSources = function() {
28508         return overlayLayers.map(function (l) { return l.source(); });
28509     };
28510
28511     background.toggleOverlayLayer = function(d) {
28512         var layer;
28513
28514         for (var i = 0; i < overlayLayers.length; i++) {
28515             layer = overlayLayers[i];
28516             if (layer.source() === d) {
28517                 overlayLayers.splice(i, 1);
28518                 dispatch.change();
28519                 updateImagery();
28520                 return;
28521             }
28522         }
28523
28524         layer = iD.TileLayer()
28525             .source(d)
28526             .projection(context.projection)
28527             .dimensions(baseLayer.dimensions());
28528
28529         overlayLayers.push(layer);
28530         dispatch.change();
28531         updateImagery();
28532     };
28533
28534     background.nudge = function(d, zoom) {
28535         baseLayer.source().nudge(d, zoom);
28536         dispatch.change();
28537         return background;
28538     };
28539
28540     background.offset = function(d) {
28541         if (!arguments.length) return baseLayer.source().offset();
28542         baseLayer.source().offset(d);
28543         dispatch.change();
28544         return background;
28545     };
28546
28547     background.load = function(imagery) {
28548         function parseMap(qmap) {
28549             if (!qmap) return false;
28550             var args = qmap.split('/').map(Number);
28551             if (args.length < 3 || args.some(isNaN)) return false;
28552             return iD.geo.Extent([args[1], args[2]]);
28553         }
28554
28555         var q = iD.util.stringQs(location.hash.substring(1)),
28556             chosen = q.background || q.layer,
28557             extent = parseMap(q.map),
28558             best;
28559
28560         backgroundSources = imagery.map(function(source) {
28561             if (source.type === 'bing') {
28562                 return iD.BackgroundSource.Bing(source, dispatch);
28563             } else {
28564                 return iD.BackgroundSource(source);
28565             }
28566         });
28567
28568         backgroundSources.unshift(iD.BackgroundSource.None());
28569
28570         if (!chosen && extent) {
28571             best = _.find(this.sources(extent), function(s) { return s.best(); });
28572         }
28573
28574         if (chosen && chosen.indexOf('custom:') === 0) {
28575             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28576         } else {
28577             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
28578         }
28579
28580         var locator = _.find(backgroundSources, function(d) {
28581             return d.overlay && d.default;
28582         });
28583
28584         if (locator) {
28585             background.toggleOverlayLayer(locator);
28586         }
28587
28588         var overlays = (q.overlays || '').split(',');
28589         overlays.forEach(function(overlay) {
28590             overlay = findSource(overlay);
28591             if (overlay) background.toggleOverlayLayer(overlay);
28592         });
28593
28594         if (q.gpx) {
28595             var gpx = context.layers().layer('gpx');
28596             if (gpx) { gpx.url(q.gpx); }
28597         }
28598     };
28599
28600     return d3.rebind(background, dispatch, 'on');
28601 };
28602 iD.BackgroundSource = function(data) {
28603     var source = _.clone(data),
28604         offset = [0, 0],
28605         name = source.name,
28606         best = !!source.best;
28607
28608     source.scaleExtent = data.scaleExtent || [0, 20];
28609     source.overzoom = data.overzoom !== false;
28610
28611     source.offset = function(_) {
28612         if (!arguments.length) return offset;
28613         offset = _;
28614         return source;
28615     };
28616
28617     source.nudge = function(_, zoomlevel) {
28618         offset[0] += _[0] / Math.pow(2, zoomlevel);
28619         offset[1] += _[1] / Math.pow(2, zoomlevel);
28620         return source;
28621     };
28622
28623     source.name = function() {
28624         return name;
28625     };
28626
28627     source.best = function() {
28628         return best;
28629     };
28630
28631     source.imageryUsed = function() {
28632         return source.id || name;
28633     };
28634
28635     source.url = function(coord) {
28636         return data.template
28637             .replace('{x}', coord[0])
28638             .replace('{y}', coord[1])
28639             // TMS-flipped y coordinate
28640             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28641             .replace(/\{z(oom)?\}/, coord[2])
28642             .replace(/\{switch:([^}]+)\}/, function(s, r) {
28643                 var subdomains = r.split(',');
28644                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28645             })
28646             .replace('{u}', function() {
28647                 var u = '';
28648                 for (var zoom = coord[2]; zoom > 0; zoom--) {
28649                     var b = 0;
28650                     var mask = 1 << (zoom - 1);
28651                     if ((coord[0] & mask) !== 0) b++;
28652                     if ((coord[1] & mask) !== 0) b += 2;
28653                     u += b.toString();
28654                 }
28655                 return u;
28656             });
28657     };
28658
28659     source.intersects = function(extent) {
28660         extent = extent.polygon();
28661         return !data.polygon || data.polygon.some(function(polygon) {
28662             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28663         });
28664     };
28665
28666     source.validZoom = function(z) {
28667         return source.scaleExtent[0] <= z &&
28668             (source.overzoom || source.scaleExtent[1] > z);
28669     };
28670
28671     source.isLocatorOverlay = function() {
28672         return name === 'Locator Overlay';
28673     };
28674
28675     source.copyrightNotices = function() {};
28676
28677     return source;
28678 };
28679
28680 iD.BackgroundSource.Bing = function(data, dispatch) {
28681     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28682     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28683
28684     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28685
28686     var bing = iD.BackgroundSource(data),
28687         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28688         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28689             key + '&jsonp={callback}',
28690         providers = [];
28691
28692     d3.jsonp(url, function(json) {
28693         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28694             return {
28695                 attribution: provider.attribution,
28696                 areas: provider.coverageAreas.map(function(area) {
28697                     return {
28698                         zoom: [area.zoomMin, area.zoomMax],
28699                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28700                     };
28701                 })
28702             };
28703         });
28704         dispatch.change();
28705     });
28706
28707     bing.copyrightNotices = function(zoom, extent) {
28708         zoom = Math.min(zoom, 21);
28709         return providers.filter(function(provider) {
28710             return _.any(provider.areas, function(area) {
28711                 return extent.intersects(area.extent) &&
28712                     area.zoom[0] <= zoom &&
28713                     area.zoom[1] >= zoom;
28714             });
28715         }).map(function(provider) {
28716             return provider.attribution;
28717         }).join(', ');
28718     };
28719
28720     bing.logo = 'bing_maps.png';
28721     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
28722
28723     return bing;
28724 };
28725
28726 iD.BackgroundSource.None = function() {
28727     var source = iD.BackgroundSource({id: 'none', template: ''});
28728
28729     source.name = function() {
28730         return t('background.none');
28731     };
28732
28733     source.imageryUsed = function() {
28734         return 'None';
28735     };
28736
28737     return source;
28738 };
28739
28740 iD.BackgroundSource.Custom = function(template) {
28741     var source = iD.BackgroundSource({id: 'custom', template: template});
28742
28743     source.name = function() {
28744         return t('background.custom');
28745     };
28746
28747     source.imageryUsed = function() {
28748         return 'Custom (' + template + ')';
28749     };
28750
28751     return source;
28752 };
28753 iD.Features = function(context) {
28754     var major_roads = {
28755         'motorway': true,
28756         'motorway_link': true,
28757         'trunk': true,
28758         'trunk_link': true,
28759         'primary': true,
28760         'primary_link': true,
28761         'secondary': true,
28762         'secondary_link': true,
28763         'tertiary': true,
28764         'tertiary_link': true,
28765         'residential': true
28766     };
28767
28768     var minor_roads = {
28769         'service': true,
28770         'living_street': true,
28771         'road': true,
28772         'unclassified': true,
28773         'track': true
28774     };
28775
28776     var paths = {
28777         'path': true,
28778         'footway': true,
28779         'cycleway': true,
28780         'bridleway': true,
28781         'steps': true,
28782         'pedestrian': true,
28783         'corridor': true
28784     };
28785
28786     var past_futures = {
28787         'proposed': true,
28788         'construction': true,
28789         'abandoned': true,
28790         'dismantled': true,
28791         'disused': true,
28792         'razed': true,
28793         'demolished': true,
28794         'obliterated': true
28795     };
28796
28797     var dispatch = d3.dispatch('change', 'redraw'),
28798         _cullFactor = 1,
28799         _cache = {},
28800         _features = {},
28801         _stats = {},
28802         _keys = [],
28803         _hidden = [];
28804
28805     function update() {
28806         _hidden = features.hidden();
28807         dispatch.change();
28808         dispatch.redraw();
28809     }
28810
28811     function defineFeature(k, filter, max) {
28812         _keys.push(k);
28813         _features[k] = {
28814             filter: filter,
28815             enabled: true,   // whether the user wants it enabled..
28816             count: 0,
28817             currentMax: (max || Infinity),
28818             defaultMax: (max || Infinity),
28819             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28820             disable: function() { this.enabled = false; this.currentMax = 0; },
28821             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28822             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28823         };
28824     }
28825
28826
28827     defineFeature('points', function isPoint(entity, resolver, geometry) {
28828         return geometry === 'point';
28829     }, 200);
28830
28831     defineFeature('major_roads', function isMajorRoad(entity) {
28832         return major_roads[entity.tags.highway];
28833     });
28834
28835     defineFeature('minor_roads', function isMinorRoad(entity) {
28836         return minor_roads[entity.tags.highway];
28837     });
28838
28839     defineFeature('paths', function isPath(entity) {
28840         return paths[entity.tags.highway];
28841     });
28842
28843     defineFeature('buildings', function isBuilding(entity) {
28844         return (
28845             !!entity.tags['building:part'] ||
28846             (!!entity.tags.building && entity.tags.building !== 'no') ||
28847             entity.tags.amenity === 'shelter' ||
28848             entity.tags.parking === 'multi-storey' ||
28849             entity.tags.parking === 'sheds' ||
28850             entity.tags.parking === 'carports' ||
28851             entity.tags.parking === 'garage_boxes'
28852         );
28853     }, 250);
28854
28855     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28856         return geometry === 'area' &&
28857             !_features.buildings.filter(entity) &&
28858             !_features.water.filter(entity);
28859     });
28860
28861     defineFeature('boundaries', function isBoundary(entity) {
28862         return !!entity.tags.boundary;
28863     });
28864
28865     defineFeature('water', function isWater(entity) {
28866         return (
28867             !!entity.tags.waterway ||
28868             entity.tags.natural === 'water' ||
28869             entity.tags.natural === 'coastline' ||
28870             entity.tags.natural === 'bay' ||
28871             entity.tags.landuse === 'pond' ||
28872             entity.tags.landuse === 'basin' ||
28873             entity.tags.landuse === 'reservoir' ||
28874             entity.tags.landuse === 'salt_pond'
28875         );
28876     });
28877
28878     defineFeature('rail', function isRail(entity) {
28879         return (
28880             !!entity.tags.railway ||
28881             entity.tags.landuse === 'railway'
28882         ) && !(
28883             major_roads[entity.tags.highway] ||
28884             minor_roads[entity.tags.highway] ||
28885             paths[entity.tags.highway]
28886         );
28887     });
28888
28889     defineFeature('power', function isPower(entity) {
28890         return !!entity.tags.power;
28891     });
28892
28893     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
28894     defineFeature('past_future', function isPastFuture(entity) {
28895         if (
28896             major_roads[entity.tags.highway] ||
28897             minor_roads[entity.tags.highway] ||
28898             paths[entity.tags.highway]
28899         ) { return false; }
28900
28901         var strings = Object.keys(entity.tags);
28902
28903         for (var i = 0; i < strings.length; i++) {
28904             var s = strings[i];
28905             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
28906         }
28907         return false;
28908     });
28909
28910     // Lines or areas that don't match another feature filter.
28911     // IMPORTANT: The 'others' feature must be the last one defined,
28912     //   so that code in getMatches can skip this test if `hasMatch = true`
28913     defineFeature('others', function isOther(entity, resolver, geometry) {
28914         return (geometry === 'line' || geometry === 'area');
28915     });
28916
28917
28918     function features() {}
28919
28920     features.features = function() {
28921         return _features;
28922     };
28923
28924     features.keys = function() {
28925         return _keys;
28926     };
28927
28928     features.enabled = function(k) {
28929         if (!arguments.length) {
28930             return _.filter(_keys, function(k) { return _features[k].enabled; });
28931         }
28932         return _features[k] && _features[k].enabled;
28933     };
28934
28935     features.disabled = function(k) {
28936         if (!arguments.length) {
28937             return _.reject(_keys, function(k) { return _features[k].enabled; });
28938         }
28939         return _features[k] && !_features[k].enabled;
28940     };
28941
28942     features.hidden = function(k) {
28943         if (!arguments.length) {
28944             return _.filter(_keys, function(k) { return _features[k].hidden(); });
28945         }
28946         return _features[k] && _features[k].hidden();
28947     };
28948
28949     features.autoHidden = function(k) {
28950         if (!arguments.length) {
28951             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
28952         }
28953         return _features[k] && _features[k].autoHidden();
28954     };
28955
28956     features.enable = function(k) {
28957         if (_features[k] && !_features[k].enabled) {
28958             _features[k].enable();
28959             update();
28960         }
28961     };
28962
28963     features.disable = function(k) {
28964         if (_features[k] && _features[k].enabled) {
28965             _features[k].disable();
28966             update();
28967         }
28968     };
28969
28970     features.toggle = function(k) {
28971         if (_features[k]) {
28972             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
28973             update();
28974         }
28975     };
28976
28977     features.resetStats = function() {
28978         _.each(_features, function(f) { f.count = 0; });
28979         dispatch.change();
28980     };
28981
28982     features.gatherStats = function(d, resolver, dimensions) {
28983         var needsRedraw = false,
28984             type = _.groupBy(d, function(ent) { return ent.type; }),
28985             entities = [].concat(type.relation || [], type.way || [], type.node || []),
28986             currHidden, geometry, matches;
28987
28988         _.each(_features, function(f) { f.count = 0; });
28989
28990         // adjust the threshold for point/building culling based on viewport size..
28991         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
28992         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
28993
28994         for (var i = 0; i < entities.length; i++) {
28995             geometry = entities[i].geometry(resolver);
28996             if (!(geometry === 'vertex' || geometry === 'relation')) {
28997                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
28998                 for (var j = 0; j < matches.length; j++) {
28999                     _features[matches[j]].count++;
29000                 }
29001             }
29002         }
29003
29004         currHidden = features.hidden();
29005         if (currHidden !== _hidden) {
29006             _hidden = currHidden;
29007             needsRedraw = true;
29008             dispatch.change();
29009         }
29010
29011         return needsRedraw;
29012     };
29013
29014     features.stats = function() {
29015         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
29016         return _stats;
29017     };
29018
29019     features.clear = function(d) {
29020         for (var i = 0; i < d.length; i++) {
29021             features.clearEntity(d[i]);
29022         }
29023     };
29024
29025     features.clearEntity = function(entity) {
29026         delete _cache[iD.Entity.key(entity)];
29027     };
29028
29029     features.reset = function() {
29030         _cache = {};
29031     };
29032
29033     features.getMatches = function(entity, resolver, geometry) {
29034         if (geometry === 'vertex' || geometry === 'relation') return {};
29035
29036         var ent = iD.Entity.key(entity);
29037         if (!_cache[ent]) {
29038             _cache[ent] = {};
29039         }
29040
29041         if (!_cache[ent].matches) {
29042             var matches = {},
29043                 hasMatch = false;
29044
29045             for (var i = 0; i < _keys.length; i++) {
29046                 if (_keys[i] === 'others') {
29047                     if (hasMatch) continue;
29048
29049                     // Multipolygon members:
29050                     // If an entity...
29051                     //   1. is a way that hasn't matched other "interesting" feature rules,
29052                     //   2. and it belongs to a single parent multipolygon relation
29053                     // ...then match whatever feature rules the parent multipolygon has matched.
29054                     // see #2548, #2887
29055                     //
29056                     // IMPORTANT:
29057                     // For this to work, getMatches must be called on relations before ways.
29058                     //
29059                     if (entity.type === 'way') {
29060                         var parents = features.getParents(entity, resolver, geometry);
29061                         if (parents.length === 1 && parents[0].isMultipolygon()) {
29062                             var pkey = iD.Entity.key(parents[0]);
29063                             if (_cache[pkey] && _cache[pkey].matches) {
29064                                 matches = _.clone(_cache[pkey].matches);
29065                                 continue;
29066                             }
29067                         }
29068                     }
29069                 }
29070
29071                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
29072                     matches[_keys[i]] = hasMatch = true;
29073                 }
29074             }
29075             _cache[ent].matches = matches;
29076         }
29077
29078         return _cache[ent].matches;
29079     };
29080
29081     features.getParents = function(entity, resolver, geometry) {
29082         if (geometry === 'point') return [];
29083
29084         var ent = iD.Entity.key(entity);
29085         if (!_cache[ent]) {
29086             _cache[ent] = {};
29087         }
29088
29089         if (!_cache[ent].parents) {
29090             var parents = [];
29091             if (geometry === 'vertex') {
29092                 parents = resolver.parentWays(entity);
29093             } else {   // 'line', 'area', 'relation'
29094                 parents = resolver.parentRelations(entity);
29095             }
29096             _cache[ent].parents = parents;
29097         }
29098         return _cache[ent].parents;
29099     };
29100
29101     features.isHiddenFeature = function(entity, resolver, geometry) {
29102         if (!_hidden.length) return false;
29103         if (!entity.version) return false;
29104
29105         var matches = features.getMatches(entity, resolver, geometry);
29106
29107         for (var i = 0; i < _hidden.length; i++) {
29108             if (matches[_hidden[i]]) return true;
29109         }
29110         return false;
29111     };
29112
29113     features.isHiddenChild = function(entity, resolver, geometry) {
29114         if (!_hidden.length) return false;
29115         if (!entity.version || geometry === 'point') return false;
29116
29117         var parents = features.getParents(entity, resolver, geometry);
29118         if (!parents.length) return false;
29119
29120         for (var i = 0; i < parents.length; i++) {
29121             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
29122                 return false;
29123             }
29124         }
29125         return true;
29126     };
29127
29128     features.hasHiddenConnections = function(entity, resolver) {
29129         if (!_hidden.length) return false;
29130         var childNodes, connections;
29131
29132         if (entity.type === 'midpoint') {
29133             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
29134             connections = [];
29135         } else {
29136             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
29137             connections = features.getParents(entity, resolver, entity.geometry(resolver));
29138         }
29139
29140         // gather ways connected to child nodes..
29141         connections = _.reduce(childNodes, function(result, e) {
29142             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
29143         }, connections);
29144
29145         return connections.length ? _.any(connections, function(e) {
29146             return features.isHidden(e, resolver, e.geometry(resolver));
29147         }) : false;
29148     };
29149
29150     features.isHidden = function(entity, resolver, geometry) {
29151         if (!_hidden.length) return false;
29152         if (!entity.version) return false;
29153
29154         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
29155         return fn(entity, resolver, geometry);
29156     };
29157
29158     features.filter = function(d, resolver) {
29159         if (!_hidden.length) return d;
29160
29161         var result = [];
29162         for (var i = 0; i < d.length; i++) {
29163             var entity = d[i];
29164             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
29165                 result.push(entity);
29166             }
29167         }
29168         return result;
29169     };
29170
29171     return d3.rebind(features, dispatch, 'on');
29172 };
29173 iD.Map = function(context) {
29174     var dimensions = [1, 1],
29175         dispatch = d3.dispatch('move', 'drawn'),
29176         projection = context.projection,
29177         zoom = d3.behavior.zoom()
29178             .translate(projection.translate())
29179             .scale(projection.scale() * 2 * Math.PI)
29180             .scaleExtent([1024, 256 * Math.pow(2, 24)])
29181             .on('zoom', zoomPan),
29182         dblclickEnabled = true,
29183         redrawEnabled = true,
29184         transformStart,
29185         transformed = false,
29186         minzoom = 0,
29187         drawLayers = iD.svg.Layers(projection, context),
29188         drawPoints = iD.svg.Points(projection, context),
29189         drawVertices = iD.svg.Vertices(projection, context),
29190         drawLines = iD.svg.Lines(projection),
29191         drawAreas = iD.svg.Areas(projection),
29192         drawMidpoints = iD.svg.Midpoints(projection, context),
29193         drawLabels = iD.svg.Labels(projection, context),
29194         supersurface,
29195         wrapper,
29196         surface,
29197         mouse,
29198         mousemove;
29199
29200     function map(selection) {
29201         context.history()
29202             .on('change.map', redraw);
29203         context.background()
29204             .on('change.map', redraw);
29205         context.features()
29206             .on('redraw.map', redraw);
29207
29208         selection
29209             .on('dblclick.map', dblClick)
29210             .call(zoom);
29211
29212         supersurface = selection.append('div')
29213             .attr('id', 'supersurface')
29214             .call(iD.util.setTransform, 0, 0);
29215
29216         // Need a wrapper div because Opera can't cope with an absolutely positioned
29217         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29218         wrapper = supersurface
29219             .append('div')
29220             .attr('class', 'layer layer-data');
29221
29222         map.surface = surface = wrapper
29223             .call(drawLayers)
29224             .selectAll('.surface')
29225             .attr('id', 'surface');
29226
29227         surface
29228             .on('mousedown.zoom', function() {
29229                 if (d3.event.button === 2) {
29230                     d3.event.stopPropagation();
29231                 }
29232             }, true)
29233             .on('mouseup.zoom', function() {
29234                 if (resetTransform()) redraw();
29235             })
29236             .on('mousemove.map', function() {
29237                 mousemove = d3.event;
29238             })
29239             .on('mouseover.vertices', function() {
29240                 if (map.editable() && !transformed) {
29241                     var hover = d3.event.target.__data__;
29242                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29243                     dispatch.drawn({full: false});
29244                 }
29245             })
29246             .on('mouseout.vertices', function() {
29247                 if (map.editable() && !transformed) {
29248                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29249                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29250                     dispatch.drawn({full: false});
29251                 }
29252             });
29253
29254
29255         supersurface
29256             .call(context.background());
29257
29258
29259         context.on('enter.map', function() {
29260             if (map.editable() && !transformed) {
29261                 var all = context.intersects(map.extent()),
29262                     filter = d3.functor(true),
29263                     graph = context.graph();
29264
29265                 all = context.features().filter(all, graph);
29266                 surface
29267                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
29268                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
29269                 dispatch.drawn({full: false});
29270             }
29271         });
29272
29273         map.dimensions(selection.dimensions());
29274
29275         drawLabels.supersurface(supersurface);
29276     }
29277
29278     function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29279
29280     function drawVector(difference, extent) {
29281         var graph = context.graph(),
29282             features = context.features(),
29283             all = context.intersects(map.extent()),
29284             data, filter;
29285
29286         if (difference) {
29287             var complete = difference.complete(map.extent());
29288             data = _.compact(_.values(complete));
29289             filter = function(d) { return d.id in complete; };
29290             features.clear(data);
29291
29292         } else {
29293             // force a full redraw if gatherStats detects that a feature
29294             // should be auto-hidden (e.g. points or buildings)..
29295             if (features.gatherStats(all, graph, dimensions)) {
29296                 extent = undefined;
29297             }
29298
29299             if (extent) {
29300                 data = context.intersects(map.extent().intersection(extent));
29301                 var set = d3.set(_.pluck(data, 'id'));
29302                 filter = function(d) { return set.has(d.id); };
29303
29304             } else {
29305                 data = all;
29306                 filter = d3.functor(true);
29307             }
29308         }
29309
29310         data = features.filter(data, graph);
29311
29312         surface
29313             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
29314             .call(drawLines, graph, data, filter)
29315             .call(drawAreas, graph, data, filter)
29316             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
29317             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
29318             .call(drawPoints, graph, data, filter);
29319
29320         dispatch.drawn({full: true});
29321     }
29322
29323     function editOff() {
29324         context.features().resetStats();
29325         surface.selectAll('.layer-osm *').remove();
29326         dispatch.drawn({full: true});
29327     }
29328
29329     function dblClick() {
29330         if (!dblclickEnabled) {
29331             d3.event.preventDefault();
29332             d3.event.stopImmediatePropagation();
29333         }
29334     }
29335
29336     function zoomPan() {
29337         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29338             surface.interrupt();
29339             iD.ui.flash(context.container())
29340                 .select('.content')
29341                 .text(t('cannot_zoom'));
29342             setZoom(context.minEditableZoom(), true);
29343             queueRedraw();
29344             dispatch.move(map);
29345             return;
29346         }
29347
29348         projection
29349             .translate(d3.event.translate)
29350             .scale(d3.event.scale / (2 * Math.PI));
29351
29352         var scale = d3.event.scale / transformStart[0],
29353             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29354             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29355
29356         transformed = true;
29357         iD.util.setTransform(supersurface, tX, tY, scale);
29358         queueRedraw();
29359
29360         dispatch.move(map);
29361     }
29362
29363     function resetTransform() {
29364         if (!transformed) return false;
29365
29366         surface.selectAll('.radial-menu').interrupt().remove();
29367         iD.util.setTransform(supersurface, 0, 0);
29368         transformed = false;
29369         return true;
29370     }
29371
29372     function redraw(difference, extent) {
29373         if (!surface || !redrawEnabled) return;
29374
29375         clearTimeout(timeoutId);
29376
29377         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29378         // It would result in artifacts where differenced entities are redrawn with
29379         // one transform and unchanged entities with another.
29380         if (resetTransform()) {
29381             difference = extent = undefined;
29382         }
29383
29384         var zoom = String(~~map.zoom());
29385         if (surface.attr('data-zoom') !== zoom) {
29386             surface.attr('data-zoom', zoom)
29387                 .classed('low-zoom', zoom <= 16);
29388         }
29389
29390         if (!difference) {
29391             supersurface.call(context.background());
29392         }
29393
29394         // OSM
29395         if (map.editable()) {
29396             context.loadTiles(projection, dimensions);
29397             drawVector(difference, extent);
29398         } else {
29399             editOff();
29400         }
29401
29402         wrapper
29403             .call(drawLayers);
29404
29405         transformStart = [
29406             projection.scale() * 2 * Math.PI,
29407             projection.translate().slice()];
29408
29409         return map;
29410     }
29411
29412     var timeoutId;
29413     function queueRedraw() {
29414         clearTimeout(timeoutId);
29415         timeoutId = setTimeout(function() { redraw(); }, 300);
29416     }
29417
29418     function pointLocation(p) {
29419         var translate = projection.translate(),
29420             scale = projection.scale() * 2 * Math.PI;
29421         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29422     }
29423
29424     function locationPoint(l) {
29425         var translate = projection.translate(),
29426             scale = projection.scale() * 2 * Math.PI;
29427         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29428     }
29429
29430     map.mouse = function() {
29431         var e = mousemove || d3.event, s;
29432         while ((s = e.sourceEvent)) e = s;
29433         return mouse(e);
29434     };
29435
29436     map.mouseCoordinates = function() {
29437         return projection.invert(map.mouse());
29438     };
29439
29440     map.dblclickEnable = function(_) {
29441         if (!arguments.length) return dblclickEnabled;
29442         dblclickEnabled = _;
29443         return map;
29444     };
29445
29446     map.redrawEnable = function(_) {
29447         if (!arguments.length) return redrawEnabled;
29448         redrawEnabled = _;
29449         return map;
29450     };
29451
29452     function interpolateZoom(_) {
29453         var k = projection.scale(),
29454             t = projection.translate();
29455
29456         surface.node().__chart__ = {
29457             x: t[0],
29458             y: t[1],
29459             k: k * 2 * Math.PI
29460         };
29461
29462         setZoom(_);
29463         projection.scale(k).translate(t);  // undo setZoom projection changes
29464
29465         zoom.event(surface.transition());
29466     }
29467
29468     function setZoom(_, force) {
29469         if (_ === map.zoom() && !force)
29470             return false;
29471         var scale = 256 * Math.pow(2, _),
29472             center = pxCenter(),
29473             l = pointLocation(center);
29474         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29475         projection.scale(scale / (2 * Math.PI));
29476         zoom.scale(scale);
29477         var t = projection.translate();
29478         l = locationPoint(l);
29479         t[0] += center[0] - l[0];
29480         t[1] += center[1] - l[1];
29481         projection.translate(t);
29482         zoom.translate(projection.translate());
29483         return true;
29484     }
29485
29486     function setCenter(_) {
29487         var c = map.center();
29488         if (_[0] === c[0] && _[1] === c[1])
29489             return false;
29490         var t = projection.translate(),
29491             pxC = pxCenter(),
29492             ll = projection(_);
29493         projection.translate([
29494             t[0] - ll[0] + pxC[0],
29495             t[1] - ll[1] + pxC[1]]);
29496         zoom.translate(projection.translate());
29497         return true;
29498     }
29499
29500     map.pan = function(d) {
29501         var t = projection.translate();
29502         t[0] += d[0];
29503         t[1] += d[1];
29504         projection.translate(t);
29505         zoom.translate(projection.translate());
29506         dispatch.move(map);
29507         return redraw();
29508     };
29509
29510     map.dimensions = function(_) {
29511         if (!arguments.length) return dimensions;
29512         var center = map.center();
29513         dimensions = _;
29514         drawLayers.dimensions(dimensions);
29515         context.background().dimensions(dimensions);
29516         projection.clipExtent([[0, 0], dimensions]);
29517         mouse = iD.util.fastMouse(supersurface.node());
29518         setCenter(center);
29519         return redraw();
29520     };
29521
29522     function zoomIn(integer) {
29523       interpolateZoom(~~map.zoom() + integer);
29524     }
29525
29526     function zoomOut(integer) {
29527       interpolateZoom(~~map.zoom() - integer);
29528     }
29529
29530     map.zoomIn = function() { zoomIn(1); };
29531     map.zoomInFurther = function() { zoomIn(4); };
29532
29533     map.zoomOut = function() { zoomOut(1); };
29534     map.zoomOutFurther = function() { zoomOut(4); };
29535
29536     map.center = function(loc) {
29537         if (!arguments.length) {
29538             return projection.invert(pxCenter());
29539         }
29540
29541         if (setCenter(loc)) {
29542             dispatch.move(map);
29543         }
29544
29545         return redraw();
29546     };
29547
29548     map.zoom = function(z) {
29549         if (!arguments.length) {
29550             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29551         }
29552
29553         if (z < minzoom) {
29554             surface.interrupt();
29555             iD.ui.flash(context.container())
29556                 .select('.content')
29557                 .text(t('cannot_zoom'));
29558             z = context.minEditableZoom();
29559         }
29560
29561         if (setZoom(z)) {
29562             dispatch.move(map);
29563         }
29564
29565         return redraw();
29566     };
29567
29568     map.zoomTo = function(entity, zoomLimits) {
29569         var extent = entity.extent(context.graph());
29570         if (!isFinite(extent.area())) return;
29571
29572         var zoom = map.trimmedExtentZoom(extent);
29573         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29574         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29575     };
29576
29577     map.centerZoom = function(loc, z) {
29578         var centered = setCenter(loc),
29579             zoomed   = setZoom(z);
29580
29581         if (centered || zoomed) {
29582             dispatch.move(map);
29583         }
29584
29585         return redraw();
29586     };
29587
29588     map.centerEase = function(loc) {
29589         var from = map.center().slice(),
29590             t = 0,
29591             stop;
29592
29593         surface.one('mousedown.ease', function() {
29594             stop = true;
29595         });
29596
29597         d3.timer(function() {
29598             if (stop) return true;
29599             map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29600             return t === 10;
29601         }, 20);
29602         return map;
29603     };
29604
29605     map.extent = function(_) {
29606         if (!arguments.length) {
29607             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29608                                  projection.invert([dimensions[0], 0]));
29609         } else {
29610             var extent = iD.geo.Extent(_);
29611             map.centerZoom(extent.center(), map.extentZoom(extent));
29612         }
29613     };
29614
29615     map.trimmedExtent = function(_) {
29616         if (!arguments.length) {
29617             var headerY = 60, footerY = 30, pad = 10;
29618             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29619                     projection.invert([dimensions[0] - pad, headerY + pad]));
29620         } else {
29621             var extent = iD.geo.Extent(_);
29622             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29623         }
29624     };
29625
29626     function calcZoom(extent, dim) {
29627         var tl = projection([extent[0][0], extent[1][1]]),
29628             br = projection([extent[1][0], extent[0][1]]);
29629
29630         // Calculate maximum zoom that fits extent
29631         var hFactor = (br[0] - tl[0]) / dim[0],
29632             vFactor = (br[1] - tl[1]) / dim[1],
29633             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29634             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29635             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29636
29637         return newZoom;
29638     }
29639
29640     map.extentZoom = function(_) {
29641         return calcZoom(iD.geo.Extent(_), dimensions);
29642     };
29643
29644     map.trimmedExtentZoom = function(_) {
29645         var trimY = 120, trimX = 40,
29646             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29647         return calcZoom(iD.geo.Extent(_), trimmed);
29648     };
29649
29650     map.editable = function() {
29651         return map.zoom() >= context.minEditableZoom();
29652     };
29653
29654     map.minzoom = function(_) {
29655         if (!arguments.length) return minzoom;
29656         minzoom = _;
29657         return map;
29658     };
29659
29660     map.layers = drawLayers;
29661
29662     return d3.rebind(map, dispatch, 'on');
29663 };
29664 iD.TileLayer = function() {
29665     var tileSize = 256,
29666         tile = d3.geo.tile(),
29667         projection,
29668         cache = {},
29669         tileOrigin,
29670         z,
29671         transformProp = iD.util.prefixCSSProperty('Transform'),
29672         source = d3.functor('');
29673
29674     function tileSizeAtZoom(d, z) {
29675         return (tileSize * Math.pow(2, z - d[2])) / tileSize;
29676     }
29677
29678     function atZoom(t, distance) {
29679         var power = Math.pow(2, distance);
29680         return [
29681             Math.floor(t[0] * power),
29682             Math.floor(t[1] * power),
29683             t[2] + distance];
29684     }
29685
29686     function lookUp(d) {
29687         for (var up = -1; up > -d[2]; up--) {
29688             var tile = atZoom(d, up);
29689             if (cache[source.url(tile)] !== false) {
29690                 return tile;
29691             }
29692         }
29693     }
29694
29695     function uniqueBy(a, n) {
29696         var o = [], seen = {};
29697         for (var i = 0; i < a.length; i++) {
29698             if (seen[a[i][n]] === undefined) {
29699                 o.push(a[i]);
29700                 seen[a[i][n]] = true;
29701             }
29702         }
29703         return o;
29704     }
29705
29706     function addSource(d) {
29707         d.push(source.url(d));
29708         return d;
29709     }
29710
29711     // Update tiles based on current state of `projection`.
29712     function background(selection) {
29713         tile.scale(projection.scale() * 2 * Math.PI)
29714             .translate(projection.translate());
29715
29716         tileOrigin = [
29717             projection.scale() * Math.PI - projection.translate()[0],
29718             projection.scale() * Math.PI - projection.translate()[1]];
29719
29720         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29721
29722         render(selection);
29723     }
29724
29725     // Derive the tiles onscreen, remove those offscreen and position them.
29726     // Important that this part not depend on `projection` because it's
29727     // rentered when tiles load/error (see #644).
29728     function render(selection) {
29729         var requests = [];
29730
29731         if (source.validZoom(z)) {
29732             tile().forEach(function(d) {
29733                 addSource(d);
29734                 if (d[3] === '') return;
29735                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29736                 requests.push(d);
29737                 if (cache[d[3]] === false && lookUp(d)) {
29738                     requests.push(addSource(lookUp(d)));
29739                 }
29740             });
29741
29742             requests = uniqueBy(requests, 3).filter(function(r) {
29743                 // don't re-request tiles which have failed in the past
29744                 return cache[r[3]] !== false;
29745             });
29746         }
29747
29748         var pixelOffset = [
29749             source.offset()[0] * Math.pow(2, z),
29750             source.offset()[1] * Math.pow(2, z)
29751         ];
29752
29753         function load(d) {
29754             cache[d[3]] = true;
29755             d3.select(this)
29756                 .on('error', null)
29757                 .on('load', null)
29758                 .classed('tile-loaded', true);
29759             render(selection);
29760         }
29761
29762         function error(d) {
29763             cache[d[3]] = false;
29764             d3.select(this)
29765                 .on('error', null)
29766                 .on('load', null)
29767                 .remove();
29768             render(selection);
29769         }
29770
29771         function imageTransform(d) {
29772             var _ts = tileSize * Math.pow(2, z - d[2]);
29773             var scale = tileSizeAtZoom(d, z);
29774             return 'translate(' +
29775                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29776                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29777                 'scale(' + scale + ',' + scale + ')';
29778         }
29779
29780         var image = selection
29781             .selectAll('img')
29782             .data(requests, function(d) { return d[3]; });
29783
29784         image.exit()
29785             .style(transformProp, imageTransform)
29786             .classed('tile-removing', true)
29787             .each(function() {
29788                 var tile = d3.select(this);
29789                 window.setTimeout(function() {
29790                     if (tile.classed('tile-removing')) {
29791                         tile.remove();
29792                     }
29793                 }, 300);
29794             });
29795
29796         image.enter().append('img')
29797             .attr('class', 'tile')
29798             .attr('src', function(d) { return d[3]; })
29799             .on('error', error)
29800             .on('load', load);
29801
29802         image
29803             .style(transformProp, imageTransform)
29804             .classed('tile-removing', false);
29805     }
29806
29807     background.projection = function(_) {
29808         if (!arguments.length) return projection;
29809         projection = _;
29810         return background;
29811     };
29812
29813     background.dimensions = function(_) {
29814         if (!arguments.length) return tile.size();
29815         tile.size(_);
29816         return background;
29817     };
29818
29819     background.source = function(_) {
29820         if (!arguments.length) return source;
29821         source = _;
29822         cache = {};
29823         tile.scaleExtent(source.scaleExtent);
29824         return background;
29825     };
29826
29827     return background;
29828 };
29829 iD.svg = {
29830     PointTransform: function(projection) {
29831         return function(entity) {
29832             // http://jsperf.com/short-array-join
29833             var pt = projection(entity.loc);
29834             return 'translate(' + pt[0] + ',' + pt[1] + ')';
29835         };
29836     },
29837
29838     Path: function(projection, graph, polygon) {
29839         var cache = {},
29840             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29841             project = projection.stream,
29842             path = d3.geo.path()
29843                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29844
29845         return function(entity) {
29846             if (entity.id in cache) {
29847                 return cache[entity.id];
29848             } else {
29849                 return cache[entity.id] = path(entity.asGeoJSON(graph));
29850             }
29851         };
29852     },
29853
29854     OneWaySegments: function(projection, graph, dt) {
29855         return function(entity) {
29856             var a,
29857                 b,
29858                 i = 0,
29859                 offset = dt,
29860                 segments = [],
29861                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29862                 coordinates = graph.childNodes(entity).map(function(n) {
29863                     return n.loc;
29864                 });
29865
29866             if (entity.tags.oneway === '-1') coordinates.reverse();
29867
29868             d3.geo.stream({
29869                 type: 'LineString',
29870                 coordinates: coordinates
29871             }, projection.stream(clip({
29872                 lineStart: function() {},
29873                 lineEnd: function() {
29874                     a = null;
29875                 },
29876                 point: function(x, y) {
29877                     b = [x, y];
29878
29879                     if (a) {
29880                         var span = iD.geo.euclideanDistance(a, b) - offset;
29881
29882                         if (span >= 0) {
29883                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
29884                                 dx = dt * Math.cos(angle),
29885                                 dy = dt * Math.sin(angle),
29886                                 p = [a[0] + offset * Math.cos(angle),
29887                                      a[1] + offset * Math.sin(angle)];
29888
29889                             var segment = 'M' + a[0] + ',' + a[1] +
29890                                           'L' + p[0] + ',' + p[1];
29891
29892                             for (span -= dt; span >= 0; span -= dt) {
29893                                 p[0] += dx;
29894                                 p[1] += dy;
29895                                 segment += 'L' + p[0] + ',' + p[1];
29896                             }
29897
29898                             segment += 'L' + b[0] + ',' + b[1];
29899                             segments.push({id: entity.id, index: i, d: segment});
29900                         }
29901
29902                         offset = -span;
29903                         i++;
29904                     }
29905
29906                     a = b;
29907                 }
29908             })));
29909
29910             return segments;
29911         };
29912     },
29913
29914     RelationMemberTags: function(graph) {
29915         return function(entity) {
29916             var tags = entity.tags;
29917             graph.parentRelations(entity).forEach(function(relation) {
29918                 var type = relation.tags.type;
29919                 if (type === 'multipolygon' || type === 'boundary') {
29920                     tags = _.extend({}, relation.tags, tags);
29921                 }
29922             });
29923             return tags;
29924         };
29925     }
29926 };
29927 iD.svg.Areas = function(projection) {
29928     // Patterns only work in Firefox when set directly on element.
29929     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
29930     var patterns = {
29931         wetland: 'wetland',
29932         beach: 'beach',
29933         scrub: 'scrub',
29934         construction: 'construction',
29935         military: 'construction',
29936         cemetery: 'cemetery',
29937         grave_yard: 'cemetery',
29938         meadow: 'meadow',
29939         farm: 'farmland',
29940         farmland: 'farmland',
29941         orchard: 'orchard'
29942     };
29943
29944     var patternKeys = ['landuse', 'natural', 'amenity'];
29945
29946     function setPattern(d) {
29947         for (var i = 0; i < patternKeys.length; i++) {
29948             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
29949                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
29950                 return;
29951             }
29952         }
29953         this.style.fill = this.style.stroke = '';
29954     }
29955
29956     return function drawAreas(surface, graph, entities, filter) {
29957         var path = iD.svg.Path(projection, graph, true),
29958             areas = {},
29959             multipolygon;
29960
29961         for (var i = 0; i < entities.length; i++) {
29962             var entity = entities[i];
29963             if (entity.geometry(graph) !== 'area') continue;
29964
29965             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
29966             if (multipolygon) {
29967                 areas[multipolygon.id] = {
29968                     entity: multipolygon.mergeTags(entity.tags),
29969                     area: Math.abs(entity.area(graph))
29970                 };
29971             } else if (!areas[entity.id]) {
29972                 areas[entity.id] = {
29973                     entity: entity,
29974                     area: Math.abs(entity.area(graph))
29975                 };
29976             }
29977         }
29978
29979         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
29980         areas.sort(function areaSort(a, b) { return b.area - a.area; });
29981         areas = _.pluck(areas, 'entity');
29982
29983         var strokes = areas.filter(function(area) {
29984             return area.type === 'way';
29985         });
29986
29987         var data = {
29988             clip: areas,
29989             shadow: strokes,
29990             stroke: strokes,
29991             fill: areas
29992         };
29993
29994         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
29995            .filter(filter)
29996            .data(data.clip, iD.Entity.key);
29997
29998         clipPaths.enter()
29999            .append('clipPath')
30000            .attr('class', 'clipPath')
30001            .attr('id', function(entity) { return entity.id + '-clippath'; })
30002            .append('path');
30003
30004         clipPaths.selectAll('path')
30005            .attr('d', path);
30006
30007         clipPaths.exit()
30008            .remove();
30009
30010         var areagroup = surface
30011             .selectAll('.layer-areas')
30012             .selectAll('g.areagroup')
30013             .data(['fill', 'shadow', 'stroke']);
30014
30015         areagroup.enter()
30016             .append('g')
30017             .attr('class', function(d) { return 'layer areagroup area-' + d; });
30018
30019         var paths = areagroup
30020             .selectAll('path')
30021             .filter(filter)
30022             .data(function(layer) { return data[layer]; }, iD.Entity.key);
30023
30024         // Remove exiting areas first, so they aren't included in the `fills`
30025         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30026         paths.exit()
30027             .remove();
30028
30029         var fills = surface.selectAll('.area-fill path.area')[0];
30030
30031         var bisect = d3.bisector(function(node) {
30032             return -node.__data__.area(graph);
30033         }).left;
30034
30035         function sortedByArea(entity) {
30036             if (this.__data__ === 'fill') {
30037                 return fills[bisect(fills, -entity.area(graph))];
30038             }
30039         }
30040
30041         paths.enter()
30042             .insert('path', sortedByArea)
30043             .each(function(entity) {
30044                 var layer = this.parentNode.__data__;
30045
30046                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30047
30048                 if (layer === 'fill') {
30049                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30050                     setPattern.apply(this, arguments);
30051                 }
30052             })
30053             .call(iD.svg.TagClasses());
30054
30055         paths
30056             .attr('d', path);
30057     };
30058 };
30059 /*
30060     A standalone SVG element that contains only a `defs` sub-element. To be
30061     used once globally, since defs IDs must be unique within a document.
30062 */
30063 iD.svg.Defs = function(context) {
30064
30065     function SVGSpriteDefinition(id, href) {
30066         return function(defs) {
30067             d3.xml(href, 'image/svg+xml', function(err, svg) {
30068                 if (err) return;
30069                 defs.node().appendChild(
30070                     d3.select(svg.documentElement).attr('id', id).node()
30071                 );
30072             });
30073         };
30074     }
30075
30076     return function drawDefs(selection) {
30077         var defs = selection.append('defs');
30078
30079         // marker
30080         defs.append('marker')
30081             .attr({
30082                 id: 'oneway-marker',
30083                 viewBox: '0 0 10 10',
30084                 refY: 2.5,
30085                 refX: 5,
30086                 markerWidth: 2,
30087                 markerHeight: 2,
30088                 markerUnits: 'strokeWidth',
30089                 orient: 'auto'
30090             })
30091             .append('path')
30092             .attr('class', 'oneway')
30093             .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')
30094             .attr('stroke', 'none')
30095             .attr('fill', '#000')
30096             .attr('opacity', '0.5');
30097
30098         // patterns
30099         var patterns = defs.selectAll('pattern')
30100             .data([
30101                 // pattern name, pattern image name
30102                 ['wetland', 'wetland'],
30103                 ['construction', 'construction'],
30104                 ['cemetery', 'cemetery'],
30105                 ['orchard', 'orchard'],
30106                 ['farmland', 'farmland'],
30107                 ['beach', 'dots'],
30108                 ['scrub', 'dots'],
30109                 ['meadow', 'dots']
30110             ])
30111             .enter()
30112             .append('pattern')
30113             .attr({
30114                 id: function (d) {
30115                     return 'pattern-' + d[0];
30116                 },
30117                 width: 32,
30118                 height: 32,
30119                 patternUnits: 'userSpaceOnUse'
30120             });
30121
30122         patterns.append('rect')
30123             .attr({
30124                 x: 0,
30125                 y: 0,
30126                 width: 32,
30127                 height: 32,
30128                 'class': function (d) {
30129                     return 'pattern-color-' + d[0];
30130                 }
30131             });
30132
30133         patterns.append('image')
30134             .attr({
30135                 x: 0,
30136                 y: 0,
30137                 width: 32,
30138                 height: 32
30139             })
30140             .attr('xlink:href', function (d) {
30141                 return context.imagePath('pattern/' + d[1] + '.png');
30142             });
30143
30144         // clip paths
30145         defs.selectAll()
30146             .data([12, 18, 20, 32, 45])
30147             .enter().append('clipPath')
30148             .attr('id', function (d) {
30149                 return 'clip-square-' + d;
30150             })
30151             .append('rect')
30152             .attr('x', 0)
30153             .attr('y', 0)
30154             .attr('width', function (d) {
30155                 return d;
30156             })
30157             .attr('height', function (d) {
30158                 return d;
30159             });
30160
30161         defs.call(SVGSpriteDefinition(
30162             'iD-sprite',
30163             context.imagePath('iD-sprite.svg')));
30164
30165         defs.call(SVGSpriteDefinition(
30166             'maki-sprite',
30167             context.imagePath('maki-sprite.svg')));
30168     };
30169 };
30170 iD.svg.Gpx = function(projection, context) {
30171     var showLabels = true,
30172         layer;
30173
30174     function init() {
30175         if (iD.svg.Gpx.initialized) return;  // run once
30176
30177         iD.svg.Gpx.geojson = {};
30178         iD.svg.Gpx.enabled = true;
30179
30180         function over() {
30181             d3.event.stopPropagation();
30182             d3.event.preventDefault();
30183             d3.event.dataTransfer.dropEffect = 'copy';
30184         }
30185
30186         d3.select('body')
30187             .attr('dropzone', 'copy')
30188             .on('drop.localgpx', function() {
30189                 d3.event.stopPropagation();
30190                 d3.event.preventDefault();
30191                 if (!iD.detect().filedrop) return;
30192                 drawGpx.files(d3.event.dataTransfer.files);
30193             })
30194             .on('dragenter.localgpx', over)
30195             .on('dragexit.localgpx', over)
30196             .on('dragover.localgpx', over);
30197
30198         iD.svg.Gpx.initialized = true;
30199     }
30200
30201
30202     function drawGpx(surface) {
30203         var geojson = iD.svg.Gpx.geojson,
30204             enabled = iD.svg.Gpx.enabled;
30205
30206         layer = surface.selectAll('.layer-gpx')
30207             .data(enabled ? [0] : []);
30208
30209         layer.enter()
30210             .append('g')
30211             .attr('class', 'layer-gpx');
30212
30213         layer.exit()
30214             .remove();
30215
30216
30217         var paths = layer
30218             .selectAll('path')
30219             .data([geojson]);
30220
30221         paths.enter()
30222             .append('path')
30223             .attr('class', 'gpx');
30224
30225         paths.exit()
30226             .remove();
30227
30228         var path = d3.geo.path()
30229             .projection(projection);
30230
30231         paths
30232             .attr('d', path);
30233
30234
30235         var labels = layer.selectAll('text')
30236             .data(showLabels && geojson.features ? geojson.features : []);
30237
30238         labels.enter()
30239             .append('text')
30240             .attr('class', 'gpx')
30241             .text(function(d) {
30242                 return d.properties.desc || d.properties.name;
30243             });
30244
30245         labels.exit()
30246             .remove();
30247
30248         labels
30249             .attr('x', function(d) {
30250                 var centroid = path.centroid(d);
30251                 return centroid[0] + 7;
30252             })
30253             .attr('y', function(d) {
30254                 var centroid = path.centroid(d);
30255                 return centroid[1];
30256             });
30257
30258     }
30259
30260     function toDom(x) {
30261         return (new DOMParser()).parseFromString(x, 'text/xml');
30262     }
30263
30264     function redraw() {
30265         context.pan([0,0]);
30266     }
30267
30268     drawGpx.showLabels = function(_) {
30269         if (!arguments.length) return showLabels;
30270         showLabels = _;
30271         return this;
30272     };
30273
30274     drawGpx.enabled = function(_) {
30275         if (!arguments.length) return iD.svg.Gpx.enabled;
30276         iD.svg.Gpx.enabled = _;
30277         return this;
30278     };
30279
30280     drawGpx.hasGpx = function() {
30281         var geojson = iD.svg.Gpx.geojson;
30282         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
30283     };
30284
30285     drawGpx.geojson = function(gj) {
30286         if (!arguments.length) return iD.svg.Gpx.geojson;
30287         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
30288         iD.svg.Gpx.geojson = gj;
30289         return this;
30290     };
30291
30292     drawGpx.url = function(url) {
30293         d3.text(url, function(err, data) {
30294             if (!err) {
30295                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
30296                 redraw();
30297             }
30298         });
30299         return this;
30300     };
30301
30302     drawGpx.files = function(fileList) {
30303         var f = fileList[0],
30304             reader = new FileReader();
30305
30306         reader.onload = function(e) {
30307             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
30308             redraw();
30309         };
30310
30311         reader.readAsText(f);
30312         return this;
30313     };
30314
30315     drawGpx.fitZoom = function() {
30316         if (!this.hasGpx()) return this;
30317         var geojson = iD.svg.Gpx.geojson;
30318
30319         var map = context.map(),
30320             viewport = map.trimmedExtent().polygon(),
30321             coords = _.reduce(geojson.features, function(coords, feature) {
30322                 var c = feature.geometry.coordinates;
30323                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
30324             }, []);
30325
30326         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
30327             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
30328             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
30329         }
30330
30331         return this;
30332     };
30333
30334     init();
30335     return drawGpx;
30336 };
30337 iD.svg.Icon = function(name, svgklass, useklass) {
30338     return function drawIcon(selection) {
30339         selection.selectAll('svg')
30340             .data([0])
30341             .enter()
30342             .append('svg')
30343             .attr('class', 'icon ' + (svgklass || ''))
30344             .append('use')
30345             .attr('xlink:href', name)
30346             .attr('class', useklass);
30347     };
30348 };
30349 iD.svg.Labels = function(projection, context) {
30350     var path = d3.geo.path().projection(projection);
30351
30352     // Replace with dict and iterate over entities tags instead?
30353     var label_stack = [
30354         ['line', 'aeroway'],
30355         ['line', 'highway'],
30356         ['line', 'railway'],
30357         ['line', 'waterway'],
30358         ['area', 'aeroway'],
30359         ['area', 'amenity'],
30360         ['area', 'building'],
30361         ['area', 'historic'],
30362         ['area', 'leisure'],
30363         ['area', 'man_made'],
30364         ['area', 'natural'],
30365         ['area', 'shop'],
30366         ['area', 'tourism'],
30367         ['point', 'aeroway'],
30368         ['point', 'amenity'],
30369         ['point', 'building'],
30370         ['point', 'historic'],
30371         ['point', 'leisure'],
30372         ['point', 'man_made'],
30373         ['point', 'natural'],
30374         ['point', 'shop'],
30375         ['point', 'tourism'],
30376         ['line', 'name'],
30377         ['area', 'name'],
30378         ['point', 'name']
30379     ];
30380
30381     var default_size = 12;
30382
30383     var font_sizes = label_stack.map(function(d) {
30384         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30385             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30386         if (m) return parseInt(m[1], 10);
30387
30388         style = iD.util.getStyle('text.' + d[0]);
30389         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30390         if (m) return parseInt(m[1], 10);
30391
30392         return default_size;
30393     });
30394
30395     var iconSize = 18;
30396
30397     var pointOffsets = [
30398         [15, -11, 'start'], // right
30399         [10, -11, 'start'], // unused right now
30400         [-15, -11, 'end']
30401     ];
30402
30403     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30404         75, 20, 80, 15, 95, 10, 90, 5, 95];
30405
30406
30407     var noIcons = ['building', 'landuse', 'natural'];
30408     function blacklisted(preset) {
30409         return _.any(noIcons, function(s) {
30410             return preset.id.indexOf(s) >= 0;
30411         });
30412     }
30413
30414     function get(array, prop) {
30415         return function(d, i) { return array[i][prop]; };
30416     }
30417
30418     var textWidthCache = {};
30419
30420     function textWidth(text, size, elem) {
30421         var c = textWidthCache[size];
30422         if (!c) c = textWidthCache[size] = {};
30423
30424         if (c[text]) {
30425             return c[text];
30426
30427         } else if (elem) {
30428             c[text] = elem.getComputedTextLength();
30429             return c[text];
30430
30431         } else {
30432             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30433             if (str === null) {
30434                 return size / 3 * 2 * text.length;
30435             } else {
30436                 return size / 3 * (2 * text.length + str.length);
30437             }
30438         }
30439     }
30440
30441     function drawLineLabels(group, entities, filter, classes, labels) {
30442         var texts = group.selectAll('text.' + classes)
30443             .filter(filter)
30444             .data(entities, iD.Entity.key);
30445
30446         texts.enter()
30447             .append('text')
30448             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30449             .append('textPath')
30450             .attr('class', 'textpath');
30451
30452
30453         texts.selectAll('.textpath')
30454             .filter(filter)
30455             .data(entities, iD.Entity.key)
30456             .attr({
30457                 'startOffset': '50%',
30458                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30459             })
30460             .text(iD.util.displayName);
30461
30462         texts.exit().remove();
30463     }
30464
30465     function drawLinePaths(group, entities, filter, classes, labels) {
30466         var halos = group.selectAll('path')
30467             .filter(filter)
30468             .data(entities, iD.Entity.key);
30469
30470         halos.enter()
30471             .append('path')
30472             .style('stroke-width', get(labels, 'font-size'))
30473             .attr('id', function(d) { return 'labelpath-' + d.id; })
30474             .attr('class', classes);
30475
30476         halos.attr('d', get(labels, 'lineString'));
30477
30478         halos.exit().remove();
30479     }
30480
30481     function drawPointLabels(group, entities, filter, classes, labels) {
30482         var texts = group.selectAll('text.' + classes)
30483             .filter(filter)
30484             .data(entities, iD.Entity.key);
30485
30486         texts.enter()
30487             .append('text')
30488             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30489
30490         texts.attr('x', get(labels, 'x'))
30491             .attr('y', get(labels, 'y'))
30492             .style('text-anchor', get(labels, 'textAnchor'))
30493             .text(iD.util.displayName)
30494             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30495
30496         texts.exit().remove();
30497         return texts;
30498     }
30499
30500     function drawAreaLabels(group, entities, filter, classes, labels) {
30501         entities = entities.filter(hasText);
30502         labels = labels.filter(hasText);
30503         return drawPointLabels(group, entities, filter, classes, labels);
30504
30505         function hasText(d, i) {
30506             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30507         }
30508     }
30509
30510     function drawAreaIcons(group, entities, filter, classes, labels) {
30511         var icons = group.selectAll('use')
30512             .filter(filter)
30513             .data(entities, iD.Entity.key);
30514
30515         icons.enter()
30516             .append('use')
30517             .attr('class', 'icon areaicon')
30518             .attr('width', '18px')
30519             .attr('height', '18px');
30520
30521         icons.attr('transform', get(labels, 'transform'))
30522             .attr('xlink:href', function(d) {
30523                 var icon = context.presets().match(d, context.graph()).icon;
30524                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
30525             });
30526
30527
30528         icons.exit().remove();
30529     }
30530
30531     function reverse(p) {
30532         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30533         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30534     }
30535
30536     function lineString(nodes) {
30537         return 'M' + nodes.join('L');
30538     }
30539
30540     function subpath(nodes, from, to) {
30541         function segmentLength(i) {
30542             var dx = nodes[i][0] - nodes[i + 1][0];
30543             var dy = nodes[i][1] - nodes[i + 1][1];
30544             return Math.sqrt(dx * dx + dy * dy);
30545         }
30546
30547         var sofar = 0,
30548             start, end, i0, i1;
30549         for (var i = 0; i < nodes.length - 1; i++) {
30550             var current = segmentLength(i);
30551             var portion;
30552             if (!start && sofar + current >= from) {
30553                 portion = (from - sofar) / current;
30554                 start = [
30555                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30556                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30557                 ];
30558                 i0 = i + 1;
30559             }
30560             if (!end && sofar + current >= to) {
30561                 portion = (to - sofar) / current;
30562                 end = [
30563                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30564                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30565                 ];
30566                 i1 = i + 1;
30567             }
30568             sofar += current;
30569
30570         }
30571         var ret = nodes.slice(i0, i1);
30572         ret.unshift(start);
30573         ret.push(end);
30574         return ret;
30575
30576     }
30577
30578     function hideOnMouseover() {
30579         var layers = d3.select(this)
30580             .selectAll('.layer-label, .layer-halo');
30581
30582         layers.selectAll('.proximate')
30583             .classed('proximate', false);
30584
30585         var mouse = context.mouse(),
30586             pad = 50,
30587             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30588             ids = _.pluck(rtree.search(rect), 'id');
30589
30590         if (!ids.length) return;
30591         layers.selectAll('.' + ids.join(', .'))
30592             .classed('proximate', true);
30593     }
30594
30595     var rtree = rbush(),
30596         rectangles = {};
30597
30598     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
30599         var hidePoints = !surface.selectAll('.node.point').node();
30600
30601         var labelable = [], i, k, entity;
30602         for (i = 0; i < label_stack.length; i++) labelable.push([]);
30603
30604         if (fullRedraw) {
30605             rtree.clear();
30606             rectangles = {};
30607         } else {
30608             for (i = 0; i < entities.length; i++) {
30609                 rtree.remove(rectangles[entities[i].id]);
30610             }
30611         }
30612
30613         // Split entities into groups specified by label_stack
30614         for (i = 0; i < entities.length; i++) {
30615             entity = entities[i];
30616             var geometry = entity.geometry(graph);
30617
30618             if (geometry === 'vertex')
30619                 continue;
30620             if (hidePoints && geometry === 'point')
30621                 continue;
30622
30623             var preset = geometry === 'area' && context.presets().match(entity, graph),
30624                 icon = preset && !blacklisted(preset) && preset.icon;
30625
30626             if (!icon && !iD.util.displayName(entity))
30627                 continue;
30628
30629             for (k = 0; k < label_stack.length; k++) {
30630                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30631                     labelable[k].push(entity);
30632                     break;
30633                 }
30634             }
30635         }
30636
30637         var positions = {
30638             point: [],
30639             line: [],
30640             area: []
30641         };
30642
30643         var labelled = {
30644             point: [],
30645             line: [],
30646             area: []
30647         };
30648
30649         // Try and find a valid label for labellable entities
30650         for (k = 0; k < labelable.length; k++) {
30651             var font_size = font_sizes[k];
30652             for (i = 0; i < labelable[k].length; i++) {
30653                 entity = labelable[k][i];
30654                 var name = iD.util.displayName(entity),
30655                     width = name && textWidth(name, font_size),
30656                     p;
30657                 if (entity.geometry(graph) === 'point') {
30658                     p = getPointLabel(entity, width, font_size);
30659                 } else if (entity.geometry(graph) === 'line') {
30660                     p = getLineLabel(entity, width, font_size);
30661                 } else if (entity.geometry(graph) === 'area') {
30662                     p = getAreaLabel(entity, width, font_size);
30663                 }
30664                 if (p) {
30665                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30666                     positions[entity.geometry(graph)].push(p);
30667                     labelled[entity.geometry(graph)].push(entity);
30668                 }
30669             }
30670         }
30671
30672         function getPointLabel(entity, width, height) {
30673             var coord = projection(entity.loc),
30674                 m = 5,  // margin
30675                 offset = pointOffsets[0],
30676                 p = {
30677                     height: height,
30678                     width: width,
30679                     x: coord[0] + offset[0],
30680                     y: coord[1] + offset[1],
30681                     textAnchor: offset[2]
30682                 };
30683             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30684             if (tryInsert(rect, entity.id)) return p;
30685         }
30686
30687
30688         function getLineLabel(entity, width, height) {
30689             var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30690                 length = iD.geo.pathLength(nodes);
30691             if (length < width + 20) return;
30692
30693             for (var i = 0; i < lineOffsets.length; i++) {
30694                 var offset = lineOffsets[i],
30695                     middle = offset / 100 * length,
30696                     start = middle - width/2;
30697                 if (start < 0 || start + width > length) continue;
30698                 var sub = subpath(nodes, start, start + width),
30699                     rev = reverse(sub),
30700                     rect = [
30701                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30702                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30703                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30704                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30705                     ];
30706                 if (rev) sub = sub.reverse();
30707                 if (tryInsert(rect, entity.id)) return {
30708                     'font-size': height + 2,
30709                     lineString: lineString(sub),
30710                     startOffset: offset + '%'
30711                 };
30712             }
30713         }
30714
30715         function getAreaLabel(entity, width, height) {
30716             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30717                 extent = entity.extent(graph),
30718                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30719                 rect;
30720
30721             if (isNaN(centroid[0]) || entitywidth < 20) return;
30722
30723             var iconX = centroid[0] - (iconSize/2),
30724                 iconY = centroid[1] - (iconSize/2),
30725                 textOffset = iconSize + 5;
30726
30727             var p = {
30728                 transform: 'translate(' + iconX + ',' + iconY + ')'
30729             };
30730
30731             if (width && entitywidth >= width + 20) {
30732                 p.x = centroid[0];
30733                 p.y = centroid[1] + textOffset;
30734                 p.textAnchor = 'middle';
30735                 p.height = height;
30736                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30737             } else {
30738                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30739             }
30740
30741             if (tryInsert(rect, entity.id)) return p;
30742
30743         }
30744
30745         function tryInsert(rect, id) {
30746             // Check that label is visible
30747             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30748                 rect[3] > dimensions[1]) return false;
30749             var v = rtree.search(rect).length === 0;
30750             if (v) {
30751                 rect.id = id;
30752                 rtree.insert(rect);
30753                 rectangles[id] = rect;
30754             }
30755             return v;
30756         }
30757
30758         var label = surface.selectAll('.layer-label'),
30759             halo = surface.selectAll('.layer-halo');
30760
30761         // points
30762         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30763         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30764
30765         // lines
30766         drawLinePaths(halo, labelled.line, filter, '', positions.line);
30767         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30768         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30769
30770         // areas
30771         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30772         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30773         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30774     }
30775
30776     drawLabels.supersurface = function(supersurface) {
30777         supersurface
30778             .on('mousemove.hidelabels', hideOnMouseover)
30779             .on('mousedown.hidelabels', function () {
30780                 supersurface.on('mousemove.hidelabels', null);
30781             })
30782             .on('mouseup.hidelabels', function () {
30783                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30784             });
30785     };
30786
30787     return drawLabels;
30788 };
30789 iD.svg.Layers = function(projection, context) {
30790     var svg = d3.select(null),
30791         layers = [
30792             { id: 'osm', layer: iD.svg.Osm(projection, context) },
30793             { id: 'gpx', layer: iD.svg.Gpx(projection, context) },
30794             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context) },
30795             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context) }
30796         ];
30797
30798
30799     function drawLayers(selection) {
30800         svg = selection.selectAll('.surface')
30801             .data([0]);
30802
30803         svg.enter()
30804             .append('svg')
30805             .attr('class', 'surface')
30806             .append('defs');
30807
30808         var groups = svg.selectAll('.data-layer')
30809             .data(layers);
30810
30811         groups.enter()
30812             .append('g')
30813             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
30814
30815         groups
30816             .each(function(d) { d3.select(this).call(d.layer); });
30817
30818         groups.exit()
30819             .remove();
30820     }
30821
30822     drawLayers.all = function() {
30823         return layers;
30824     };
30825
30826     drawLayers.layer = function(id) {
30827         var obj = _.find(layers, 'id', id);
30828         return obj && obj.layer;
30829     };
30830
30831     drawLayers.only = function(what) {
30832         var arr = [].concat(what);
30833         drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr));
30834         return this;
30835     };
30836
30837     drawLayers.remove = function(what) {
30838         var arr = [].concat(what);
30839         arr.forEach(function(id) {
30840             layers = _.reject(layers, 'id', id);
30841         });
30842         return this;
30843     };
30844
30845     drawLayers.add = function(what) {
30846         var arr = [].concat(what);
30847         arr.forEach(function(obj) {
30848             if ('id' in obj && 'layer' in obj) {
30849                 layers.push(obj);
30850             }
30851         });
30852         return this;
30853     };
30854
30855     drawLayers.dimensions = function(_) {
30856         if (!arguments.length) return svg.dimensions();
30857         svg.dimensions(_);
30858         layers.forEach(function(obj) {
30859             if (obj.layer.dimensions) {
30860                 obj.layer.dimensions(_);
30861             }
30862         });
30863         return this;
30864     };
30865
30866
30867     return drawLayers;
30868 };
30869 iD.svg.Lines = function(projection) {
30870
30871     var highway_stack = {
30872         motorway: 0,
30873         motorway_link: 1,
30874         trunk: 2,
30875         trunk_link: 3,
30876         primary: 4,
30877         primary_link: 5,
30878         secondary: 6,
30879         tertiary: 7,
30880         unclassified: 8,
30881         residential: 9,
30882         service: 10,
30883         footway: 11
30884     };
30885
30886     function waystack(a, b) {
30887         var as = 0, bs = 0;
30888
30889         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
30890         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
30891         return as - bs;
30892     }
30893
30894     return function drawLines(surface, graph, entities, filter) {
30895         var ways = [], pathdata = {}, onewaydata = {},
30896             getPath = iD.svg.Path(projection, graph);
30897
30898         for (var i = 0; i < entities.length; i++) {
30899             var entity = entities[i],
30900                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
30901             if (outer) {
30902                 ways.push(entity.mergeTags(outer.tags));
30903             } else if (entity.geometry(graph) === 'line') {
30904                 ways.push(entity);
30905             }
30906         }
30907
30908         ways = ways.filter(getPath);
30909
30910         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
30911
30912         _.forOwn(pathdata, function(v, k) {
30913             onewaydata[k] = _(v)
30914                 .filter(function(d) { return d.isOneWay(); })
30915                 .map(iD.svg.OneWaySegments(projection, graph, 35))
30916                 .flatten()
30917                 .valueOf();
30918         });
30919
30920         var layergroup = surface
30921             .selectAll('.layer-lines')
30922             .selectAll('g.layergroup')
30923             .data(d3.range(-10, 11));
30924
30925         layergroup.enter()
30926             .append('g')
30927             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
30928
30929
30930         var linegroup = layergroup
30931             .selectAll('g.linegroup')
30932             .data(['shadow', 'casing', 'stroke']);
30933
30934         linegroup.enter()
30935             .append('g')
30936             .attr('class', function(d) { return 'layer linegroup line-' + d; });
30937
30938
30939         var lines = linegroup
30940             .selectAll('path')
30941             .filter(filter)
30942             .data(
30943                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
30944                 iD.Entity.key
30945             );
30946
30947         // Optimization: call simple TagClasses only on enter selection. This
30948         // works because iD.Entity.key is defined to include the entity v attribute.
30949         lines.enter()
30950             .append('path')
30951             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
30952             .call(iD.svg.TagClasses());
30953
30954         lines
30955             .sort(waystack)
30956             .attr('d', getPath)
30957             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
30958
30959         lines.exit()
30960             .remove();
30961
30962
30963         var onewaygroup = layergroup
30964             .selectAll('g.onewaygroup')
30965             .data(['oneway']);
30966
30967         onewaygroup.enter()
30968             .append('g')
30969             .attr('class', 'layer onewaygroup');
30970
30971
30972         var oneways = onewaygroup
30973             .selectAll('path')
30974             .filter(filter)
30975             .data(
30976                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
30977                 function(d) { return [d.id, d.index]; }
30978             );
30979
30980         oneways.enter()
30981             .append('path')
30982             .attr('class', 'oneway')
30983             .attr('marker-mid', 'url(#oneway-marker)');
30984
30985         oneways
30986             .attr('d', function(d) { return d.d; });
30987
30988         if (iD.detect().ie) {
30989             oneways.each(function() { this.parentNode.insertBefore(this, this); });
30990         }
30991
30992         oneways.exit()
30993             .remove();
30994
30995     };
30996 };
30997 iD.svg.MapillaryImages = function(projection, context) {
30998     var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000),
30999         minZoom = 12,
31000         layer = d3.select(null),
31001         _mapillary;
31002
31003
31004     function init() {
31005         if (iD.svg.MapillaryImages.initialized) return;  // run once
31006         iD.svg.MapillaryImages.enabled = false;
31007         iD.svg.MapillaryImages.initialized = true;
31008     }
31009
31010     function getMapillary() {
31011         if (iD.services.mapillary && !_mapillary) {
31012             _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
31013         } else if (!iD.services.mapillary && _mapillary) {
31014             _mapillary = null;
31015         }
31016         return _mapillary;
31017     }
31018
31019     function showThumbnail(image) {
31020         var mapillary = getMapillary();
31021         if (!mapillary) return;
31022
31023         var thumb = mapillary.selectedThumbnail(),
31024             posX = projection(image.loc)[0],
31025             width = layer.dimensions()[0],
31026             position = (posX < width / 2) ? 'right' : 'left';
31027
31028         if (thumb) {
31029             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31030                 .classed('selected', function(d) { return d.key === thumb.key; });
31031         }
31032
31033         mapillary.showThumbnail(image.key, position);
31034     }
31035
31036     function hideThumbnail() {
31037         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31038             .classed('selected', false);
31039
31040         var mapillary = getMapillary();
31041         if (mapillary) {
31042             mapillary.hideThumbnail();
31043         }
31044     }
31045
31046     function showLayer() {
31047         editOn();
31048         layer
31049             .style('opacity', 0)
31050             .transition()
31051             .duration(500)
31052             .style('opacity', 1)
31053             .each('end', debouncedRedraw);
31054     }
31055
31056     function hideLayer() {
31057         debouncedRedraw.cancel();
31058         hideThumbnail();
31059         layer
31060             .transition()
31061             .duration(500)
31062             .style('opacity', 0)
31063             .each('end', editOff);
31064     }
31065
31066     function editOn() {
31067         layer.style('display', 'block');
31068     }
31069
31070     function editOff() {
31071         layer.selectAll('.viewfield-group').remove();
31072         layer.style('display', 'none');
31073     }
31074
31075     function transform(d) {
31076         var t = iD.svg.PointTransform(projection)(d);
31077         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
31078         return t;
31079     }
31080
31081     function update() {
31082         var mapillary = getMapillary(),
31083             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
31084
31085         var markers = layer.selectAll('.viewfield-group')
31086             .data(data, function(d) { return d.key; });
31087
31088         // Enter
31089         var enter = markers.enter()
31090             .append('g')
31091             .attr('class', 'viewfield-group');
31092
31093         enter.append('path')
31094             .attr('class', 'viewfield')
31095             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
31096             .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');
31097
31098         enter.append('circle')
31099             .attr('dx', '0')
31100             .attr('dy', '0')
31101             .attr('r', '6');
31102
31103         // Exit
31104         markers.exit()
31105             .remove();
31106
31107         // Update
31108         markers
31109             .attr('transform', transform);
31110     }
31111
31112     function drawImages(selection) {
31113         var enabled = iD.svg.MapillaryImages.enabled,
31114             mapillary = getMapillary();
31115
31116         layer = selection.selectAll('.layer-mapillary-images')
31117             .data(mapillary ? [0] : []);
31118
31119         layer.enter()
31120             .append('g')
31121             .attr('class', 'layer-mapillary-images')
31122             .style('display', enabled ? 'block' : 'none')
31123             .on('click', function() {   // deselect/select
31124                 var mapillary = getMapillary();
31125                 if (!mapillary) return;
31126                 var d = d3.event.target.__data__,
31127                     thumb = mapillary.selectedThumbnail();
31128                 if (thumb && thumb.key === d.key) {
31129                     hideThumbnail();
31130                 } else {
31131                     mapillary.selectedThumbnail(d);
31132                     context.map().centerEase(d.loc);
31133                     showThumbnail(d);
31134                 }
31135             })
31136             .on('mouseover', function() {
31137                 var mapillary = getMapillary();
31138                 if (!mapillary) return;
31139                 showThumbnail(d3.event.target.__data__);
31140             })
31141             .on('mouseout', function() {
31142                 var mapillary = getMapillary();
31143                 if (!mapillary) return;
31144                 var thumb = mapillary.selectedThumbnail();
31145                 if (thumb) {
31146                     showThumbnail(thumb);
31147                 } else {
31148                     hideThumbnail();
31149                 }
31150             });
31151
31152         layer.exit()
31153             .remove();
31154
31155         if (enabled) {
31156             if (mapillary && ~~context.map().zoom() >= minZoom) {
31157                 editOn();
31158                 update();
31159                 mapillary.loadImages(projection, layer.dimensions());
31160             } else {
31161                 editOff();
31162             }
31163         }
31164     }
31165
31166     drawImages.enabled = function(_) {
31167         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
31168         iD.svg.MapillaryImages.enabled = _;
31169         if (iD.svg.MapillaryImages.enabled) {
31170             showLayer();
31171         } else {
31172             hideLayer();
31173         }
31174         return this;
31175     };
31176
31177     drawImages.supported = function() {
31178         return !!getMapillary();
31179     };
31180
31181     drawImages.dimensions = function(_) {
31182         if (!arguments.length) return layer.dimensions();
31183         layer.dimensions(_);
31184         return this;
31185     };
31186
31187     init();
31188     return drawImages;
31189 };
31190 iD.svg.MapillarySigns = function(projection, context) {
31191     var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000),
31192         minZoom = 12,
31193         layer = d3.select(null),
31194         _mapillary;
31195
31196
31197     function init() {
31198         if (iD.svg.MapillarySigns.initialized) return;  // run once
31199         iD.svg.MapillarySigns.enabled = false;
31200         iD.svg.MapillarySigns.initialized = true;
31201     }
31202
31203     function getMapillary() {
31204         if (iD.services.mapillary && !_mapillary) {
31205             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
31206         } else if (!iD.services.mapillary && _mapillary) {
31207             _mapillary = null;
31208         }
31209         return _mapillary;
31210     }
31211
31212     function showThumbnail(image) {
31213         var mapillary = getMapillary();
31214         if (!mapillary) return;
31215
31216         var thumb = mapillary.selectedThumbnail(),
31217             posX = projection(image.loc)[0],
31218             width = layer.dimensions()[0],
31219             position = (posX < width / 2) ? 'right' : 'left';
31220
31221         if (thumb) {
31222             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31223                 .classed('selected', function(d) { return d.key === thumb.key; });
31224         }
31225
31226         mapillary.showThumbnail(image.key, position);
31227     }
31228
31229     function hideThumbnail() {
31230         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31231             .classed('selected', false);
31232
31233         var mapillary = getMapillary();
31234         if (mapillary) {
31235             mapillary.hideThumbnail();
31236         }
31237     }
31238
31239     function showLayer() {
31240         editOn();
31241         debouncedRedraw();
31242     }
31243
31244     function hideLayer() {
31245         debouncedRedraw.cancel();
31246         hideThumbnail();
31247         editOff();
31248     }
31249
31250     function editOn() {
31251         layer.style('display', 'block');
31252     }
31253
31254     function editOff() {
31255         layer.selectAll('.icon-sign').remove();
31256         layer.style('display', 'none');
31257     }
31258
31259     function update() {
31260         var mapillary = getMapillary(),
31261             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
31262
31263         var signs = layer.selectAll('.icon-sign')
31264             .data(data, function(d) { return d.key; });
31265
31266         // Enter
31267         var enter = signs.enter()
31268             .append('foreignObject')
31269             .attr('class', 'icon-sign')
31270             .attr('width', '32px')      // for Firefox
31271             .attr('height', '32px');    // for Firefox
31272
31273         enter
31274             .append('xhtml:body')
31275             .html(mapillary.signHTML);
31276
31277         enter
31278             .on('click', function(d) {   // deselect/select
31279                 var mapillary = getMapillary();
31280                 if (!mapillary) return;
31281                 var thumb = mapillary.selectedThumbnail();
31282                 if (thumb && thumb.key === d.key) {
31283                     hideThumbnail();
31284                 } else {
31285                     mapillary.selectedThumbnail(d);
31286                     context.map().centerEase(d.loc);
31287                     showThumbnail(d);
31288                 }
31289             })
31290             .on('mouseover', showThumbnail)
31291             .on('mouseout', function() {
31292                 var mapillary = getMapillary();
31293                 if (!mapillary) return;
31294                 var thumb = mapillary.selectedThumbnail();
31295                 if (thumb) {
31296                     showThumbnail(thumb);
31297                 } else {
31298                     hideThumbnail();
31299                 }
31300             });
31301
31302         // Exit
31303         signs.exit()
31304             .remove();
31305
31306         // Update
31307         signs
31308             .attr('transform', iD.svg.PointTransform(projection));
31309     }
31310
31311     function drawSigns(selection) {
31312         var enabled = iD.svg.MapillarySigns.enabled,
31313             mapillary = getMapillary();
31314
31315         layer = selection.selectAll('.layer-mapillary-signs')
31316             .data(mapillary ? [0] : []);
31317
31318         layer.enter()
31319             .append('g')
31320             .attr('class', 'layer-mapillary-signs')
31321             .style('display', enabled ? 'block' : 'none')
31322             .attr('transform', 'translate(-16, -16)');  // center signs on loc
31323
31324         layer.exit()
31325             .remove();
31326
31327         if (enabled) {
31328             if (mapillary && ~~context.map().zoom() >= minZoom) {
31329                 editOn();
31330                 update();
31331                 mapillary.loadSigns(context, projection, layer.dimensions());
31332             } else {
31333                 editOff();
31334             }
31335         }
31336     }
31337
31338     drawSigns.enabled = function(_) {
31339         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
31340         iD.svg.MapillarySigns.enabled = _;
31341         if (iD.svg.MapillarySigns.enabled) {
31342             showLayer();
31343         } else {
31344             hideLayer();
31345         }
31346         return this;
31347     };
31348
31349     drawSigns.supported = function() {
31350         var mapillary = getMapillary();
31351         return (mapillary && mapillary.signsSupported());
31352     };
31353
31354     drawSigns.dimensions = function(_) {
31355         if (!arguments.length) return layer.dimensions();
31356         layer.dimensions(_);
31357         return this;
31358     };
31359
31360     init();
31361     return drawSigns;
31362 };
31363 iD.svg.Midpoints = function(projection, context) {
31364     return function drawMidpoints(surface, graph, entities, filter, extent) {
31365         var poly = extent.polygon(),
31366             midpoints = {};
31367
31368         for (var i = 0; i < entities.length; i++) {
31369             var entity = entities[i];
31370
31371             if (entity.type !== 'way')
31372                 continue;
31373             if (!filter(entity))
31374                 continue;
31375             if (context.selectedIDs().indexOf(entity.id) < 0)
31376                 continue;
31377
31378             var nodes = graph.childNodes(entity);
31379             for (var j = 0; j < nodes.length - 1; j++) {
31380
31381                 var a = nodes[j],
31382                     b = nodes[j + 1],
31383                     id = [a.id, b.id].sort().join('-');
31384
31385                 if (midpoints[id]) {
31386                     midpoints[id].parents.push(entity);
31387                 } else {
31388                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
31389                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
31390                             loc = null;
31391
31392                         if (extent.intersects(point)) {
31393                             loc = point;
31394                         } else {
31395                             for (var k = 0; k < 4; k++) {
31396                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
31397                                 if (point &&
31398                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
31399                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
31400                                 {
31401                                     loc = point;
31402                                     break;
31403                                 }
31404                             }
31405                         }
31406
31407                         if (loc) {
31408                             midpoints[id] = {
31409                                 type: 'midpoint',
31410                                 id: id,
31411                                 loc: loc,
31412                                 edge: [a.id, b.id],
31413                                 parents: [entity]
31414                             };
31415                         }
31416                     }
31417                 }
31418             }
31419         }
31420
31421         function midpointFilter(d) {
31422             if (midpoints[d.id])
31423                 return true;
31424
31425             for (var i = 0; i < d.parents.length; i++)
31426                 if (filter(d.parents[i]))
31427                     return true;
31428
31429             return false;
31430         }
31431
31432         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
31433             .filter(midpointFilter)
31434             .data(_.values(midpoints), function(d) { return d.id; });
31435
31436         var enter = groups.enter()
31437             .insert('g', ':first-child')
31438             .attr('class', 'midpoint');
31439
31440         enter.append('polygon')
31441             .attr('points', '-6,8 10,0 -6,-8')
31442             .attr('class', 'shadow');
31443
31444         enter.append('polygon')
31445             .attr('points', '-3,4 5,0 -3,-4')
31446             .attr('class', 'fill');
31447
31448         groups
31449             .attr('transform', function(d) {
31450                 var translate = iD.svg.PointTransform(projection),
31451                     a = context.entity(d.edge[0]),
31452                     b = context.entity(d.edge[1]),
31453                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
31454                 return translate(d) + ' rotate(' + angle + ')';
31455             })
31456             .call(iD.svg.TagClasses().tags(
31457                 function(d) { return d.parents[0].tags; }
31458             ));
31459
31460         // Propagate data bindings.
31461         groups.select('polygon.shadow');
31462         groups.select('polygon.fill');
31463
31464         groups.exit()
31465             .remove();
31466     };
31467 };
31468 iD.svg.Osm = function() {
31469     return function drawOsm(selection) {
31470         var layers = selection.selectAll('.layer-osm')
31471             .data(['areas', 'lines', 'hit', 'halo', 'label']);
31472
31473         layers.enter().append('g')
31474             .attr('class', function(d) { return 'layer-osm layer-' + d; });
31475     };
31476 };
31477 iD.svg.Points = function(projection, context) {
31478     function markerPath(selection, klass) {
31479         selection
31480             .attr('class', klass)
31481             .attr('transform', 'translate(-8, -23)')
31482             .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');
31483     }
31484
31485     function sortY(a, b) {
31486         return b.loc[1] - a.loc[1];
31487     }
31488
31489     return function drawPoints(surface, graph, entities, filter) {
31490         var wireframe = surface.classed('fill-wireframe'),
31491             points = wireframe ? [] : _.filter(entities, function(e) {
31492                 return e.geometry(graph) === 'point';
31493             });
31494
31495         points.sort(sortY);
31496
31497         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
31498             .filter(filter)
31499             .data(points, iD.Entity.key);
31500
31501         var group = groups.enter()
31502             .append('g')
31503             .attr('class', function(d) { return 'node point ' + d.id; })
31504             .order();
31505
31506         group.append('path')
31507             .call(markerPath, 'shadow');
31508
31509         group.append('path')
31510             .call(markerPath, 'stroke');
31511
31512         group.append('use')
31513             .attr('transform', 'translate(-6, -20)')
31514             .attr('class', 'icon')
31515             .attr('width', '12px')
31516             .attr('height', '12px');
31517
31518         groups.attr('transform', iD.svg.PointTransform(projection))
31519             .call(iD.svg.TagClasses());
31520
31521         // Selecting the following implicitly
31522         // sets the data (point entity) on the element
31523         groups.select('.shadow');
31524         groups.select('.stroke');
31525         groups.select('.icon')
31526             .attr('xlink:href', function(entity) {
31527                 var preset = context.presets().match(entity, graph);
31528                 return preset.icon ? '#' + preset.icon + '-12' : '';
31529             });
31530
31531         groups.exit()
31532             .remove();
31533     };
31534 };
31535 iD.svg.TagClasses = function() {
31536     var primaries = [
31537             'building', 'highway', 'railway', 'waterway', 'aeroway',
31538             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
31539             'leisure', 'place'
31540         ],
31541         statuses = [
31542             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
31543             'razed', 'demolished', 'obliterated'
31544         ],
31545         secondaries = [
31546             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
31547             'surface', 'tracktype', 'crossing'
31548         ],
31549         tagClassRe = /^tag-/,
31550         tags = function(entity) { return entity.tags; };
31551
31552
31553     var tagClasses = function(selection) {
31554         selection.each(function tagClassesEach(entity) {
31555             var value = this.className,
31556                 classes, primary, status;
31557
31558             if (value.baseVal !== undefined) value = value.baseVal;
31559
31560             classes = value.trim().split(/\s+/).filter(function(name) {
31561                 return name.length && !tagClassRe.test(name);
31562             }).join(' ');
31563
31564             var t = tags(entity), i, k, v;
31565
31566             // pick at most one primary classification tag..
31567             for (i = 0; i < primaries.length; i++) {
31568                 k = primaries[i];
31569                 v = t[k];
31570                 if (!v || v === 'no') continue;
31571
31572                 primary = k;
31573                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
31574                     status = v;
31575                     classes += ' tag-' + k;
31576                 } else {
31577                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
31578                 }
31579
31580                 break;
31581             }
31582
31583             // add at most one status tag, only if relates to primary tag..
31584             if (!status) {
31585                 for (i = 0; i < statuses.length; i++) {
31586                     k = statuses[i];
31587                     v = t[k];
31588                     if (!v || v === 'no') continue;
31589
31590                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
31591                         status = k;
31592                     }
31593                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
31594                         status = k;
31595                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
31596                         status = k;
31597                         primary = v;
31598                         classes += ' tag-' + v;
31599                     }  // else ignore e.g.  `highway=path + abandoned=railway`
31600
31601                     if (status) break;
31602                 }
31603             }
31604
31605             if (status) {
31606                 classes += ' tag-status tag-status-' + status;
31607             }
31608
31609             // add any secondary (structure) tags
31610             for (i = 0; i < secondaries.length; i++) {
31611                 k = secondaries[i];
31612                 v = t[k];
31613                 if (!v || v === 'no') continue;
31614                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31615             }
31616
31617             // For highways, look for surface tagging..
31618             if (primary === 'highway') {
31619                 var paved = (t.highway !== 'track');
31620                 for (k in t) {
31621                     v = t[k];
31622                     if (k in iD.pavedTags) {
31623                         paved = !!iD.pavedTags[k][v];
31624                         break;
31625                     }
31626                 }
31627                 if (!paved) {
31628                     classes += ' tag-unpaved';
31629                 }
31630             }
31631
31632             classes = classes.trim();
31633
31634             if (classes !== value) {
31635                 d3.select(this).attr('class', classes);
31636             }
31637         });
31638     };
31639
31640     tagClasses.tags = function(_) {
31641         if (!arguments.length) return tags;
31642         tags = _;
31643         return tagClasses;
31644     };
31645
31646     return tagClasses;
31647 };
31648 iD.svg.Turns = function(projection) {
31649     return function drawTurns(surface, graph, turns) {
31650         function key(turn) {
31651             return [turn.from.node + turn.via.node + turn.to.node].join('-');
31652         }
31653
31654         function icon(turn) {
31655             var u = turn.u ? '-u' : '';
31656             if (!turn.restriction)
31657                 return '#turn-yes' + u;
31658             var restriction = graph.entity(turn.restriction).tags.restriction;
31659             return '#turn-' +
31660                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31661         }
31662
31663         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
31664             .data(turns, key);
31665
31666         // Enter
31667         var enter = groups.enter().append('g')
31668             .attr('class', 'turn');
31669
31670         var nEnter = enter.filter(function (turn) { return !turn.u; });
31671
31672         nEnter.append('rect')
31673             .attr('transform', 'translate(-22, -12)')
31674             .attr('width', '44')
31675             .attr('height', '24');
31676
31677         nEnter.append('use')
31678             .attr('transform', 'translate(-22, -12)')
31679             .attr('width', '44')
31680             .attr('height', '24');
31681
31682
31683         var uEnter = enter.filter(function (turn) { return turn.u; });
31684
31685         uEnter.append('circle')
31686             .attr('r', '16');
31687
31688         uEnter.append('use')
31689             .attr('transform', 'translate(-16, -16)')
31690             .attr('width', '32')
31691             .attr('height', '32');
31692
31693
31694         // Update
31695         groups
31696             .attr('transform', function (turn) {
31697                 var v = graph.entity(turn.via.node),
31698                     t = graph.entity(turn.to.node),
31699                     a = iD.geo.angle(v, t, projection),
31700                     p = projection(v.loc),
31701                     r = turn.u ? 0 : 60;
31702
31703                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31704                     'rotate(' + a * 180 / Math.PI + ')';
31705             });
31706
31707         groups.select('use')
31708             .attr('xlink:href', icon);
31709
31710         groups.select('rect');
31711         groups.select('circle');
31712
31713
31714         // Exit
31715         groups.exit()
31716             .remove();
31717
31718         return this;
31719     };
31720 };
31721 iD.svg.Vertices = function(projection, context) {
31722     var radiuses = {
31723         //       z16-, z17, z18+, tagged
31724         shadow: [6,    7.5,   7.5,  11.5],
31725         stroke: [2.5,  3.5,   3.5,  7],
31726         fill:   [1,    1.5,   1.5,  1.5]
31727     };
31728
31729     var hover;
31730
31731     function siblingAndChildVertices(ids, graph, extent) {
31732         var vertices = {};
31733
31734         function addChildVertices(entity) {
31735             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31736                 var i;
31737                 if (entity.type === 'way') {
31738                     for (i = 0; i < entity.nodes.length; i++) {
31739                         addChildVertices(graph.entity(entity.nodes[i]));
31740                     }
31741                 } else if (entity.type === 'relation') {
31742                     for (i = 0; i < entity.members.length; i++) {
31743                         var member = context.hasEntity(entity.members[i].id);
31744                         if (member) {
31745                             addChildVertices(member);
31746                         }
31747                     }
31748                 } else if (entity.intersects(extent, graph)) {
31749                     vertices[entity.id] = entity;
31750                 }
31751             }
31752         }
31753
31754         ids.forEach(function(id) {
31755             var entity = context.hasEntity(id);
31756             if (entity && entity.type === 'node') {
31757                 vertices[entity.id] = entity;
31758                 context.graph().parentWays(entity).forEach(function(entity) {
31759                     addChildVertices(entity);
31760                 });
31761             } else if (entity) {
31762                 addChildVertices(entity);
31763             }
31764         });
31765
31766         return vertices;
31767     }
31768
31769     function draw(selection, vertices, klass, graph, zoom) {
31770         var icons = {},
31771             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
31772
31773         var groups = selection
31774             .data(vertices, iD.Entity.key);
31775
31776         function icon(entity) {
31777             if (entity.id in icons) return icons[entity.id];
31778             icons[entity.id] =
31779                 entity.hasInterestingTags() &&
31780                 context.presets().match(entity, graph).icon;
31781             return icons[entity.id];
31782         }
31783
31784         function setClass(klass) {
31785             return function(entity) {
31786                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31787             };
31788         }
31789
31790         function setAttributes(selection) {
31791             ['shadow','stroke','fill'].forEach(function(klass) {
31792                 var rads = radiuses[klass];
31793                 selection.selectAll('.' + klass)
31794                     .each(function(entity) {
31795                         var i = z && icon(entity),
31796                             c = i ? 0.5 : 0,
31797                             r = rads[i ? 3 : z];
31798                         this.setAttribute('cx', c);
31799                         this.setAttribute('cy', -c);
31800                         this.setAttribute('r', r);
31801                         if (i && klass === 'fill') {
31802                             this.setAttribute('visibility', 'hidden');
31803                         } else {
31804                             this.removeAttribute('visibility');
31805                         }
31806                     });
31807             });
31808
31809             selection.selectAll('use')
31810                 .each(function() {
31811                     if (z) {
31812                         this.removeAttribute('visibility');
31813                     } else {
31814                         this.setAttribute('visibility', 'hidden');
31815                     }
31816                 });
31817         }
31818
31819         var enter = groups.enter()
31820             .append('g')
31821             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
31822
31823         enter.append('circle')
31824             .each(setClass('shadow'));
31825
31826         enter.append('circle')
31827             .each(setClass('stroke'));
31828
31829         // Vertices with icons get a `use`.
31830         enter.filter(function(d) { return icon(d); })
31831             .append('use')
31832             .attr('transform', 'translate(-6, -6)')
31833             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
31834             .attr('width', '12px')
31835             .attr('height', '12px')
31836             .each(setClass('icon'));
31837
31838         // Vertices with tags get a fill.
31839         enter.filter(function(d) { return d.hasInterestingTags(); })
31840             .append('circle')
31841             .each(setClass('fill'));
31842
31843         groups
31844             .attr('transform', iD.svg.PointTransform(projection))
31845             .classed('shared', function(entity) { return graph.isShared(entity); })
31846             .call(setAttributes);
31847
31848         groups.exit()
31849             .remove();
31850     }
31851
31852     function drawVertices(surface, graph, entities, filter, extent, zoom) {
31853         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
31854             wireframe = surface.classed('fill-wireframe'),
31855             vertices = [];
31856
31857         for (var i = 0; i < entities.length; i++) {
31858             var entity = entities[i],
31859                 geometry = entity.geometry(graph);
31860
31861             if (wireframe && geometry === 'point') {
31862                 vertices.push(entity);
31863                 continue;
31864             }
31865
31866             if (geometry !== 'vertex')
31867                 continue;
31868
31869             if (entity.id in selected ||
31870                 entity.hasInterestingTags() ||
31871                 entity.isIntersection(graph)) {
31872                 vertices.push(entity);
31873             }
31874         }
31875
31876         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
31877             .filter(filter)
31878             .call(draw, vertices, 'vertex-persistent', graph, zoom);
31879
31880         drawHover(surface, graph, extent, zoom);
31881     }
31882
31883     function drawHover(surface, graph, extent, zoom) {
31884         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
31885
31886         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
31887             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
31888     }
31889
31890     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
31891         if (target === hover) return;
31892         hover = target;
31893         drawHover(surface, graph, extent, zoom);
31894     };
31895
31896     return drawVertices;
31897 };
31898 iD.ui = function(context) {
31899     function render(container) {
31900         var map = context.map();
31901
31902         if (iD.detect().opera) container.classed('opera', true);
31903
31904         var hash = iD.behavior.Hash(context);
31905
31906         hash();
31907
31908         if (!hash.hadHash) {
31909             map.centerZoom([0, 0], 2);
31910         }
31911
31912         container.append('svg')
31913             .attr('id', 'defs')
31914             .call(iD.svg.Defs(context));
31915
31916         container.append('div')
31917             .attr('id', 'sidebar')
31918             .attr('class', 'col4')
31919             .call(ui.sidebar);
31920
31921         var content = container.append('div')
31922             .attr('id', 'content');
31923
31924         var bar = content.append('div')
31925             .attr('id', 'bar')
31926             .attr('class', 'fillD');
31927
31928         content.append('div')
31929             .attr('id', 'map')
31930             .call(map);
31931
31932         content
31933             .call(iD.ui.MapInMap(context));
31934
31935         content.append('div')
31936             .call(iD.ui.Info(context));
31937
31938         bar.append('div')
31939             .attr('class', 'spacer col4');
31940
31941         var limiter = bar.append('div')
31942             .attr('class', 'limiter');
31943
31944         limiter.append('div')
31945             .attr('class', 'button-wrap joined col3')
31946             .call(iD.ui.Modes(context), limiter);
31947
31948         limiter.append('div')
31949             .attr('class', 'button-wrap joined col1')
31950             .call(iD.ui.UndoRedo(context));
31951
31952         limiter.append('div')
31953             .attr('class', 'button-wrap col1')
31954             .call(iD.ui.Save(context));
31955
31956         bar.append('div')
31957             .attr('class', 'full-screen')
31958             .call(iD.ui.FullScreen(context));
31959
31960         bar.append('div')
31961             .attr('class', 'spinner')
31962             .call(iD.ui.Spinner(context));
31963
31964         var controls = bar.append('div')
31965             .attr('class', 'map-controls');
31966
31967         controls.append('div')
31968             .attr('class', 'map-control zoombuttons')
31969             .call(iD.ui.Zoom(context));
31970
31971         controls.append('div')
31972             .attr('class', 'map-control geolocate-control')
31973             .call(iD.ui.Geolocate(map));
31974
31975         controls.append('div')
31976             .attr('class', 'map-control background-control')
31977             .call(iD.ui.Background(context));
31978
31979         controls.append('div')
31980             .attr('class', 'map-control map-data-control')
31981             .call(iD.ui.MapData(context));
31982
31983         controls.append('div')
31984             .attr('class', 'map-control help-control')
31985             .call(iD.ui.Help(context));
31986
31987         var about = content.append('div')
31988             .attr('id', 'about');
31989
31990         about.append('div')
31991             .attr('id', 'attrib')
31992             .call(iD.ui.Attribution(context));
31993
31994         var footer = about.append('div')
31995             .attr('id', 'footer')
31996             .attr('class', 'fillD');
31997
31998         footer.append('div')
31999             .attr('class', 'api-status')
32000             .call(iD.ui.Status(context));
32001
32002         footer.append('div')
32003             .attr('id', 'scale-block')
32004             .call(iD.ui.Scale(context));
32005
32006         var aboutList = footer.append('div')
32007             .attr('id', 'info-block')
32008             .append('ul')
32009             .attr('id', 'about-list');
32010
32011         if (!context.embed()) {
32012             aboutList.call(iD.ui.Account(context));
32013         }
32014
32015         aboutList.append('li')
32016             .append('a')
32017             .attr('target', '_blank')
32018             .attr('tabindex', -1)
32019             .attr('href', 'https://github.com/openstreetmap/iD')
32020             .text(iD.version);
32021
32022         var issueLinks = aboutList.append('li');
32023
32024         issueLinks.append('a')
32025             .attr('target', '_blank')
32026             .attr('tabindex', -1)
32027             .attr('href', 'https://github.com/openstreetmap/iD/issues')
32028             .call(iD.svg.Icon('#icon-bug', 'light'))
32029             .call(bootstrap.tooltip()
32030                 .title(t('report_a_bug'))
32031                 .placement('top')
32032             );
32033
32034         issueLinks.append('a')
32035             .attr('target', '_blank')
32036             .attr('tabindex', -1)
32037             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
32038             .call(iD.svg.Icon('#icon-translate', 'light'))
32039             .call(bootstrap.tooltip()
32040                 .title(t('help_translate'))
32041                 .placement('top')
32042             );
32043
32044         aboutList.append('li')
32045             .attr('class', 'feature-warning')
32046             .attr('tabindex', -1)
32047             .call(iD.ui.FeatureInfo(context));
32048
32049         aboutList.append('li')
32050             .attr('class', 'user-list')
32051             .attr('tabindex', -1)
32052             .call(iD.ui.Contributors(context));
32053
32054         window.onbeforeunload = function() {
32055             return context.save();
32056         };
32057
32058         window.onunload = function() {
32059             context.history().unlock();
32060         };
32061
32062         var mapDimensions = map.dimensions();
32063
32064         d3.select(window).on('resize.editor', function() {
32065             mapDimensions = content.dimensions(null);
32066             map.dimensions(mapDimensions);
32067         });
32068
32069         function pan(d) {
32070             return function() {
32071                 d3.event.preventDefault();
32072                 if (!context.inIntro()) context.pan(d);
32073             };
32074         }
32075
32076         // pan amount
32077         var pa = 10;
32078
32079         var keybinding = d3.keybinding('main')
32080             .on('⌫', function() { d3.event.preventDefault(); })
32081             .on('←', pan([pa, 0]))
32082             .on('↑', pan([0, pa]))
32083             .on('→', pan([-pa, 0]))
32084             .on('↓', pan([0, -pa]))
32085             .on('⇧←', pan([mapDimensions[0], 0]))
32086             .on('⇧↑', pan([0, mapDimensions[1]]))
32087             .on('⇧→', pan([-mapDimensions[0], 0]))
32088             .on('⇧↓', pan([0, -mapDimensions[1]]))
32089             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
32090             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
32091             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
32092             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
32093
32094         d3.select(document)
32095             .call(keybinding);
32096
32097         context.enter(iD.modes.Browse(context));
32098
32099         context.container()
32100             .call(iD.ui.Splash(context))
32101             .call(iD.ui.Restore(context));
32102
32103         var authenticating = iD.ui.Loading(context)
32104             .message(t('loading_auth'));
32105
32106         context.connection()
32107             .on('authenticating.ui', function() {
32108                 context.container()
32109                     .call(authenticating);
32110             })
32111             .on('authenticated.ui', function() {
32112                 authenticating.close();
32113             });
32114     }
32115
32116     function ui(container) {
32117         context.container(container);
32118         context.loadLocale(function() {
32119             render(container);
32120         });
32121     }
32122
32123     ui.sidebar = iD.ui.Sidebar(context);
32124
32125     return ui;
32126 };
32127
32128 iD.ui.tooltipHtml = function(text, key) {
32129     var s = '<span>' + text + '</span>';
32130     if (key) {
32131         s += '<div class="keyhint-wrap">' +
32132             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
32133             '<span class="keyhint"> ' + key + '</span></div>';
32134     }
32135     return s;
32136 };
32137 iD.ui.Account = function(context) {
32138     var connection = context.connection();
32139
32140     function update(selection) {
32141         if (!connection.authenticated()) {
32142             selection.selectAll('#userLink, #logoutLink')
32143                 .classed('hide', true);
32144             return;
32145         }
32146
32147         connection.userDetails(function(err, details) {
32148             var userLink = selection.select('#userLink'),
32149                 logoutLink = selection.select('#logoutLink');
32150
32151             userLink.html('');
32152             logoutLink.html('');
32153
32154             if (err) return;
32155
32156             selection.selectAll('#userLink, #logoutLink')
32157                 .classed('hide', false);
32158
32159             // Link
32160             userLink.append('a')
32161                 .attr('href', connection.userURL(details.display_name))
32162                 .attr('target', '_blank');
32163
32164             // Add thumbnail or dont
32165             if (details.image_url) {
32166                 userLink.append('img')
32167                     .attr('class', 'icon pre-text user-icon')
32168                     .attr('src', details.image_url);
32169             } else {
32170                 userLink
32171                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
32172             }
32173
32174             // Add user name
32175             userLink.append('span')
32176                 .attr('class', 'label')
32177                 .text(details.display_name);
32178
32179             logoutLink.append('a')
32180                 .attr('class', 'logout')
32181                 .attr('href', '#')
32182                 .text(t('logout'))
32183                 .on('click.logout', function() {
32184                     d3.event.preventDefault();
32185                     connection.logout();
32186                 });
32187         });
32188     }
32189
32190     return function(selection) {
32191         selection.append('li')
32192             .attr('id', 'logoutLink')
32193             .classed('hide', true);
32194
32195         selection.append('li')
32196             .attr('id', 'userLink')
32197             .classed('hide', true);
32198
32199         connection.on('auth.account', function() { update(selection); });
32200         update(selection);
32201     };
32202 };
32203 iD.ui.Attribution = function(context) {
32204     var selection;
32205
32206     function attribution(data, klass) {
32207         var div = selection.selectAll('.' + klass)
32208             .data([0]);
32209
32210         div.enter()
32211             .append('div')
32212             .attr('class', klass);
32213
32214         var background = div.selectAll('.attribution')
32215             .data(data, function(d) { return d.name(); });
32216
32217         background.enter()
32218             .append('span')
32219             .attr('class', 'attribution')
32220             .each(function(d) {
32221                 if (d.terms_html) {
32222                     d3.select(this)
32223                         .html(d.terms_html);
32224                     return;
32225                 }
32226
32227                 var source = d.terms_text || d.id || d.name();
32228
32229                 if (d.logo) {
32230                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
32231                 }
32232
32233                 if (d.terms_url) {
32234                     d3.select(this)
32235                         .append('a')
32236                         .attr('href', d.terms_url)
32237                         .attr('target', '_blank')
32238                         .html(source);
32239                 } else {
32240                     d3.select(this)
32241                         .text(source);
32242                 }
32243             });
32244
32245         background.exit()
32246             .remove();
32247
32248         var copyright = background.selectAll('.copyright-notice')
32249             .data(function(d) {
32250                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
32251                 return notice ? [notice] : [];
32252             });
32253
32254         copyright.enter()
32255             .append('span')
32256             .attr('class', 'copyright-notice');
32257
32258         copyright.text(String);
32259
32260         copyright.exit()
32261             .remove();
32262     }
32263
32264     function update() {
32265         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
32266         attribution(context.background().overlayLayerSources().filter(function (s) {
32267             return s.validZoom(context.map().zoom());
32268         }), 'overlay-layer-attribution');
32269     }
32270
32271     return function(select) {
32272         selection = select;
32273
32274         context.background()
32275             .on('change.attribution', update);
32276
32277         context.map()
32278             .on('move.attribution', _.throttle(update, 400, {leading: false}));
32279
32280         update();
32281     };
32282 };
32283 iD.ui.Background = function(context) {
32284     var key = 'B',
32285         opacities = [1, 0.75, 0.5, 0.25],
32286         directions = [
32287             ['left', [1, 0]],
32288             ['top', [0, -1]],
32289             ['right', [-1, 0]],
32290             ['bottom', [0, 1]]],
32291         opacityDefault = (context.storage('background-opacity') !== null) ?
32292             (+context.storage('background-opacity')) : 1.0,
32293         customTemplate = context.storage('background-custom-template') || '';
32294
32295     // Can be 0 from <1.3.0 use or due to issue #1923.
32296     if (opacityDefault === 0) opacityDefault = 1.0;
32297
32298     function background(selection) {
32299
32300         function sortSources(a, b) {
32301             return a.best() ? -1
32302                 : b.best() ? 1
32303                 : a.id === 'none' ? 1
32304                 : b.id === 'none' ? -1
32305                 : d3.ascending(a, b);
32306         }
32307
32308         function setOpacity(d) {
32309             var bg = context.container().selectAll('.layer-background')
32310                 .transition()
32311                 .style('opacity', d)
32312                 .attr('data-opacity', d);
32313
32314             if (!iD.detect().opera) {
32315                 iD.util.setTransform(bg, 0, 0);
32316             }
32317
32318             opacityList.selectAll('li')
32319                 .classed('active', function(_) { return _ === d; });
32320
32321             context.storage('background-opacity', d);
32322         }
32323
32324         function selectLayer() {
32325             function active(d) {
32326                 return context.background().showsLayer(d);
32327             }
32328
32329             content.selectAll('.layer, .custom_layer')
32330                 .classed('active', active)
32331                 .selectAll('input')
32332                 .property('checked', active);
32333         }
32334
32335         function clickSetSource(d) {
32336             d3.event.preventDefault();
32337             context.background().baseLayerSource(d);
32338             selectLayer();
32339         }
32340
32341         function editCustom() {
32342             d3.event.preventDefault();
32343             var template = window.prompt(t('background.custom_prompt'), customTemplate);
32344             if (!template ||
32345                 template.indexOf('google.com') !== -1 ||
32346                 template.indexOf('googleapis.com') !== -1 ||
32347                 template.indexOf('google.ru') !== -1) {
32348                 selectLayer();
32349                 return;
32350             }
32351             setCustom(template);
32352         }
32353
32354         function setCustom(template) {
32355             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
32356             selectLayer();
32357             context.storage('background-custom-template', template);
32358         }
32359
32360         function clickSetOverlay(d) {
32361             d3.event.preventDefault();
32362             context.background().toggleOverlayLayer(d);
32363             selectLayer();
32364         }
32365
32366         function drawList(layerList, type, change, filter) {
32367             var sources = context.background()
32368                 .sources(context.map().extent())
32369                 .filter(filter);
32370
32371             var layerLinks = layerList.selectAll('li.layer')
32372                 .data(sources, function(d) { return d.name(); })
32373                 .sort(sortSources);
32374
32375             var enter = layerLinks.enter()
32376                 .insert('li', '.custom_layer')
32377                 .attr('class', 'layer')
32378                 .classed('best', function(d) { return d.best(); });
32379
32380             // only set tooltips for layers with tooltips
32381             enter.filter(function(d) { return d.description; })
32382                 .call(bootstrap.tooltip()
32383                     .title(function(d) { return d.description; })
32384                     .placement('top'));
32385
32386             enter.filter(function(d) { return d.best(); })
32387                 .append('div')
32388                 .attr('class', 'best')
32389                 .call(bootstrap.tooltip()
32390                     .title(t('background.best_imagery'))
32391                     .placement('left'))
32392                 .append('span')
32393                 .html('&#9733;');
32394
32395             var label = enter.append('label');
32396
32397             label.append('input')
32398                 .attr('type', type)
32399                 .attr('name', 'layers')
32400                 .on('change', change);
32401
32402             label.append('span')
32403                 .text(function(d) { return d.name(); });
32404
32405             layerLinks.exit()
32406                 .remove();
32407
32408             layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
32409         }
32410
32411         function update() {
32412             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
32413             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
32414
32415             selectLayer();
32416
32417             var source = context.background().baseLayerSource();
32418             if (source.id === 'custom') {
32419                 customTemplate = source.template;
32420             }
32421         }
32422
32423         function clickNudge(d) {
32424             var timeout = window.setTimeout(function() {
32425                     interval = window.setInterval(nudge, 100);
32426                 }, 500),
32427                 interval;
32428
32429             d3.select(this).on('mouseup', function() {
32430                 window.clearInterval(interval);
32431                 window.clearTimeout(timeout);
32432                 nudge();
32433             });
32434
32435             function nudge() {
32436                 var offset = context.background()
32437                     .nudge(d[1], context.map().zoom())
32438                     .offset();
32439                 resetButton.classed('disabled', offset[0] === 0 && offset[1] === 0);
32440             }
32441         }
32442
32443         function hide() { setVisible(false); }
32444
32445         function toggle() {
32446             if (d3.event) d3.event.preventDefault();
32447             tooltip.hide(button);
32448             setVisible(!button.classed('active'));
32449         }
32450
32451         function setVisible(show) {
32452             if (show !== shown) {
32453                 button.classed('active', show);
32454                 shown = show;
32455
32456                 if (show) {
32457                     selection.on('mousedown.background-inside', function() {
32458                         return d3.event.stopPropagation();
32459                     });
32460                     content.style('display', 'block')
32461                         .style('right', '-300px')
32462                         .transition()
32463                         .duration(200)
32464                         .style('right', '0px');
32465                 } else {
32466                     content.style('display', 'block')
32467                         .style('right', '0px')
32468                         .transition()
32469                         .duration(200)
32470                         .style('right', '-300px')
32471                         .each('end', function() {
32472                             d3.select(this).style('display', 'none');
32473                         });
32474                     selection.on('mousedown.background-inside', null);
32475                 }
32476             }
32477         }
32478
32479
32480         var content = selection.append('div')
32481                 .attr('class', 'fillL map-overlay col3 content hide'),
32482             tooltip = bootstrap.tooltip()
32483                 .placement('left')
32484                 .html(true)
32485                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
32486             button = selection.append('button')
32487                 .attr('tabindex', -1)
32488                 .on('click', toggle)
32489                 .call(iD.svg.Icon('#icon-layers', 'light'))
32490                 .call(tooltip),
32491             shown = false;
32492
32493         var opa = content.append('div')
32494                 .attr('class', 'opacity-options-wrapper');
32495
32496         opa.append('h4')
32497             .text(t('background.title'));
32498
32499         var opacityList = opa.append('ul')
32500             .attr('class', 'opacity-options');
32501
32502         opacityList.selectAll('div.opacity')
32503             .data(opacities)
32504             .enter()
32505             .append('li')
32506             .attr('data-original-title', function(d) {
32507                 return t('background.percent_brightness', { opacity: (d * 100) });
32508             })
32509             .on('click.set-opacity', setOpacity)
32510             .html('<div class="select-box"></div>')
32511             .call(bootstrap.tooltip()
32512                 .placement('left'))
32513             .append('div')
32514             .attr('class', 'opacity')
32515             .style('opacity', function(d) { return 1.25 - d; });
32516
32517         var backgroundList = content.append('ul')
32518             .attr('class', 'layer-list');
32519
32520         var custom = backgroundList.append('li')
32521             .attr('class', 'custom_layer')
32522             .datum(iD.BackgroundSource.Custom());
32523
32524         custom.append('button')
32525             .attr('class', 'layer-browse')
32526             .call(bootstrap.tooltip()
32527                 .title(t('background.custom_button'))
32528                 .placement('left'))
32529             .on('click', editCustom)
32530             .call(iD.svg.Icon('#icon-search'));
32531
32532         var label = custom.append('label');
32533
32534         label.append('input')
32535             .attr('type', 'radio')
32536             .attr('name', 'layers')
32537             .on('change', function () {
32538                 if (customTemplate) {
32539                     setCustom(customTemplate);
32540                 } else {
32541                     editCustom();
32542                 }
32543             });
32544
32545         label.append('span')
32546             .text(t('background.custom'));
32547
32548         content.append('div')
32549           .attr('class', 'imagery-faq')
32550           .append('a')
32551           .attr('target', '_blank')
32552           .attr('tabindex', -1)
32553           .call(iD.svg.Icon('#icon-out-link', 'inline'))
32554           .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
32555           .append('span')
32556           .text(t('background.imagery_source_faq'));
32557
32558         var overlayList = content.append('ul')
32559             .attr('class', 'layer-list');
32560
32561         var controls = content.append('div')
32562             .attr('class', 'controls-list');
32563
32564         var minimapLabel = controls
32565             .append('label')
32566             .call(bootstrap.tooltip()
32567                 .html(true)
32568                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
32569                 .placement('top')
32570             );
32571
32572         minimapLabel.classed('minimap-toggle', true)
32573             .append('input')
32574             .attr('type', 'checkbox')
32575             .on('change', function() {
32576                 iD.ui.MapInMap.toggle();
32577                 d3.event.preventDefault();
32578             });
32579
32580         minimapLabel.append('span')
32581             .text(t('background.minimap.description'));
32582
32583         var adjustments = content.append('div')
32584             .attr('class', 'adjustments');
32585
32586         adjustments.append('a')
32587             .text(t('background.fix_misalignment'))
32588             .attr('href', '#')
32589             .classed('hide-toggle', true)
32590             .classed('expanded', false)
32591             .on('click', function() {
32592                 var exp = d3.select(this).classed('expanded');
32593                 nudgeContainer.style('display', exp ? 'none' : 'block');
32594                 d3.select(this).classed('expanded', !exp);
32595                 d3.event.preventDefault();
32596             });
32597
32598         var nudgeContainer = adjustments.append('div')
32599             .attr('class', 'nudge-container cf')
32600             .style('display', 'none');
32601
32602         nudgeContainer.selectAll('button')
32603             .data(directions).enter()
32604             .append('button')
32605             .attr('class', function(d) { return d[0] + ' nudge'; })
32606             .on('mousedown', clickNudge);
32607
32608         var resetButton = nudgeContainer
32609             .append('button')
32610             .attr('class', 'reset disabled')
32611             .on('click', function () {
32612                 context.background().offset([0, 0]);
32613                 resetButton.classed('disabled', true);
32614             })
32615             .call(iD.svg.Icon('#icon-undo'));
32616
32617         context.map()
32618             .on('move.background-update', _.debounce(update, 1000));
32619
32620         context.background()
32621             .on('change.background-update', update);
32622
32623         update();
32624         setOpacity(opacityDefault);
32625
32626         var keybinding = d3.keybinding('background')
32627             .on(key, toggle)
32628             .on('F', hide)
32629             .on('H', hide);
32630
32631         d3.select(document)
32632             .call(keybinding);
32633
32634         context.surface().on('mousedown.background-outside', hide);
32635         context.container().on('mousedown.background-outside', hide);
32636     }
32637
32638     return background;
32639 };
32640 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32641 // For example, ⌘Z -> Ctrl+Z
32642 iD.ui.cmd = function(code) {
32643     if (iD.detect().os === 'mac') {
32644         return code;
32645     }
32646
32647     if (iD.detect().os === 'win') {
32648         if (code === '⌘⇧Z') return 'Ctrl+Y';
32649     }
32650
32651     var result = '',
32652         replacements = {
32653             '⌘': 'Ctrl',
32654             '⇧': 'Shift',
32655             '⌥': 'Alt',
32656             '⌫': 'Backspace',
32657             '⌦': 'Delete'
32658         };
32659
32660     for (var i = 0; i < code.length; i++) {
32661         if (code[i] in replacements) {
32662             result += replacements[code[i]] + '+';
32663         } else {
32664             result += code[i];
32665         }
32666     }
32667
32668     return result;
32669 };
32670 iD.ui.Commit = function(context) {
32671     var dispatch = d3.dispatch('cancel', 'save');
32672
32673     function commit(selection) {
32674         var changes = context.history().changes(),
32675             summary = context.history().difference().summary();
32676
32677         function zoomToEntity(change) {
32678             var entity = change.entity;
32679             if (change.changeType !== 'deleted' &&
32680                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32681                 context.map().zoomTo(entity);
32682                 context.surface().selectAll(
32683                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
32684                     .classed('hover', true);
32685             }
32686         }
32687
32688         var header = selection.append('div')
32689             .attr('class', 'header fillL');
32690
32691         header.append('h3')
32692             .text(t('commit.title'));
32693
32694         var body = selection.append('div')
32695             .attr('class', 'body');
32696
32697
32698         // Comment Section
32699         var commentSection = body.append('div')
32700             .attr('class', 'modal-section form-field commit-form');
32701
32702         commentSection.append('label')
32703             .attr('class', 'form-label')
32704             .text(t('commit.message_label'));
32705
32706         var commentField = commentSection.append('textarea')
32707             .attr('placeholder', t('commit.description_placeholder'))
32708             .attr('maxlength', 255)
32709             .property('value', context.storage('comment') || '')
32710             .on('input.save', enableDisableSaveButton)
32711             .on('change.save', enableDisableSaveButton)
32712             .on('blur.save', function() {
32713                 context.storage('comment', this.value);
32714             });
32715
32716         function enableDisableSaveButton() {
32717             d3.selectAll('.save-section .save-button')
32718                 .attr('disabled', (this.value.length ? null : true));
32719         }
32720
32721         commentField.node().select();
32722
32723         context.connection().userChangesets(function (err, changesets) {
32724             if (err) return;
32725
32726             var comments = [];
32727
32728             for (var i = 0; i < changesets.length; i++) {
32729                 if (changesets[i].tags.comment) {
32730                     comments.push({
32731                         title: changesets[i].tags.comment,
32732                         value: changesets[i].tags.comment
32733                     });
32734                 }
32735             }
32736
32737             commentField.call(d3.combobox().data(comments));
32738         });
32739
32740         var changeSetInfo = commentSection.append('div')
32741             .attr('class', 'changeset-info');
32742
32743         changeSetInfo.append('a')
32744           .attr('target', '_blank')
32745           .attr('tabindex', -1)
32746           .call(iD.svg.Icon('#icon-out-link', 'inline'))
32747           .attr('href', t('commit.about_changeset_comments_link'))
32748           .append('span')
32749           .text(t('commit.about_changeset_comments'));
32750
32751         // Warnings
32752         var warnings = body.selectAll('div.warning-section')
32753             .data([context.history().validate(changes)])
32754             .enter()
32755             .append('div')
32756             .attr('class', 'modal-section warning-section fillL2')
32757             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
32758             .style('background', '#ffb');
32759
32760         warnings.append('h3')
32761             .text(t('commit.warnings'));
32762
32763         var warningLi = warnings.append('ul')
32764             .attr('class', 'changeset-list')
32765             .selectAll('li')
32766             .data(function(d) { return d; })
32767             .enter()
32768             .append('li')
32769             .style()
32770             .on('mouseover', mouseover)
32771             .on('mouseout', mouseout)
32772             .on('click', warningClick);
32773
32774         warningLi
32775             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
32776
32777         warningLi
32778             .append('strong').text(function(d) {
32779                 return d.message;
32780             });
32781
32782         warningLi.filter(function(d) { return d.tooltip; })
32783             .call(bootstrap.tooltip()
32784                 .title(function(d) { return d.tooltip; })
32785                 .placement('top')
32786             );
32787
32788
32789         // Upload Explanation
32790         var saveSection = body.append('div')
32791             .attr('class','modal-section save-section fillL cf');
32792
32793         var prose = saveSection.append('p')
32794             .attr('class', 'commit-info')
32795             .html(t('commit.upload_explanation'));
32796
32797         context.connection().userDetails(function(err, user) {
32798             if (err) return;
32799
32800             var userLink = d3.select(document.createElement('div'));
32801
32802             if (user.image_url) {
32803                 userLink.append('img')
32804                     .attr('src', user.image_url)
32805                     .attr('class', 'icon pre-text user-icon');
32806             }
32807
32808             userLink.append('a')
32809                 .attr('class','user-info')
32810                 .text(user.display_name)
32811                 .attr('href', context.connection().userURL(user.display_name))
32812                 .attr('tabindex', -1)
32813                 .attr('target', '_blank');
32814
32815             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
32816         });
32817
32818
32819         // Buttons
32820         var buttonSection = saveSection.append('div')
32821             .attr('class','buttons fillL cf');
32822
32823         var saveButton = buttonSection.append('button')
32824             .attr('class', 'action col5 button save-button')
32825             .attr('disabled', function() {
32826                 var n = d3.select('.commit-form textarea').node();
32827                 return (n && n.value.length) ? null : true;
32828             })
32829             .on('click.save', function() {
32830                 dispatch.save({
32831                     comment: commentField.node().value
32832                 });
32833             });
32834
32835         saveButton.append('span')
32836             .attr('class', 'label')
32837             .text(t('commit.save'));
32838
32839         var cancelButton = buttonSection.append('button')
32840             .attr('class', 'action col5 button cancel-button')
32841             .on('click.cancel', function() { dispatch.cancel(); });
32842
32843         cancelButton.append('span')
32844             .attr('class', 'label')
32845             .text(t('commit.cancel'));
32846
32847
32848         // Changes
32849         var changeSection = body.selectAll('div.commit-section')
32850             .data([0])
32851             .enter()
32852             .append('div')
32853             .attr('class', 'commit-section modal-section fillL2');
32854
32855         changeSection.append('h3')
32856             .text(t('commit.changes', {count: summary.length}));
32857
32858         var li = changeSection.append('ul')
32859             .attr('class', 'changeset-list')
32860             .selectAll('li')
32861             .data(summary)
32862             .enter()
32863             .append('li')
32864             .on('mouseover', mouseover)
32865             .on('mouseout', mouseout)
32866             .on('click', zoomToEntity);
32867
32868         li.each(function(d) {
32869             d3.select(this)
32870                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
32871         });
32872
32873         li.append('span')
32874             .attr('class', 'change-type')
32875             .text(function(d) {
32876                 return t('commit.' + d.changeType) + ' ';
32877             });
32878
32879         li.append('strong')
32880             .attr('class', 'entity-type')
32881             .text(function(d) {
32882                 return context.presets().match(d.entity, d.graph).name();
32883             });
32884
32885         li.append('span')
32886             .attr('class', 'entity-name')
32887             .text(function(d) {
32888                 var name = iD.util.displayName(d.entity) || '',
32889                     string = '';
32890                 if (name !== '') string += ':';
32891                 return string += ' ' + name;
32892             });
32893
32894         li.style('opacity', 0)
32895             .transition()
32896             .style('opacity', 1);
32897
32898         li.style('opacity', 0)
32899             .transition()
32900             .style('opacity', 1);
32901
32902         function mouseover(d) {
32903             if (d.entity) {
32904                 context.surface().selectAll(
32905                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
32906                 ).classed('hover', true);
32907             }
32908         }
32909
32910         function mouseout() {
32911             context.surface().selectAll('.hover')
32912                 .classed('hover', false);
32913         }
32914
32915         function warningClick(d) {
32916             if (d.entity) {
32917                 context.map().zoomTo(d.entity);
32918                 context.enter(
32919                     iD.modes.Select(context, [d.entity.id])
32920                         .suppressMenu(true));
32921             }
32922         }
32923     }
32924
32925     return d3.rebind(commit, dispatch, 'on');
32926 };
32927 iD.ui.confirm = function(selection) {
32928     var modal = iD.ui.modal(selection);
32929
32930     modal.select('.modal')
32931         .classed('modal-alert', true);
32932
32933     var section = modal.select('.content');
32934
32935     section.append('div')
32936         .attr('class', 'modal-section header');
32937
32938     section.append('div')
32939         .attr('class', 'modal-section message-text');
32940
32941     var buttons = section.append('div')
32942         .attr('class', 'modal-section buttons cf');
32943
32944     modal.okButton = function() {
32945         buttons
32946             .append('button')
32947             .attr('class', 'action col4')
32948             .on('click.confirm', function() {
32949                 modal.remove();
32950             })
32951             .text(t('confirm.okay'));
32952
32953         return modal;
32954     };
32955
32956     return modal;
32957 };
32958 iD.ui.Conflicts = function(context) {
32959     var dispatch = d3.dispatch('download', 'cancel', 'save'),
32960         list;
32961
32962     function conflicts(selection) {
32963         var header = selection
32964             .append('div')
32965             .attr('class', 'header fillL');
32966
32967         header
32968             .append('button')
32969             .attr('class', 'fr')
32970             .on('click', function() { dispatch.cancel(); })
32971             .call(iD.svg.Icon('#icon-close'));
32972
32973         header
32974             .append('h3')
32975             .text(t('save.conflict.header'));
32976
32977         var body = selection
32978             .append('div')
32979             .attr('class', 'body fillL');
32980
32981         body
32982             .append('div')
32983             .attr('class', 'conflicts-help')
32984             .text(t('save.conflict.help'))
32985             .append('a')
32986             .attr('class', 'conflicts-download')
32987             .text(t('save.conflict.download_changes'))
32988             .on('click.download', function() { dispatch.download(); });
32989
32990         body
32991             .append('div')
32992             .attr('class', 'conflict-container fillL3')
32993             .call(showConflict, 0);
32994
32995         body
32996             .append('div')
32997             .attr('class', 'conflicts-done')
32998             .attr('opacity', 0)
32999             .style('display', 'none')
33000             .text(t('save.conflict.done'));
33001
33002         var buttons = body
33003             .append('div')
33004             .attr('class','buttons col12 joined conflicts-buttons');
33005
33006         buttons
33007             .append('button')
33008             .attr('disabled', list.length > 1)
33009             .attr('class', 'action conflicts-button col6')
33010             .text(t('save.title'))
33011             .on('click.try_again', function() { dispatch.save(); });
33012
33013         buttons
33014             .append('button')
33015             .attr('class', 'secondary-action conflicts-button col6')
33016             .text(t('confirm.cancel'))
33017             .on('click.cancel', function() { dispatch.cancel(); });
33018     }
33019
33020
33021     function showConflict(selection, index) {
33022         if (index < 0 || index >= list.length) return;
33023
33024         var parent = d3.select(selection.node().parentNode);
33025
33026         // enable save button if this is the last conflict being reviewed..
33027         if (index === list.length - 1) {
33028             window.setTimeout(function() {
33029                 parent.select('.conflicts-button')
33030                     .attr('disabled', null);
33031
33032                 parent.select('.conflicts-done')
33033                     .transition()
33034                     .attr('opacity', 1)
33035                     .style('display', 'block');
33036             }, 250);
33037         }
33038
33039         var item = selection
33040             .selectAll('.conflict')
33041             .data([list[index]]);
33042
33043         var enter = item.enter()
33044             .append('div')
33045             .attr('class', 'conflict');
33046
33047         enter
33048             .append('h4')
33049             .attr('class', 'conflict-count')
33050             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
33051
33052         enter
33053             .append('a')
33054             .attr('class', 'conflict-description')
33055             .attr('href', '#')
33056             .text(function(d) { return d.name; })
33057             .on('click', function(d) {
33058                 zoomToEntity(d.id);
33059                 d3.event.preventDefault();
33060             });
33061
33062         var details = enter
33063             .append('div')
33064             .attr('class', 'conflict-detail-container');
33065
33066         details
33067             .append('ul')
33068             .attr('class', 'conflict-detail-list')
33069             .selectAll('li')
33070             .data(function(d) { return d.details || []; })
33071             .enter()
33072             .append('li')
33073             .attr('class', 'conflict-detail-item')
33074             .html(function(d) { return d; });
33075
33076         details
33077             .append('div')
33078             .attr('class', 'conflict-choices')
33079             .call(addChoices);
33080
33081         details
33082             .append('div')
33083             .attr('class', 'conflict-nav-buttons joined cf')
33084             .selectAll('button')
33085             .data(['previous', 'next'])
33086             .enter()
33087             .append('button')
33088             .text(function(d) { return t('save.conflict.' + d); })
33089             .attr('class', 'conflict-nav-button action col6')
33090             .attr('disabled', function(d, i) {
33091                 return (i === 0 && index === 0) ||
33092                     (i === 1 && index === list.length - 1) || null;
33093             })
33094             .on('click', function(d, i) {
33095                 var container = parent.select('.conflict-container'),
33096                 sign = (i === 0 ? -1 : 1);
33097
33098                 container
33099                     .selectAll('.conflict')
33100                     .remove();
33101
33102                 container
33103                     .call(showConflict, index + sign);
33104
33105                 d3.event.preventDefault();
33106             });
33107
33108         item.exit()
33109             .remove();
33110
33111     }
33112
33113     function addChoices(selection) {
33114         var choices = selection
33115             .append('ul')
33116             .attr('class', 'layer-list')
33117             .selectAll('li')
33118             .data(function(d) { return d.choices || []; });
33119
33120         var enter = choices.enter()
33121             .append('li')
33122             .attr('class', 'layer');
33123
33124         var label = enter
33125             .append('label');
33126
33127         label
33128             .append('input')
33129             .attr('type', 'radio')
33130             .attr('name', function(d) { return d.id; })
33131             .on('change', function(d, i) {
33132                 var ul = this.parentNode.parentNode.parentNode;
33133                 ul.__data__.chosen = i;
33134                 choose(ul, d);
33135             });
33136
33137         label
33138             .append('span')
33139             .text(function(d) { return d.text; });
33140
33141         choices
33142             .each(function(d, i) {
33143                 var ul = this.parentNode;
33144                 if (ul.__data__.chosen === i) choose(ul, d);
33145             });
33146     }
33147
33148     function choose(ul, datum) {
33149         if (d3.event) d3.event.preventDefault();
33150
33151         d3.select(ul)
33152             .selectAll('li')
33153             .classed('active', function(d) { return d === datum; })
33154             .selectAll('input')
33155             .property('checked', function(d) { return d === datum; });
33156
33157         var extent = iD.geo.Extent(),
33158             entity;
33159
33160         entity = context.graph().hasEntity(datum.id);
33161         if (entity) extent._extend(entity.extent(context.graph()));
33162
33163         datum.action();
33164
33165         entity = context.graph().hasEntity(datum.id);
33166         if (entity) extent._extend(entity.extent(context.graph()));
33167
33168         zoomToEntity(datum.id, extent);
33169     }
33170
33171     function zoomToEntity(id, extent) {
33172         context.surface().selectAll('.hover')
33173             .classed('hover', false);
33174
33175         var entity = context.graph().hasEntity(id);
33176         if (entity) {
33177             if (extent) {
33178                 context.map().trimmedExtent(extent);
33179             } else {
33180                 context.map().zoomTo(entity);
33181             }
33182             context.surface().selectAll(
33183                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
33184                 .classed('hover', true);
33185         }
33186     }
33187
33188
33189     // The conflict list should be an array of objects like:
33190     // {
33191     //     id: id,
33192     //     name: entityName(local),
33193     //     details: merge.conflicts(),
33194     //     chosen: 1,
33195     //     choices: [
33196     //         choice(id, keepMine, forceLocal),
33197     //         choice(id, keepTheirs, forceRemote)
33198     //     ]
33199     // }
33200     conflicts.list = function(_) {
33201         if (!arguments.length) return list;
33202         list = _;
33203         return conflicts;
33204     };
33205
33206     return d3.rebind(conflicts, dispatch, 'on');
33207 };
33208 iD.ui.Contributors = function(context) {
33209     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
33210         limit = 4,
33211         hidden = false,
33212         wrap = d3.select(null);
33213
33214     function update() {
33215         var users = {},
33216             entities = context.intersects(context.map().extent());
33217
33218         entities.forEach(function(entity) {
33219             if (entity && entity.user) users[entity.user] = true;
33220         });
33221
33222         var u = Object.keys(users),
33223             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
33224
33225         wrap.html('')
33226             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
33227
33228         var userList = d3.select(document.createElement('span'));
33229
33230         userList.selectAll()
33231             .data(subset)
33232             .enter()
33233             .append('a')
33234             .attr('class', 'user-link')
33235             .attr('href', function(d) { return context.connection().userURL(d); })
33236             .attr('target', '_blank')
33237             .attr('tabindex', -1)
33238             .text(String);
33239
33240         if (u.length > limit) {
33241             var count = d3.select(document.createElement('span'));
33242
33243             count.append('a')
33244                 .attr('target', '_blank')
33245                 .attr('tabindex', -1)
33246                 .attr('href', function() {
33247                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
33248                 })
33249                 .text(u.length - limit + 1);
33250
33251             wrap.append('span')
33252                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
33253
33254         } else {
33255             wrap.append('span')
33256                 .html(t('contributors.list', { users: userList.html() }));
33257         }
33258
33259         if (!u.length) {
33260             hidden = true;
33261             wrap
33262                 .transition()
33263                 .style('opacity', 0);
33264
33265         } else if (hidden) {
33266             wrap
33267                 .transition()
33268                 .style('opacity', 1);
33269         }
33270     }
33271
33272     return function(selection) {
33273         wrap = selection;
33274         update();
33275
33276         context.connection().on('loaded.contributors', debouncedUpdate);
33277         context.map().on('move.contributors', debouncedUpdate);
33278     };
33279 };
33280 iD.ui.Disclosure = function() {
33281     var dispatch = d3.dispatch('toggled'),
33282         title,
33283         expanded = false,
33284         content = function () {};
33285
33286     var disclosure = function(selection) {
33287         var $link = selection.selectAll('.hide-toggle')
33288             .data([0]);
33289
33290         $link.enter().append('a')
33291             .attr('href', '#')
33292             .attr('class', 'hide-toggle');
33293
33294         $link.text(title)
33295             .on('click', toggle)
33296             .classed('expanded', expanded);
33297
33298         var $body = selection.selectAll('div')
33299             .data([0]);
33300
33301         $body.enter().append('div');
33302
33303         $body.classed('hide', !expanded)
33304             .call(content);
33305
33306         function toggle() {
33307             expanded = !expanded;
33308             $link.classed('expanded', expanded);
33309             $body.call(iD.ui.Toggle(expanded));
33310             dispatch.toggled(expanded);
33311         }
33312     };
33313
33314     disclosure.title = function(_) {
33315         if (!arguments.length) return title;
33316         title = _;
33317         return disclosure;
33318     };
33319
33320     disclosure.expanded = function(_) {
33321         if (!arguments.length) return expanded;
33322         expanded = _;
33323         return disclosure;
33324     };
33325
33326     disclosure.content = function(_) {
33327         if (!arguments.length) return content;
33328         content = _;
33329         return disclosure;
33330     };
33331
33332     return d3.rebind(disclosure, dispatch, 'on');
33333 };
33334 iD.ui.EntityEditor = function(context) {
33335     var dispatch = d3.dispatch('choose'),
33336         state = 'select',
33337         coalesceChanges = false,
33338         modified = false,
33339         base,
33340         id,
33341         preset,
33342         reference;
33343
33344     var presetEditor = iD.ui.preset(context)
33345         .on('change', changeTags);
33346     var rawTagEditor = iD.ui.RawTagEditor(context)
33347         .on('change', changeTags);
33348
33349     function entityEditor(selection) {
33350         var entity = context.entity(id),
33351             tags = _.clone(entity.tags);
33352
33353         var $header = selection.selectAll('.header')
33354             .data([0]);
33355
33356         // Enter
33357         var $enter = $header.enter().append('div')
33358             .attr('class', 'header fillL cf');
33359
33360         $enter.append('button')
33361             .attr('class', 'fl preset-reset preset-choose')
33362             .append('span')
33363             .html('&#9668;');
33364
33365         $enter.append('button')
33366             .attr('class', 'fr preset-close')
33367             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
33368
33369         $enter.append('h3');
33370
33371         // Update
33372         $header.select('h3')
33373             .text(t('inspector.edit'));
33374
33375         $header.select('.preset-close')
33376             .on('click', function() {
33377                 context.enter(iD.modes.Browse(context));
33378             });
33379
33380         var $body = selection.selectAll('.inspector-body')
33381             .data([0]);
33382
33383         // Enter
33384         $enter = $body.enter().append('div')
33385             .attr('class', 'inspector-body');
33386
33387         $enter.append('div')
33388             .attr('class', 'preset-list-item inspector-inner')
33389             .append('div')
33390             .attr('class', 'preset-list-button-wrap')
33391             .append('button')
33392             .attr('class', 'preset-list-button preset-reset')
33393             .call(bootstrap.tooltip()
33394                 .title(t('inspector.back_tooltip'))
33395                 .placement('bottom'))
33396             .append('div')
33397             .attr('class', 'label');
33398
33399         $body.select('.preset-list-button-wrap')
33400             .call(reference.button);
33401
33402         $body.select('.preset-list-item')
33403             .call(reference.body);
33404
33405         $enter.append('div')
33406             .attr('class', 'inspector-border inspector-preset');
33407
33408         $enter.append('div')
33409             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
33410
33411         $enter.append('div')
33412             .attr('class', 'inspector-border raw-member-editor inspector-inner');
33413
33414         $enter.append('div')
33415             .attr('class', 'raw-membership-editor inspector-inner');
33416
33417         selection.selectAll('.preset-reset')
33418             .on('click', function() {
33419                 dispatch.choose(preset);
33420             });
33421
33422         // Update
33423         $body.select('.preset-list-item button')
33424             .call(iD.ui.PresetIcon()
33425                 .geometry(context.geometry(id))
33426                 .preset(preset));
33427
33428         $body.select('.preset-list-item .label')
33429             .text(preset.name());
33430
33431         $body.select('.inspector-preset')
33432             .call(presetEditor
33433                 .preset(preset)
33434                 .entityID(id)
33435                 .tags(tags)
33436                 .state(state));
33437
33438         $body.select('.raw-tag-editor')
33439             .call(rawTagEditor
33440                 .preset(preset)
33441                 .entityID(id)
33442                 .tags(tags)
33443                 .state(state));
33444
33445         if (entity.type === 'relation') {
33446             $body.select('.raw-member-editor')
33447                 .style('display', 'block')
33448                 .call(iD.ui.RawMemberEditor(context)
33449                     .entityID(id));
33450         } else {
33451             $body.select('.raw-member-editor')
33452                 .style('display', 'none');
33453         }
33454
33455         $body.select('.raw-membership-editor')
33456             .call(iD.ui.RawMembershipEditor(context)
33457                 .entityID(id));
33458
33459         function historyChanged() {
33460             if (state === 'hide') return;
33461
33462             var entity = context.hasEntity(id),
33463                 graph = context.graph();
33464             if (!entity) return;
33465
33466             entityEditor.preset(context.presets().match(entity, graph));
33467             entityEditor.modified(base !== graph);
33468             entityEditor(selection);
33469         }
33470
33471         context.history()
33472             .on('change.entity-editor', historyChanged);
33473     }
33474
33475     function clean(o) {
33476
33477         function cleanVal(k, v) {
33478             function keepSpaces(k) {
33479                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
33480                     'operating_times', 'smoking_hours', 'happy_hours'];
33481                 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
33482             }
33483
33484             var blacklist = ['description', 'note', 'fixme'];
33485             if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
33486
33487             var cleaned = v.split(';')
33488                 .map(function(s) { return s.trim(); })
33489                 .join(keepSpaces(k) ? '; ' : ';');
33490
33491             // The code below is not intended to validate websites and emails.
33492             // It is only intended to prevent obvious copy-paste errors. (#2323)
33493
33494             // clean website- and email-like tags
33495             if (k.indexOf('website') !== -1 ||
33496                 k.indexOf('email') !== -1 ||
33497                 cleaned.indexOf('http') === 0) {
33498                 cleaned = cleaned
33499                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
33500
33501             }
33502
33503             return cleaned;
33504         }
33505
33506         var out = {}, k, v;
33507         for (k in o) {
33508             if (k && (v = o[k]) !== undefined) {
33509                 out[k] = cleanVal(k, v);
33510             }
33511         }
33512         return out;
33513     }
33514
33515     // Tag changes that fire on input can all get coalesced into a single
33516     // history operation when the user leaves the field.  #2342
33517     function changeTags(changed, onInput) {
33518         var entity = context.entity(id),
33519             annotation = t('operations.change_tags.annotation'),
33520             tags = clean(_.extend({}, entity.tags, changed));
33521
33522         if (!_.isEqual(entity.tags, tags)) {
33523             if (coalesceChanges) {
33524                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
33525             } else {
33526                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
33527             }
33528         }
33529
33530         coalesceChanges = !!onInput;
33531     }
33532
33533     entityEditor.modified = function(_) {
33534         if (!arguments.length) return modified;
33535         modified = _;
33536         d3.selectAll('button.preset-close use')
33537             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
33538     };
33539
33540     entityEditor.state = function(_) {
33541         if (!arguments.length) return state;
33542         state = _;
33543         return entityEditor;
33544     };
33545
33546     entityEditor.entityID = function(_) {
33547         if (!arguments.length) return id;
33548         id = _;
33549         base = context.graph();
33550         entityEditor.preset(context.presets().match(context.entity(id), base));
33551         entityEditor.modified(false);
33552         coalesceChanges = false;
33553         return entityEditor;
33554     };
33555
33556     entityEditor.preset = function(_) {
33557         if (!arguments.length) return preset;
33558         if (_ !== preset) {
33559             preset = _;
33560             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
33561                 .showing(false);
33562         }
33563         return entityEditor;
33564     };
33565
33566     return d3.rebind(entityEditor, dispatch, 'on');
33567 };
33568 iD.ui.FeatureInfo = function(context) {
33569     function update(selection) {
33570         var features = context.features(),
33571             stats = features.stats(),
33572             count = 0,
33573             hiddenList = _.compact(_.map(features.hidden(), function(k) {
33574                 if (stats[k]) {
33575                     count += stats[k];
33576                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
33577                 }
33578             }));
33579
33580         selection.html('');
33581
33582         if (hiddenList.length) {
33583             var tooltip = bootstrap.tooltip()
33584                     .placement('top')
33585                     .html(true)
33586                     .title(function() {
33587                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
33588                     });
33589
33590             var warning = selection.append('a')
33591                 .attr('href', '#')
33592                 .attr('tabindex', -1)
33593                 .html(t('feature_info.hidden_warning', { count: count }))
33594                 .call(tooltip)
33595                 .on('click', function() {
33596                     tooltip.hide(warning);
33597                     // open map data panel?
33598                     d3.event.preventDefault();
33599                 });
33600         }
33601
33602         selection
33603             .classed('hide', !hiddenList.length);
33604     }
33605
33606     return function(selection) {
33607         update(selection);
33608
33609         context.features().on('change.feature_info', function() {
33610             update(selection);
33611         });
33612     };
33613 };
33614 iD.ui.FeatureList = function(context) {
33615     var geocodeResults;
33616
33617     function featureList(selection) {
33618         var header = selection.append('div')
33619             .attr('class', 'header fillL cf');
33620
33621         header.append('h3')
33622             .text(t('inspector.feature_list'));
33623
33624         function keypress() {
33625             var q = search.property('value'),
33626                 items = list.selectAll('.feature-list-item');
33627             if (d3.event.keyCode === 13 && q.length && items.size()) {
33628                 click(items.datum());
33629             }
33630         }
33631
33632         function inputevent() {
33633             geocodeResults = undefined;
33634             drawList();
33635         }
33636
33637         var searchWrap = selection.append('div')
33638             .attr('class', 'search-header');
33639
33640         var search = searchWrap.append('input')
33641             .attr('placeholder', t('inspector.search'))
33642             .attr('type', 'search')
33643             .on('keypress', keypress)
33644             .on('input', inputevent);
33645
33646         searchWrap
33647             .call(iD.svg.Icon('#icon-search', 'pre-text'));
33648
33649         var listWrap = selection.append('div')
33650             .attr('class', 'inspector-body');
33651
33652         var list = listWrap.append('div')
33653             .attr('class', 'feature-list cf');
33654
33655         context
33656             .on('exit.feature-list', clearSearch);
33657         context.map()
33658             .on('drawn.feature-list', mapDrawn);
33659
33660         function clearSearch() {
33661             search.property('value', '');
33662             drawList();
33663         }
33664
33665         function mapDrawn(e) {
33666             if (e.full) {
33667                 drawList();
33668             }
33669         }
33670
33671         function features() {
33672             var entities = {},
33673                 result = [],
33674                 graph = context.graph(),
33675                 q = search.property('value').toLowerCase();
33676
33677             if (!q) return result;
33678
33679             var idMatch = q.match(/^([nwr])([0-9]+)$/);
33680
33681             if (idMatch) {
33682                 result.push({
33683                     id: idMatch[0],
33684                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33685                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33686                     name: idMatch[2]
33687                 });
33688             }
33689
33690             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
33691
33692             if (locationMatch) {
33693                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
33694                 result.push({
33695                     id: -1,
33696                     geometry: 'point',
33697                     type: t('inspector.location'),
33698                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
33699                     location: loc
33700                 });
33701             }
33702
33703             function addEntity(entity) {
33704                 if (entity.id in entities || result.length > 200)
33705                     return;
33706
33707                 entities[entity.id] = true;
33708
33709                 var name = iD.util.displayName(entity) || '';
33710                 if (name.toLowerCase().indexOf(q) >= 0) {
33711                     result.push({
33712                         id: entity.id,
33713                         entity: entity,
33714                         geometry: context.geometry(entity.id),
33715                         type: context.presets().match(entity, graph).name(),
33716                         name: name
33717                     });
33718                 }
33719
33720                 graph.parentRelations(entity).forEach(function(parent) {
33721                     addEntity(parent);
33722                 });
33723             }
33724
33725             var visible = context.surface().selectAll('.point, .line, .area')[0];
33726             for (var i = 0; i < visible.length && result.length <= 200; i++) {
33727                 addEntity(visible[i].__data__);
33728             }
33729
33730             (geocodeResults || []).forEach(function(d) {
33731                 // https://github.com/openstreetmap/iD/issues/1890
33732                 if (d.osm_type && d.osm_id) {
33733                     result.push({
33734                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
33735                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
33736                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
33737                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
33738                         name: d.display_name,
33739                         extent: new iD.geo.Extent(
33740                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
33741                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
33742                     });
33743                 }
33744             });
33745
33746             return result;
33747         }
33748
33749         function drawList() {
33750             var value = search.property('value'),
33751                 results = features();
33752
33753             list.classed('filtered', value.length);
33754
33755             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
33756
33757             var resultsIndicator = list.selectAll('.no-results-item')
33758                 .data([0])
33759                 .enter().append('button')
33760                 .property('disabled', true)
33761                 .attr('class', 'no-results-item')
33762                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33763
33764             resultsIndicator.append('span')
33765                 .attr('class', 'entity-name');
33766
33767             list.selectAll('.no-results-item .entity-name')
33768                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
33769
33770             list.selectAll('.geocode-item')
33771                 .data([0])
33772                 .enter().append('button')
33773                 .attr('class', 'geocode-item')
33774                 .on('click', geocode)
33775                 .append('div')
33776                 .attr('class', 'label')
33777                 .append('span')
33778                 .attr('class', 'entity-name')
33779                 .text(t('geocoder.search'));
33780
33781             list.selectAll('.no-results-item')
33782                 .style('display', (value.length && !results.length) ? 'block' : 'none');
33783
33784             list.selectAll('.geocode-item')
33785                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
33786
33787             list.selectAll('.feature-list-item')
33788                 .data([-1])
33789                 .remove();
33790
33791             var items = list.selectAll('.feature-list-item')
33792                 .data(results, function(d) { return d.id; });
33793
33794             var enter = items.enter()
33795                 .insert('button', '.geocode-item')
33796                 .attr('class', 'feature-list-item')
33797                 .on('mouseover', mouseover)
33798                 .on('mouseout', mouseout)
33799                 .on('click', click);
33800
33801             var label = enter
33802                 .append('div')
33803                 .attr('class', 'label');
33804
33805             label.each(function(d) {
33806                 d3.select(this)
33807                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
33808             });
33809
33810             label.append('span')
33811                 .attr('class', 'entity-type')
33812                 .text(function(d) { return d.type; });
33813
33814             label.append('span')
33815                 .attr('class', 'entity-name')
33816                 .text(function(d) { return d.name; });
33817
33818             enter.style('opacity', 0)
33819                 .transition()
33820                 .style('opacity', 1);
33821
33822             items.order();
33823
33824             items.exit()
33825                 .remove();
33826         }
33827
33828         function mouseover(d) {
33829             if (d.id === -1) return;
33830
33831             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
33832                 .classed('hover', true);
33833         }
33834
33835         function mouseout() {
33836             context.surface().selectAll('.hover')
33837                 .classed('hover', false);
33838         }
33839
33840         function click(d) {
33841             d3.event.preventDefault();
33842             if (d.location) {
33843                 context.map().centerZoom([d.location[1], d.location[0]], 20);
33844             }
33845             else if (d.entity) {
33846                 if (d.entity.type === 'node') {
33847                     context.map().center(d.entity.loc);
33848                 } else if (d.entity.type === 'way') {
33849                     var center = context.projection(context.map().center()),
33850                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
33851                     context.map().center(edge.loc);
33852                 }
33853                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
33854             } else {
33855                 context.zoomToEntity(d.id);
33856             }
33857         }
33858
33859         function geocode() {
33860             var searchVal = encodeURIComponent(search.property('value'));
33861             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
33862                 geocodeResults = resp || [];
33863                 drawList();
33864             });
33865         }
33866     }
33867
33868     return featureList;
33869 };
33870 iD.ui.flash = function(selection) {
33871     var modal = iD.ui.modal(selection);
33872
33873     modal.select('.modal').classed('modal-flash', true);
33874
33875     modal.select('.content')
33876         .classed('modal-section', true)
33877         .append('div')
33878         .attr('class', 'description');
33879
33880     modal.on('click.flash', function() { modal.remove(); });
33881
33882     setTimeout(function() {
33883         modal.remove();
33884         return true;
33885     }, 1500);
33886
33887     return modal;
33888 };
33889 iD.ui.FullScreen = function(context) {
33890     var element = context.container().node(),
33891         keybinding = d3.keybinding('full-screen');
33892         // button;
33893
33894     function getFullScreenFn() {
33895         if (element.requestFullscreen) {
33896             return element.requestFullscreen;
33897         } else if (element.msRequestFullscreen) {
33898             return  element.msRequestFullscreen;
33899         } else if (element.mozRequestFullScreen) {
33900             return  element.mozRequestFullScreen;
33901         } else if (element.webkitRequestFullscreen) {
33902             return element.webkitRequestFullscreen;
33903         }
33904     }
33905
33906     function getExitFullScreenFn() {
33907         if (document.exitFullscreen) {
33908             return document.exitFullscreen;
33909         } else if (document.msExitFullscreen) {
33910             return  document.msExitFullscreen;
33911         } else if (document.mozCancelFullScreen) {
33912             return  document.mozCancelFullScreen;
33913         } else if (document.webkitExitFullscreen) {
33914             return document.webkitExitFullscreen;
33915         }
33916     }
33917
33918     function isFullScreen() {
33919         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
33920             document.msFullscreenElement;
33921     }
33922
33923     function isSupported() {
33924         return !!getFullScreenFn();
33925     }
33926
33927     function fullScreen() {
33928         d3.event.preventDefault();
33929         if (!isFullScreen()) {
33930             // button.classed('active', true);
33931             getFullScreenFn().apply(element);
33932         } else {
33933             // button.classed('active', false);
33934             getExitFullScreenFn().apply(document);
33935         }
33936     }
33937
33938     return function() { // selection) {
33939         if (!isSupported())
33940             return;
33941
33942         // var tooltip = bootstrap.tooltip()
33943         //     .placement('left');
33944
33945         // button = selection.append('button')
33946         //     .attr('title', t('full_screen'))
33947         //     .attr('tabindex', -1)
33948         //     .on('click', fullScreen)
33949         //     .call(tooltip);
33950
33951         // button.append('span')
33952         //     .attr('class', 'icon full-screen');
33953
33954         keybinding
33955             .on('f11', fullScreen)
33956             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
33957
33958         d3.select(document)
33959             .call(keybinding);
33960     };
33961 };
33962 iD.ui.Geolocate = function(map) {
33963     function click() {
33964         navigator.geolocation.getCurrentPosition(
33965             success, error);
33966     }
33967
33968     function success(position) {
33969         var extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
33970             .padByMeters(position.coords.accuracy);
33971
33972         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
33973     }
33974
33975     function error() { }
33976
33977     return function(selection) {
33978         if (!navigator.geolocation) return;
33979
33980         selection.append('button')
33981             .attr('tabindex', -1)
33982             .attr('title', t('geolocate.title'))
33983             .on('click', click)
33984             .call(iD.svg.Icon('#icon-geolocate', 'light'))
33985             .call(bootstrap.tooltip()
33986                 .placement('left'));
33987     };
33988 };
33989 iD.ui.Help = function(context) {
33990     var key = 'H';
33991
33992     var docKeys = [
33993         'help.help',
33994         'help.editing_saving',
33995         'help.roads',
33996         'help.gps',
33997         'help.imagery',
33998         'help.addresses',
33999         'help.inspector',
34000         'help.buildings',
34001         'help.relations'];
34002
34003     var docs = docKeys.map(function(key) {
34004         var text = t(key);
34005         return {
34006             title: text.split('\n')[0].replace('#', '').trim(),
34007             html: marked(text.split('\n').slice(1).join('\n'))
34008         };
34009     });
34010
34011     function help(selection) {
34012
34013         function hide() {
34014             setVisible(false);
34015         }
34016
34017         function toggle() {
34018             if (d3.event) d3.event.preventDefault();
34019             tooltip.hide(button);
34020             setVisible(!button.classed('active'));
34021         }
34022
34023         function setVisible(show) {
34024             if (show !== shown) {
34025                 button.classed('active', show);
34026                 shown = show;
34027
34028                 if (show) {
34029                     selection.on('mousedown.help-inside', function() {
34030                         return d3.event.stopPropagation();
34031                     });
34032                     pane.style('display', 'block')
34033                         .style('right', '-500px')
34034                         .transition()
34035                         .duration(200)
34036                         .style('right', '0px');
34037                 } else {
34038                     pane.style('right', '0px')
34039                         .transition()
34040                         .duration(200)
34041                         .style('right', '-500px')
34042                         .each('end', function() {
34043                             d3.select(this).style('display', 'none');
34044                         });
34045                     selection.on('mousedown.help-inside', null);
34046                 }
34047             }
34048         }
34049
34050         function clickHelp(d, i) {
34051             pane.property('scrollTop', 0);
34052             doctitle.html(d.title);
34053             body.html(d.html);
34054             body.selectAll('a')
34055                 .attr('target', '_blank');
34056             menuItems.classed('selected', function(m) {
34057                 return m.title === d.title;
34058             });
34059
34060             nav.html('');
34061
34062             if (i > 0) {
34063                 var prevLink = nav.append('a')
34064                     .attr('class', 'previous')
34065                     .on('click', function() {
34066                         clickHelp(docs[i - 1], i - 1);
34067                     });
34068                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
34069             }
34070             if (i < docs.length - 1) {
34071                 var nextLink = nav.append('a')
34072                     .attr('class', 'next')
34073                     .on('click', function() {
34074                         clickHelp(docs[i + 1], i + 1);
34075                     });
34076                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
34077             }
34078         }
34079
34080         function clickWalkthrough() {
34081             d3.select(document.body).call(iD.ui.intro(context));
34082             setVisible(false);
34083         }
34084
34085
34086         var pane = selection.append('div')
34087                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
34088             tooltip = bootstrap.tooltip()
34089                 .placement('left')
34090                 .html(true)
34091                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
34092             button = selection.append('button')
34093                 .attr('tabindex', -1)
34094                 .on('click', toggle)
34095                 .call(iD.svg.Icon('#icon-help', 'light'))
34096                 .call(tooltip),
34097             shown = false;
34098
34099
34100         var toc = pane.append('ul')
34101             .attr('class', 'toc');
34102
34103         var menuItems = toc.selectAll('li')
34104             .data(docs)
34105             .enter()
34106             .append('li')
34107             .append('a')
34108             .html(function(d) { return d.title; })
34109             .on('click', clickHelp);
34110
34111         toc.append('li')
34112             .attr('class','walkthrough')
34113             .append('a')
34114             .text(t('splash.walkthrough'))
34115             .on('click', clickWalkthrough);
34116
34117         var content = pane.append('div')
34118             .attr('class', 'left-content');
34119
34120         var doctitle = content.append('h2')
34121             .text(t('help.title'));
34122
34123         var body = content.append('div')
34124             .attr('class', 'body');
34125
34126         var nav = content.append('div')
34127             .attr('class', 'nav');
34128
34129         clickHelp(docs[0], 0);
34130
34131         var keybinding = d3.keybinding('help')
34132             .on(key, toggle)
34133             .on('B', hide)
34134             .on('F', hide);
34135
34136         d3.select(document)
34137             .call(keybinding);
34138
34139         context.surface().on('mousedown.help-outside', hide);
34140         context.container().on('mousedown.help-outside', hide);
34141     }
34142
34143     return help;
34144 };
34145 iD.ui.Info = function(context) {
34146     var key = iD.ui.cmd('⌘I'),
34147         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
34148         hidden = true;
34149
34150     function info(selection) {
34151         function radiansToMeters(r) {
34152             // using WGS84 authalic radius (6371007.1809 m)
34153             return r * 6371007.1809;
34154         }
34155
34156         function steradiansToSqmeters(r) {
34157             // http://gis.stackexchange.com/a/124857/40446
34158             return r / 12.56637 * 510065621724000;
34159         }
34160
34161         function toLineString(feature) {
34162             if (feature.type === 'LineString') return feature;
34163
34164             var result = { type: 'LineString', coordinates: [] };
34165             if (feature.type === 'Polygon') {
34166                 result.coordinates = feature.coordinates[0];
34167             } else if (feature.type === 'MultiPolygon') {
34168                 result.coordinates = feature.coordinates[0][0];
34169             }
34170
34171             return result;
34172         }
34173
34174         function displayLength(m) {
34175             var d = m * (imperial ? 3.28084 : 1),
34176                 p, unit;
34177
34178             if (imperial) {
34179                 if (d >= 5280) {
34180                     d /= 5280;
34181                     unit = 'mi';
34182                 } else {
34183                     unit = 'ft';
34184                 }
34185             } else {
34186                 if (d >= 1000) {
34187                     d /= 1000;
34188                     unit = 'km';
34189                 } else {
34190                     unit = 'm';
34191                 }
34192             }
34193
34194             // drop unnecessary precision
34195             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
34196
34197             return String(d.toFixed(p)) + ' ' + unit;
34198         }
34199
34200         function displayArea(m2) {
34201             var d = m2 * (imperial ? 10.7639111056 : 1),
34202                 d1, d2, p1, p2, unit1, unit2;
34203
34204             if (imperial) {
34205                 if (d >= 6969600) {     // > 0.25mi² show mi²
34206                     d1 = d / 27878400;
34207                     unit1 = 'mi²';
34208                 } else {
34209                     d1 = d;
34210                     unit1 = 'ft²';
34211                 }
34212
34213                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
34214                     d2 = d / 43560;
34215                     unit2 = 'ac';
34216                 }
34217
34218             } else {
34219                 if (d >= 250000) {    // > 0.25km² show km²
34220                     d1 = d / 1000000;
34221                     unit1 = 'km²';
34222                 } else {
34223                     d1 = d;
34224                     unit1 = 'm²';
34225                 }
34226
34227                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
34228                     d2 = d / 10000;
34229                     unit2 = 'ha';
34230                 }
34231             }
34232
34233             // drop unnecessary precision
34234             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
34235             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
34236
34237             return String(d1.toFixed(p1)) + ' ' + unit1 +
34238                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
34239         }
34240
34241
34242         function redraw() {
34243             if (hidden) return;
34244
34245             var resolver = context.graph(),
34246                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
34247                 singular = selected.length === 1 ? selected[0] : null,
34248                 extent = iD.geo.Extent(),
34249                 entity;
34250
34251             wrap.html('');
34252             wrap.append('h4')
34253                 .attr('class', 'infobox-heading fillD')
34254                 .text(singular || t('infobox.selected', { n: selected.length }));
34255
34256             if (!selected.length) return;
34257
34258             var center;
34259             for (var i = 0; i < selected.length; i++) {
34260                 entity = context.entity(selected[i]);
34261                 extent._extend(entity.extent(resolver));
34262             }
34263             center = extent.center();
34264
34265
34266             var list = wrap.append('ul');
34267
34268             // multiple wrap, just display extent center..
34269             if (!singular) {
34270                 list.append('li')
34271                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34272                 return;
34273             }
34274
34275             // single wrap, display details..
34276             if (!entity) return;
34277             var geometry = entity.geometry(resolver);
34278
34279             if (geometry === 'line' || geometry === 'area') {
34280                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
34281                     feature = entity.asGeoJSON(resolver),
34282                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
34283                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
34284                     centroid = d3.geo.centroid(feature);
34285
34286                 list.append('li')
34287                     .text(t('infobox.geometry') + ': ' +
34288                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
34289
34290                 if (closed) {
34291                     var area = steradiansToSqmeters(entity.area(resolver));
34292                     list.append('li')
34293                         .text(t('infobox.area') + ': ' + displayArea(area));
34294                 }
34295
34296                 list.append('li')
34297                     .text(lengthLabel + ': ' + displayLength(length));
34298
34299                 list.append('li')
34300                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
34301
34302
34303                 var toggle  = imperial ? 'imperial' : 'metric';
34304                 wrap.append('a')
34305                     .text(t('infobox.' + toggle))
34306                     .attr('href', '#')
34307                     .attr('class', 'button')
34308                     .on('click', function() {
34309                         d3.event.preventDefault();
34310                         imperial = !imperial;
34311                         redraw();
34312                     });
34313
34314             } else {
34315                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
34316
34317                 list.append('li')
34318                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
34319
34320                 list.append('li')
34321                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34322             }
34323         }
34324
34325
34326         function toggle() {
34327             if (d3.event) d3.event.preventDefault();
34328
34329             hidden = !hidden;
34330
34331             if (hidden) {
34332                 wrap
34333                     .style('display', 'block')
34334                     .style('opacity', 1)
34335                     .transition()
34336                     .duration(200)
34337                     .style('opacity', 0)
34338                     .each('end', function() {
34339                         d3.select(this).style('display', 'none');
34340                     });
34341             } else {
34342                 wrap
34343                     .style('display', 'block')
34344                     .style('opacity', 0)
34345                     .transition()
34346                     .duration(200)
34347                     .style('opacity', 1);
34348
34349                 redraw();
34350             }
34351         }
34352
34353
34354         var wrap = selection.selectAll('.infobox')
34355             .data([0]);
34356
34357         wrap.enter()
34358             .append('div')
34359             .attr('class', 'infobox fillD2')
34360             .style('display', (hidden ? 'none' : 'block'));
34361
34362         context.map()
34363             .on('drawn.info', redraw);
34364
34365         redraw();
34366
34367         var keybinding = d3.keybinding('info')
34368             .on(key, toggle);
34369
34370         d3.select(document)
34371             .call(keybinding);
34372     }
34373
34374     return info;
34375 };
34376 iD.ui.Inspector = function(context) {
34377     var presetList = iD.ui.PresetList(context),
34378         entityEditor = iD.ui.EntityEditor(context),
34379         state = 'select',
34380         entityID,
34381         newFeature = false;
34382
34383     function inspector(selection) {
34384         presetList
34385             .entityID(entityID)
34386             .autofocus(newFeature)
34387             .on('choose', setPreset);
34388
34389         entityEditor
34390             .state(state)
34391             .entityID(entityID)
34392             .on('choose', showList);
34393
34394         var $wrap = selection.selectAll('.panewrap')
34395             .data([0]);
34396
34397         var $enter = $wrap.enter().append('div')
34398             .attr('class', 'panewrap');
34399
34400         $enter.append('div')
34401             .attr('class', 'preset-list-pane pane');
34402
34403         $enter.append('div')
34404             .attr('class', 'entity-editor-pane pane');
34405
34406         var $presetPane = $wrap.select('.preset-list-pane');
34407         var $editorPane = $wrap.select('.entity-editor-pane');
34408
34409         var graph = context.graph(),
34410             entity = context.entity(entityID),
34411             showEditor = state === 'hover' ||
34412                 entity.isUsed(graph) ||
34413                 entity.isHighwayIntersection(graph);
34414
34415         if (showEditor) {
34416             $wrap.style('right', '0%');
34417             $editorPane.call(entityEditor);
34418         } else {
34419             $wrap.style('right', '-100%');
34420             $presetPane.call(presetList);
34421         }
34422
34423         var $footer = selection.selectAll('.footer')
34424             .data([0]);
34425
34426         $footer.enter().append('div')
34427             .attr('class', 'footer');
34428
34429         selection.select('.footer')
34430             .call(iD.ui.ViewOnOSM(context)
34431                 .entityID(entityID));
34432
34433         function showList(preset) {
34434             $wrap.transition()
34435                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
34436
34437             $presetPane.call(presetList
34438                 .preset(preset)
34439                 .autofocus(true));
34440         }
34441
34442         function setPreset(preset) {
34443             $wrap.transition()
34444                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
34445
34446             $editorPane.call(entityEditor
34447                 .preset(preset));
34448         }
34449     }
34450
34451     inspector.state = function(_) {
34452         if (!arguments.length) return state;
34453         state = _;
34454         entityEditor.state(state);
34455         return inspector;
34456     };
34457
34458     inspector.entityID = function(_) {
34459         if (!arguments.length) return entityID;
34460         entityID = _;
34461         return inspector;
34462     };
34463
34464     inspector.newFeature = function(_) {
34465         if (!arguments.length) return newFeature;
34466         newFeature = _;
34467         return inspector;
34468     };
34469
34470     return inspector;
34471 };
34472 iD.ui.intro = function(context) {
34473     var step;
34474
34475     function intro(selection) {
34476
34477         function localizedName(id) {
34478             var features = {
34479                 n2140018997: 'city_hall',
34480                 n367813436: 'fire_department',
34481                 w203988286: 'memory_isle_park',
34482                 w203972937: 'riverwalk_trail',
34483                 w203972938: 'riverwalk_trail',
34484                 w203972940: 'riverwalk_trail',
34485                 w41785752: 'w_michigan_ave',
34486                 w134150789: 'w_michigan_ave',
34487                 w134150795: 'w_michigan_ave',
34488                 w134150800: 'w_michigan_ave',
34489                 w134150811: 'w_michigan_ave',
34490                 w134150802: 'e_michigan_ave',
34491                 w134150836: 'e_michigan_ave',
34492                 w41074896: 'e_michigan_ave',
34493                 w17965834: 'spring_st',
34494                 w203986457: 'scidmore_park',
34495                 w203049587: 'petting_zoo',
34496                 w17967397: 'n_andrews_st',
34497                 w17967315: 's_andrews_st',
34498                 w17967326: 'n_constantine_st',
34499                 w17966400: 's_constantine_st',
34500                 w170848823: 'rocky_river',
34501                 w170848824: 'rocky_river',
34502                 w170848331: 'rocky_river',
34503                 w17967752: 'railroad_dr',
34504                 w17965998: 'conrail_rr',
34505                 w134150845: 'conrail_rr',
34506                 w170989131: 'st_joseph_river',
34507                 w143497377: 'n_main_st',
34508                 w134150801: 's_main_st',
34509                 w134150830: 's_main_st',
34510                 w17966462: 's_main_st',
34511                 w17967734: 'water_st',
34512                 w17964996: 'foster_st',
34513                 w170848330: 'portage_river',
34514                 w17965351: 'flower_st',
34515                 w17965502: 'elm_st',
34516                 w17965402: 'walnut_st',
34517                 w17964793: 'morris_ave',
34518                 w17967444: 'east_st',
34519                 w17966984: 'portage_ave'
34520             };
34521             return features[id] && t('intro.graph.' + features[id]);
34522         }
34523
34524         context.enter(iD.modes.Browse(context));
34525
34526         // Save current map state
34527         var history = context.history().toJSON(),
34528             hash = window.location.hash,
34529             center = context.map().center(),
34530             zoom = context.map().zoom(),
34531             background = context.background().baseLayerSource(),
34532             opacity = d3.select('.background-layer').style('opacity'),
34533             loadedTiles = context.connection().loadedTiles(),
34534             baseEntities = context.history().graph().base().entities,
34535             introGraph, name;
34536
34537         // Block saving
34538         context.inIntro(true);
34539
34540         // Load semi-real data used in intro
34541         context.connection().toggle(false).flush();
34542         context.history().reset();
34543
34544         introGraph = JSON.parse(iD.introGraph);
34545         for (var key in introGraph) {
34546             introGraph[key] = iD.Entity(introGraph[key]);
34547             name = localizedName(key);
34548             if (name) {
34549                 introGraph[key].tags.name = name;
34550             }
34551         }
34552         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
34553         context.background().bing();
34554
34555         d3.select('.background-layer').style('opacity', 1);
34556
34557         var curtain = d3.curtain();
34558         selection.call(curtain);
34559
34560         function reveal(box, text, options) {
34561             options = options || {};
34562             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
34563             else curtain.reveal(box, '', '', options.duration);
34564         }
34565
34566         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
34567             var s = iD.ui.intro[step](context, reveal)
34568                 .on('done', function() {
34569                     entered.filter(function(d) {
34570                         return d.title === s.title;
34571                     }).classed('finished', true);
34572                     enter(steps[i + 1]);
34573                 });
34574             return s;
34575         });
34576
34577         steps[steps.length - 1].on('startEditing', function() {
34578             curtain.remove();
34579             navwrap.remove();
34580             d3.select('.background-layer').style('opacity', opacity);
34581             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
34582             context.history().reset().merge(d3.values(baseEntities));
34583             context.background().baseLayerSource(background);
34584             if (history) context.history().fromJSON(history, false);
34585             context.map().centerZoom(center, zoom);
34586             window.location.replace(hash);
34587             context.inIntro(false);
34588         });
34589
34590         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
34591
34592         var buttonwrap = navwrap.append('div')
34593             .attr('class', 'joined')
34594             .selectAll('button.step');
34595
34596         var entered = buttonwrap
34597             .data(steps)
34598             .enter()
34599             .append('button')
34600             .attr('class', 'step')
34601             .on('click', enter);
34602
34603         entered
34604             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
34605
34606         entered
34607             .append('label')
34608             .text(function(d) { return t(d.title); });
34609
34610         enter(steps[0]);
34611
34612         function enter (newStep) {
34613             if (step) { step.exit(); }
34614
34615             context.enter(iD.modes.Browse(context));
34616
34617             step = newStep;
34618             step.enter();
34619
34620             entered.classed('active', function(d) {
34621                 return d.title === step.title;
34622             });
34623         }
34624
34625     }
34626     return intro;
34627 };
34628
34629 iD.ui.intro.pointBox = function(point, context) {
34630     var rect = context.surfaceRect();
34631     point = context.projection(point);
34632     return {
34633         left: point[0] + rect.left - 30,
34634         top: point[1] + rect.top - 50,
34635         width: 60,
34636         height: 70
34637     };
34638 };
34639
34640 iD.ui.intro.pad = function(box, padding, context) {
34641     if (box instanceof Array) {
34642         var rect = context.surfaceRect();
34643         box = context.projection(box);
34644         box = {
34645             left: box[0] + rect.left,
34646             top: box[1] + rect.top
34647         };
34648     }
34649     return {
34650         left: box.left - padding,
34651         top: box.top - padding,
34652         width: (box.width || 0) + 2 * padding,
34653         height: (box.width || 0) + 2 * padding
34654     };
34655 };
34656
34657 iD.ui.intro.icon = function(name, svgklass) {
34658     return '<svg class="icon ' + (svgklass || '') + '">' +
34659         '<use xlink:href="' + name + '"></use></svg>';
34660 };
34661 iD.ui.Lasso = function(context) {
34662
34663     var box, group,
34664         a = [0, 0],
34665         b = [0, 0];
34666
34667     function lasso(selection) {
34668
34669         context.container().classed('lasso', true);
34670
34671         group = selection.append('g')
34672             .attr('class', 'lasso hide');
34673
34674         box = group.append('rect')
34675             .attr('class', 'lasso-box');
34676
34677         group.call(iD.ui.Toggle(true));
34678
34679     }
34680
34681     // top-left
34682     function topLeft(d) {
34683         return 'translate(' + Math.min(d[0][0], d[1][0]) + ',' + Math.min(d[0][1], d[1][1]) + ')';
34684     }
34685
34686     function width(d) { return Math.abs(d[0][0] - d[1][0]); }
34687     function height(d) { return Math.abs(d[0][1] - d[1][1]); }
34688
34689     function draw() {
34690         if (box) {
34691             box.data([[a, b]])
34692                 .attr('transform', topLeft)
34693                 .attr('width', width)
34694                 .attr('height', height);
34695         }
34696     }
34697
34698     lasso.a = function(_) {
34699         if (!arguments.length) return a;
34700         a = _;
34701         draw();
34702         return lasso;
34703     };
34704
34705     lasso.b = function(_) {
34706         if (!arguments.length) return b;
34707         b = _;
34708         draw();
34709         return lasso;
34710     };
34711
34712     lasso.close = function() {
34713         if (group) {
34714             group.call(iD.ui.Toggle(false, function() {
34715                 d3.select(this).remove();
34716             }));
34717         }
34718         context.container().classed('lasso', false);
34719     };
34720
34721     return lasso;
34722 };
34723 iD.ui.Loading = function(context) {
34724     var message = '',
34725         blocking = false,
34726         modal;
34727
34728     var loading = function(selection) {
34729         modal = iD.ui.modal(selection, blocking);
34730
34731         var loadertext = modal.select('.content')
34732             .classed('loading-modal', true)
34733             .append('div')
34734             .attr('class', 'modal-section fillL');
34735
34736         loadertext.append('img')
34737             .attr('class', 'loader')
34738             .attr('src', context.imagePath('loader-white.gif'));
34739
34740         loadertext.append('h3')
34741             .text(message);
34742
34743         modal.select('button.close')
34744             .attr('class', 'hide');
34745
34746         return loading;
34747     };
34748
34749     loading.message = function(_) {
34750         if (!arguments.length) return message;
34751         message = _;
34752         return loading;
34753     };
34754
34755     loading.blocking = function(_) {
34756         if (!arguments.length) return blocking;
34757         blocking = _;
34758         return loading;
34759     };
34760
34761     loading.close = function() {
34762         modal.remove();
34763     };
34764
34765     return loading;
34766 };
34767 iD.ui.MapData = function(context) {
34768     var key = 'F',
34769         features = context.features().keys(),
34770         layers = context.layers(),
34771         fills = ['wireframe', 'partial', 'full'],
34772         fillDefault = context.storage('area-fill') || 'partial',
34773         fillSelected = fillDefault;
34774
34775
34776     function map_data(selection) {
34777
34778         function showsFeature(d) {
34779             return context.features().enabled(d);
34780         }
34781
34782         function autoHiddenFeature(d) {
34783             return context.features().autoHidden(d);
34784         }
34785
34786         function clickFeature(d) {
34787             context.features().toggle(d);
34788             update();
34789         }
34790
34791         function showsFill(d) {
34792             return fillSelected === d;
34793         }
34794
34795         function setFill(d) {
34796             _.each(fills, function(opt) {
34797                 context.surface().classed('fill-' + opt, Boolean(opt === d));
34798             });
34799
34800             fillSelected = d;
34801             if (d !== 'wireframe') {
34802                 fillDefault = d;
34803                 context.storage('area-fill', d);
34804             }
34805             update();
34806         }
34807
34808         function toggleLayer(which) {
34809             var layer = layers.layer(which);
34810             if (layer) {
34811                 layer.enabled(!layer.enabled());
34812                 update();
34813             }
34814         }
34815
34816         function clickGpx() {
34817             toggleLayer('gpx');
34818         }
34819
34820         function clickMapillaryImages() {
34821             toggleLayer('mapillary-images');
34822         }
34823
34824         function clickMapillarySigns() {
34825             toggleLayer('mapillary-signs');
34826         }
34827
34828
34829         function drawMapillaryItems(selection) {
34830             var mapillaryImages = layers.layer('mapillary-images'),
34831                 mapillarySigns = layers.layer('mapillary-signs'),
34832                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
34833                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
34834                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
34835                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
34836
34837             var mapillaryList = selection
34838                 .selectAll('.layer-list-mapillary')
34839                 .data([0]);
34840
34841             // Enter
34842             mapillaryList
34843                 .enter()
34844                 .append('ul')
34845                 .attr('class', 'layer-list layer-list-mapillary');
34846
34847             var mapillaryImageLayerItem = mapillaryList
34848                 .selectAll('.list-item-mapillary-images')
34849                 .data(supportsMapillaryImages ? [0] : []);
34850
34851             var enterImages = mapillaryImageLayerItem.enter()
34852                 .append('li')
34853                 .attr('class', 'list-item-mapillary-images');
34854
34855             var labelImages = enterImages.append('label')
34856                 .call(bootstrap.tooltip()
34857                     .title(t('mapillary_images.tooltip'))
34858                     .placement('top'));
34859
34860             labelImages.append('input')
34861                 .attr('type', 'checkbox')
34862                 .on('change', clickMapillaryImages);
34863
34864             labelImages.append('span')
34865                 .text(t('mapillary_images.title'));
34866
34867
34868             var mapillarySignLayerItem = mapillaryList
34869                 .selectAll('.list-item-mapillary-signs')
34870                 .data(supportsMapillarySigns ? [0] : []);
34871
34872             var enterSigns = mapillarySignLayerItem.enter()
34873                 .append('li')
34874                 .attr('class', 'list-item-mapillary-signs');
34875
34876             var labelSigns = enterSigns.append('label')
34877                 .call(bootstrap.tooltip()
34878                     .title(t('mapillary_signs.tooltip'))
34879                     .placement('top'));
34880
34881             labelSigns.append('input')
34882                 .attr('type', 'checkbox')
34883                 .on('change', clickMapillarySigns);
34884
34885             labelSigns.append('span')
34886                 .text(t('mapillary_signs.title'));
34887
34888             // Update
34889             mapillaryImageLayerItem
34890                 .classed('active', showsMapillaryImages)
34891                 .selectAll('input')
34892                 .property('checked', showsMapillaryImages);
34893
34894             mapillarySignLayerItem
34895                 .classed('active', showsMapillarySigns)
34896                 .selectAll('input')
34897                 .property('checked', showsMapillarySigns);
34898
34899             // Exit
34900             mapillaryImageLayerItem.exit()
34901                 .remove();
34902             mapillarySignLayerItem.exit()
34903                 .remove();
34904         }
34905
34906
34907         function drawGpxItem(selection) {
34908             var gpx = layers.layer('gpx'),
34909                 hasGpx = gpx && gpx.hasGpx(),
34910                 showsGpx = hasGpx && gpx.enabled();
34911
34912             var gpxLayerItem = selection
34913                 .selectAll('.layer-list-gpx')
34914                 .data(gpx ? [0] : []);
34915
34916             // Enter
34917             var enter = gpxLayerItem.enter()
34918                 .append('ul')
34919                 .attr('class', 'layer-list layer-list-gpx')
34920                 .append('li')
34921                 .classed('list-item-gpx', true);
34922
34923             enter.append('button')
34924                 .attr('class', 'list-item-gpx-extent')
34925                 .call(bootstrap.tooltip()
34926                     .title(t('gpx.zoom'))
34927                     .placement('left'))
34928                 .on('click', function() {
34929                     d3.event.preventDefault();
34930                     d3.event.stopPropagation();
34931                     gpx.fitZoom();
34932                 })
34933                 .call(iD.svg.Icon('#icon-search'));
34934
34935             enter.append('button')
34936                 .attr('class', 'list-item-gpx-browse')
34937                 .call(bootstrap.tooltip()
34938                     .title(t('gpx.browse'))
34939                     .placement('left'))
34940                 .on('click', function() {
34941                     d3.select(document.createElement('input'))
34942                         .attr('type', 'file')
34943                         .on('change', function() {
34944                             gpx.files(d3.event.target.files);
34945                         })
34946                         .node().click();
34947                 })
34948                 .call(iD.svg.Icon('#icon-geolocate'));
34949
34950             var labelGpx = enter.append('label')
34951                 .call(bootstrap.tooltip()
34952                     .title(t('gpx.drag_drop'))
34953                     .placement('top'));
34954
34955             labelGpx.append('input')
34956                 .attr('type', 'checkbox')
34957                 .on('change', clickGpx);
34958
34959             labelGpx.append('span')
34960                 .text(t('gpx.local_layer'));
34961
34962             // Update
34963             gpxLayerItem
34964                 .classed('active', showsGpx)
34965                 .selectAll('input')
34966                 .property('disabled', !hasGpx)
34967                 .property('checked', showsGpx);
34968
34969             // Exit
34970             gpxLayerItem.exit()
34971                 .remove();
34972         }
34973
34974
34975         function drawList(selection, data, type, name, change, active) {
34976             var items = selection.selectAll('li')
34977                 .data(data);
34978
34979             // Enter
34980             var enter = items.enter()
34981                 .append('li')
34982                 .attr('class', 'layer')
34983                 .call(bootstrap.tooltip()
34984                     .html(true)
34985                     .title(function(d) {
34986                         var tip = t(name + '.' + d + '.tooltip'),
34987                             key = (d === 'wireframe' ? 'W' : null);
34988
34989                         if (name === 'feature' && autoHiddenFeature(d)) {
34990                             tip += '<div>' + t('map_data.autohidden') + '</div>';
34991                         }
34992                         return iD.ui.tooltipHtml(tip, key);
34993                     })
34994                     .placement('top')
34995                 );
34996
34997             var label = enter.append('label');
34998
34999             label.append('input')
35000                 .attr('type', type)
35001                 .attr('name', name)
35002                 .on('change', change);
35003
35004             label.append('span')
35005                 .text(function(d) { return t(name + '.' + d + '.description'); });
35006
35007             // Update
35008             items
35009                 .classed('active', active)
35010                 .selectAll('input')
35011                 .property('checked', active)
35012                 .property('indeterminate', function(d) {
35013                     return (name === 'feature' && autoHiddenFeature(d));
35014                 });
35015
35016             // Exit
35017             items.exit()
35018                 .remove();
35019         }
35020
35021
35022         function update() {
35023             dataLayerContainer.call(drawMapillaryItems);
35024             dataLayerContainer.call(drawGpxItem);
35025
35026             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
35027
35028             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
35029         }
35030
35031         function hidePanel() {
35032             setVisible(false);
35033         }
35034
35035         function togglePanel() {
35036             if (d3.event) d3.event.preventDefault();
35037             tooltip.hide(button);
35038             setVisible(!button.classed('active'));
35039         }
35040
35041         function toggleWireframe() {
35042             if (d3.event) {
35043                 d3.event.preventDefault();
35044                 d3.event.stopPropagation();
35045             }
35046             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
35047             context.map().pan([0,0]);  // trigger a redraw
35048         }
35049
35050         function setVisible(show) {
35051             if (show !== shown) {
35052                 button.classed('active', show);
35053                 shown = show;
35054
35055                 if (show) {
35056                     update();
35057                     selection.on('mousedown.map_data-inside', function() {
35058                         return d3.event.stopPropagation();
35059                     });
35060                     content.style('display', 'block')
35061                         .style('right', '-300px')
35062                         .transition()
35063                         .duration(200)
35064                         .style('right', '0px');
35065                 } else {
35066                     content.style('display', 'block')
35067                         .style('right', '0px')
35068                         .transition()
35069                         .duration(200)
35070                         .style('right', '-300px')
35071                         .each('end', function() {
35072                             d3.select(this).style('display', 'none');
35073                         });
35074                     selection.on('mousedown.map_data-inside', null);
35075                 }
35076             }
35077         }
35078
35079
35080         var content = selection.append('div')
35081                 .attr('class', 'fillL map-overlay col3 content hide'),
35082             tooltip = bootstrap.tooltip()
35083                 .placement('left')
35084                 .html(true)
35085                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
35086             button = selection.append('button')
35087                 .attr('tabindex', -1)
35088                 .on('click', togglePanel)
35089                 .call(iD.svg.Icon('#icon-data', 'light'))
35090                 .call(tooltip),
35091             shown = false;
35092
35093         content.append('h4')
35094             .text(t('map_data.title'));
35095
35096
35097         // data layers
35098         content.append('a')
35099             .text(t('map_data.data_layers'))
35100             .attr('href', '#')
35101             .classed('hide-toggle', true)
35102             .classed('expanded', true)
35103             .on('click', function() {
35104                 var exp = d3.select(this).classed('expanded');
35105                 dataLayerContainer.style('display', exp ? 'none' : 'block');
35106                 d3.select(this).classed('expanded', !exp);
35107                 d3.event.preventDefault();
35108             });
35109
35110         var dataLayerContainer = content.append('div')
35111             .attr('class', 'data-data-layers')
35112             .style('display', 'block');
35113
35114
35115         // area fills
35116         content.append('a')
35117             .text(t('map_data.fill_area'))
35118             .attr('href', '#')
35119             .classed('hide-toggle', true)
35120             .classed('expanded', false)
35121             .on('click', function() {
35122                 var exp = d3.select(this).classed('expanded');
35123                 fillContainer.style('display', exp ? 'none' : 'block');
35124                 d3.select(this).classed('expanded', !exp);
35125                 d3.event.preventDefault();
35126             });
35127
35128         var fillContainer = content.append('div')
35129             .attr('class', 'data-area-fills')
35130             .style('display', 'none');
35131
35132         var fillList = fillContainer.append('ul')
35133             .attr('class', 'layer-list layer-fill-list');
35134
35135
35136         // feature filters
35137         content.append('a')
35138             .text(t('map_data.map_features'))
35139             .attr('href', '#')
35140             .classed('hide-toggle', true)
35141             .classed('expanded', false)
35142             .on('click', function() {
35143                 var exp = d3.select(this).classed('expanded');
35144                 featureContainer.style('display', exp ? 'none' : 'block');
35145                 d3.select(this).classed('expanded', !exp);
35146                 d3.event.preventDefault();
35147             });
35148
35149         var featureContainer = content.append('div')
35150             .attr('class', 'data-feature-filters')
35151             .style('display', 'none');
35152
35153         var featureList = featureContainer.append('ul')
35154             .attr('class', 'layer-list layer-feature-list');
35155
35156
35157         context.features()
35158             .on('change.map_data-update', update);
35159
35160         setFill(fillDefault);
35161
35162         var keybinding = d3.keybinding('features')
35163             .on(key, togglePanel)
35164             .on('W', toggleWireframe)
35165             .on('B', hidePanel)
35166             .on('H', hidePanel);
35167
35168         d3.select(document)
35169             .call(keybinding);
35170
35171         context.surface().on('mousedown.map_data-outside', hidePanel);
35172         context.container().on('mousedown.map_data-outside', hidePanel);
35173     }
35174
35175     return map_data;
35176 };
35177 iD.ui.MapInMap = function(context) {
35178     var key = '/';
35179
35180     function map_in_map(selection) {
35181         var backgroundLayer = iD.TileLayer(),
35182             overlayLayers = {},
35183             projection = iD.geo.RawMercator(),
35184             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
35185             zoom = d3.behavior.zoom()
35186                 .scaleExtent([ztok(0.5), ztok(24)])
35187                 .on('zoom', zoomPan),
35188             transformed = false,
35189             panning = false,
35190             hidden = true,
35191             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
35192             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
35193
35194         function ztok(z) { return 256 * Math.pow(2, z); }
35195         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
35196
35197
35198         function startMouse() {
35199             context.surface().on('mouseup.map-in-map-outside', endMouse);
35200             context.container().on('mouseup.map-in-map-outside', endMouse);
35201
35202             tStart = tLast = tCurr = projection.translate();
35203             panning = true;
35204         }
35205
35206
35207         function zoomPan() {
35208             var e = d3.event.sourceEvent,
35209                 t = d3.event.translate,
35210                 k = d3.event.scale,
35211                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
35212                 zMini = ktoz(k);
35213
35214             // restrict minimap zoom to < (main zoom - 3)
35215             if (zMini > zMain - 3) {
35216                 zMini = zMain - 3;
35217                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
35218                 return;
35219             }
35220
35221             tCurr = t;
35222             kCurr = k;
35223             zDiff = zMain - zMini;
35224
35225             var scale = kCurr / kLast,
35226                 tX = (tCurr[0] / scale - tLast[0]) * scale,
35227                 tY = (tCurr[1] / scale - tLast[1]) * scale;
35228
35229             iD.util.setTransform(tiles, tX, tY, scale);
35230             iD.util.setTransform(svg, 0, 0, scale);
35231             transformed = true;
35232
35233             queueRedraw();
35234
35235             e.preventDefault();
35236             e.stopPropagation();
35237         }
35238
35239
35240         function endMouse() {
35241             context.surface().on('mouseup.map-in-map-outside', null);
35242             context.container().on('mouseup.map-in-map-outside', null);
35243
35244             updateProjection();
35245             panning = false;
35246
35247             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
35248                 var dMini = wrap.dimensions(),
35249                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
35250
35251                 context.map().center(projection.invert(cMini));
35252             }
35253         }
35254
35255
35256         function updateProjection() {
35257             var loc = context.map().center(),
35258                 dMini = wrap.dimensions(),
35259                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
35260                 tMain = context.projection.translate(),
35261                 kMain = context.projection.scale(),
35262                 zMain = ktoz(kMain * 2 * Math.PI),
35263                 zMini = Math.max(zMain - zDiff, 0.5),
35264                 kMini = ztok(zMini);
35265
35266             projection
35267                 .translate(tMain)
35268                 .scale(kMini / (2 * Math.PI));
35269
35270             var s = projection(loc),
35271                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
35272                 tMini = [
35273                     cMini[0] - s[0] + tMain[0] + mouse[0],
35274                     cMini[1] - s[1] + tMain[1] + mouse[1]
35275                 ];
35276
35277             projection
35278                 .translate(tMini)
35279                 .clipExtent([[0, 0], dMini]);
35280
35281             zoom
35282                 .center(cMini)
35283                 .translate(tMini)
35284                 .scale(kMini);
35285
35286             tLast = tCurr = tMini;
35287             kLast = kCurr = kMini;
35288
35289             if (transformed) {
35290                 iD.util.setTransform(tiles, 0, 0);
35291                 iD.util.setTransform(svg, 0, 0);
35292                 transformed = false;
35293             }
35294         }
35295
35296
35297         function redraw() {
35298             if (hidden) return;
35299
35300             updateProjection();
35301
35302             var dMini = wrap.dimensions(),
35303                 zMini = ktoz(projection.scale() * 2 * Math.PI);
35304
35305             // setup tile container
35306             tiles = wrap
35307                 .selectAll('.map-in-map-tiles')
35308                 .data([0]);
35309
35310             tiles
35311                 .enter()
35312                 .append('div')
35313                 .attr('class', 'map-in-map-tiles');
35314
35315             // redraw background
35316             backgroundLayer
35317                 .source(context.background().baseLayerSource())
35318                 .projection(projection)
35319                 .dimensions(dMini);
35320
35321             var background = tiles
35322                 .selectAll('.map-in-map-background')
35323                 .data([0]);
35324
35325             background.enter()
35326                 .append('div')
35327                 .attr('class', 'map-in-map-background');
35328
35329             background
35330                 .call(backgroundLayer);
35331
35332
35333             // redraw overlay
35334             var overlaySources = context.background().overlayLayerSources();
35335             var activeOverlayLayers = [];
35336             for (var i = 0; i < overlaySources.length; i++) {
35337                 if (overlaySources[i].validZoom(zMini)) {
35338                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
35339                     activeOverlayLayers.push(overlayLayers[i]
35340                         .source(overlaySources[i])
35341                         .projection(projection)
35342                         .dimensions(dMini));
35343                 }
35344             }
35345
35346             var overlay = tiles
35347                 .selectAll('.map-in-map-overlay')
35348                 .data([0]);
35349
35350             overlay.enter()
35351                 .append('div')
35352                 .attr('class', 'map-in-map-overlay');
35353
35354             var overlays = overlay
35355                 .selectAll('div')
35356                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
35357
35358             overlays.enter().append('div');
35359             overlays.each(function(layer) {
35360                 d3.select(this).call(layer);
35361             });
35362
35363             overlays.exit()
35364                 .remove();
35365
35366
35367             var gpx = tiles
35368                 .selectAll('.map-in-map-gpx')
35369                 .data(gpxLayer.enabled() ? [0] : []);
35370
35371             gpx.enter()
35372                 .append('svg')
35373                 .attr('class', 'map-in-map-gpx');
35374
35375             gpx.exit()
35376                 .remove();
35377
35378             gpx.call(gpxLayer);
35379
35380
35381             // redraw bounding box
35382             if (!panning) {
35383                 var getPath = d3.geo.path().projection(projection),
35384                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
35385
35386                 svg = wrap.selectAll('.map-in-map-svg')
35387                     .data([0]);
35388
35389                 svg.enter()
35390                     .append('svg')
35391                     .attr('class', 'map-in-map-svg');
35392
35393                 var path = svg.selectAll('.map-in-map-bbox')
35394                     .data([bbox]);
35395
35396                 path.enter()
35397                     .append('path')
35398                     .attr('class', 'map-in-map-bbox');
35399
35400                 path
35401                     .attr('d', getPath)
35402                     .classed('thick', function(d) { return getPath.area(d) < 30; });
35403             }
35404         }
35405
35406
35407         function queueRedraw() {
35408             clearTimeout(timeoutId);
35409             timeoutId = setTimeout(function() { redraw(); }, 300);
35410         }
35411
35412
35413         function toggle() {
35414             if (d3.event) d3.event.preventDefault();
35415
35416             hidden = !hidden;
35417
35418             var label = d3.select('.minimap-toggle');
35419             label.classed('active', !hidden)
35420                 .select('input').property('checked', !hidden);
35421
35422             if (hidden) {
35423                 wrap
35424                     .style('display', 'block')
35425                     .style('opacity', 1)
35426                     .transition()
35427                     .duration(200)
35428                     .style('opacity', 0)
35429                     .each('end', function() {
35430                         d3.select(this).style('display', 'none');
35431                     });
35432             } else {
35433                 wrap
35434                     .style('display', 'block')
35435                     .style('opacity', 0)
35436                     .transition()
35437                     .duration(200)
35438                     .style('opacity', 1);
35439
35440                 redraw();
35441             }
35442         }
35443
35444         iD.ui.MapInMap.toggle = toggle;
35445
35446         var wrap = selection.selectAll('.map-in-map')
35447             .data([0]);
35448
35449         wrap.enter()
35450             .append('div')
35451             .attr('class', 'map-in-map')
35452             .style('display', (hidden ? 'none' : 'block'))
35453             .on('mousedown.map-in-map', startMouse)
35454             .on('mouseup.map-in-map', endMouse)
35455             .call(zoom)
35456             .on('dblclick.zoom', null);
35457
35458         context.map()
35459             .on('drawn.map-in-map', function(drawn) {
35460                 if (drawn.full === true) redraw();
35461             });
35462
35463         redraw();
35464
35465         var keybinding = d3.keybinding('map-in-map')
35466             .on(key, toggle);
35467
35468         d3.select(document)
35469             .call(keybinding);
35470     }
35471
35472     return map_in_map;
35473 };
35474 iD.ui.modal = function(selection, blocking) {
35475
35476     var previous = selection.select('div.modal');
35477     var animate = previous.empty();
35478
35479     previous.transition()
35480         .duration(200)
35481         .style('opacity', 0)
35482         .remove();
35483
35484     var shaded = selection
35485         .append('div')
35486         .attr('class', 'shaded')
35487         .style('opacity', 0);
35488
35489     shaded.close = function() {
35490         shaded
35491             .transition()
35492             .duration(200)
35493             .style('opacity',0)
35494             .remove();
35495         modal
35496             .transition()
35497             .duration(200)
35498             .style('top','0px');
35499         keybinding.off();
35500     };
35501
35502     var keybinding = d3.keybinding('modal')
35503         .on('⌫', shaded.close)
35504         .on('⎋', shaded.close);
35505
35506     d3.select(document).call(keybinding);
35507
35508     var modal = shaded.append('div')
35509         .attr('class', 'modal fillL col6');
35510
35511         shaded.on('click.remove-modal', function() {
35512             if (d3.event.target === this && !blocking) shaded.close();
35513         });
35514
35515     modal.append('button')
35516         .attr('class', 'close')
35517         .on('click', function() {
35518             if (!blocking) shaded.close();
35519         })
35520         .append('div')
35521             .attr('class','icon close');
35522
35523     modal.append('div')
35524         .attr('class', 'content');
35525
35526     if (animate) {
35527         shaded.transition().style('opacity', 1);
35528     } else {
35529         shaded.style('opacity', 1);
35530     }
35531
35532     return shaded;
35533 };
35534 iD.ui.Modes = function(context) {
35535     var modes = [
35536         iD.modes.AddPoint(context),
35537         iD.modes.AddLine(context),
35538         iD.modes.AddArea(context)];
35539
35540     function editable() {
35541         return context.editable() && context.mode().id !== 'save';
35542     }
35543
35544     return function(selection) {
35545         var buttons = selection.selectAll('button.add-button')
35546             .data(modes);
35547
35548        buttons.enter().append('button')
35549            .attr('tabindex', -1)
35550            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
35551            .on('click.mode-buttons', function(mode) {
35552                if (mode.id === context.mode().id) {
35553                    context.enter(iD.modes.Browse(context));
35554                } else {
35555                    context.enter(mode);
35556                }
35557            })
35558            .call(bootstrap.tooltip()
35559                .placement('bottom')
35560                .html(true)
35561                .title(function(mode) {
35562                    return iD.ui.tooltipHtml(mode.description, mode.key);
35563                }));
35564
35565         context.map()
35566             .on('move.modes', _.debounce(update, 500));
35567
35568         context
35569             .on('enter.modes', update);
35570
35571         buttons.each(function(d) {
35572             d3.select(this)
35573                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
35574         });
35575
35576         buttons.append('span')
35577             .attr('class', 'label')
35578             .text(function(mode) { return mode.title; });
35579
35580         context.on('enter.editor', function(entered) {
35581             buttons.classed('active', function(mode) { return entered.button === mode.button; });
35582             context.container()
35583                 .classed('mode-' + entered.id, true);
35584         });
35585
35586         context.on('exit.editor', function(exited) {
35587             context.container()
35588                 .classed('mode-' + exited.id, false);
35589         });
35590
35591         var keybinding = d3.keybinding('mode-buttons');
35592
35593         modes.forEach(function(m) {
35594             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
35595         });
35596
35597         d3.select(document)
35598             .call(keybinding);
35599
35600         function update() {
35601             buttons.property('disabled', !editable());
35602         }
35603     };
35604 };
35605 iD.ui.Notice = function(context) {
35606     return function(selection) {
35607         var div = selection.append('div')
35608             .attr('class', 'notice');
35609
35610         var button = div.append('button')
35611             .attr('class', 'zoom-to notice')
35612             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
35613
35614         button
35615             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
35616             .append('span')
35617             .attr('class', 'label')
35618             .text(t('zoom_in_edit'));
35619
35620         function disableTooHigh() {
35621             div.style('display', context.editable() ? 'none' : 'block');
35622         }
35623
35624         context.map()
35625             .on('move.notice', _.debounce(disableTooHigh, 500));
35626
35627         disableTooHigh();
35628     };
35629 };
35630 iD.ui.preset = function(context) {
35631     var event = d3.dispatch('change'),
35632         state,
35633         fields,
35634         preset,
35635         tags,
35636         id;
35637
35638     function UIField(field, entity, show) {
35639         field = _.clone(field);
35640
35641         field.input = iD.ui.preset[field.type](field, context)
35642             .on('change', event.change);
35643
35644         if (field.input.entity) field.input.entity(entity);
35645
35646         field.keys = field.keys || [field.key];
35647
35648         field.show = show;
35649
35650         field.shown = function() {
35651             return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
35652         };
35653
35654         field.modified = function() {
35655             var original = context.graph().base().entities[entity.id];
35656             return _.any(field.keys, function(key) {
35657                 return original ? tags[key] !== original.tags[key] : tags[key];
35658             });
35659         };
35660
35661         field.revert = function() {
35662             var original = context.graph().base().entities[entity.id],
35663                 t = {};
35664             field.keys.forEach(function(key) {
35665                 t[key] = original ? original.tags[key] : undefined;
35666             });
35667             return t;
35668         };
35669
35670         field.present = function() {
35671             return _.any(field.keys, function(key) {
35672                 return tags[key];
35673             });
35674         };
35675
35676         field.remove = function() {
35677             var t = {};
35678             field.keys.forEach(function(key) {
35679                 t[key] = undefined;
35680             });
35681             return t;
35682         };
35683
35684         return field;
35685     }
35686
35687     function fieldKey(field) {
35688         return field.id;
35689     }
35690
35691     function presets(selection) {
35692         selection.call(iD.ui.Disclosure()
35693             .title(t('inspector.all_fields'))
35694             .expanded(context.storage('preset_fields.expanded') !== 'false')
35695             .on('toggled', toggled)
35696             .content(content));
35697
35698         function toggled(expanded) {
35699             context.storage('preset_fields.expanded', expanded);
35700         }
35701     }
35702
35703     function content(selection) {
35704         if (!fields) {
35705             var entity = context.entity(id),
35706                 geometry = context.geometry(id);
35707
35708             fields = [UIField(context.presets().field('name'), entity)];
35709
35710             preset.fields.forEach(function(field) {
35711                 if (field.matchGeometry(geometry)) {
35712                     fields.push(UIField(field, entity, true));
35713                 }
35714             });
35715
35716             if (entity.isHighwayIntersection(context.graph())) {
35717                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
35718             }
35719
35720             context.presets().universal().forEach(function(field) {
35721                 if (preset.fields.indexOf(field) < 0) {
35722                     fields.push(UIField(field, entity));
35723                 }
35724             });
35725         }
35726
35727         var shown = fields.filter(function(field) { return field.shown(); }),
35728             notShown = fields.filter(function(field) { return !field.shown(); });
35729
35730         var $form = selection.selectAll('.preset-form')
35731             .data([0]);
35732
35733         $form.enter().append('div')
35734             .attr('class', 'preset-form inspector-inner fillL3');
35735
35736         var $fields = $form.selectAll('.form-field')
35737             .data(shown, fieldKey);
35738
35739         // Enter
35740
35741         var $enter = $fields.enter()
35742             .append('div')
35743             .attr('class', function(field) {
35744                 return 'form-field form-field-' + field.id;
35745             });
35746
35747         var $label = $enter.append('label')
35748             .attr('class', 'form-label')
35749             .attr('for', function(field) { return 'preset-input-' + field.id; })
35750             .text(function(field) { return field.label(); });
35751
35752         var wrap = $label.append('div')
35753             .attr('class', 'form-label-button-wrap');
35754
35755         wrap.append('button')
35756             .attr('class', 'remove-icon')
35757             .call(iD.svg.Icon('#operation-delete'));
35758
35759         wrap.append('button')
35760             .attr('class', 'modified-icon')
35761             .attr('tabindex', -1)
35762             .call(iD.svg.Icon('#icon-undo'));
35763
35764         // Update
35765
35766         $fields.select('.form-label-button-wrap .remove-icon')
35767             .on('click', remove);
35768
35769         $fields.select('.modified-icon')
35770             .on('click', revert);
35771
35772         $fields
35773             .order()
35774             .classed('modified', function(field) {
35775                 return field.modified();
35776             })
35777             .classed('present', function(field) {
35778                 return field.present();
35779             })
35780             .each(function(field) {
35781                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
35782
35783                 if (state === 'hover') {
35784                     reference.showing(false);
35785                 }
35786
35787                 d3.select(this)
35788                     .call(field.input)
35789                     .selectAll('input')
35790                     .on('keydown', function() {
35791                         if (d3.event.keyCode === 13) {  // enter
35792                             context.enter(iD.modes.Browse(context));
35793                         }
35794                     })
35795                     .call(reference.body)
35796                     .select('.form-label-button-wrap')
35797                     .call(reference.button);
35798
35799                 field.input.tags(tags);
35800             });
35801
35802         $fields.exit()
35803             .remove();
35804
35805         notShown = notShown.map(function(field) {
35806             return {
35807                 title: field.label(),
35808                 value: field.label(),
35809                 field: field
35810             };
35811         });
35812
35813         var $more = selection.selectAll('.more-fields')
35814             .data((notShown.length > 0) ? [0] : []);
35815
35816         $more.enter().append('div')
35817             .attr('class', 'more-fields')
35818             .append('label')
35819                 .text(t('inspector.add_fields'));
35820
35821         var $input = $more.selectAll('.value')
35822             .data([0]);
35823
35824         $input.enter().append('input')
35825             .attr('class', 'value')
35826             .attr('type', 'text');
35827
35828         $input.value('')
35829             .attr('placeholder', function() {
35830                 var placeholder = [];
35831                 for (var field in notShown) {
35832                     placeholder.push(notShown[field].title);
35833                 }
35834                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
35835             })
35836             .call(d3.combobox().data(notShown)
35837                 .minItems(1)
35838                 .on('accept', show));
35839
35840         $more.exit()
35841             .remove();
35842
35843         $input.exit()
35844             .remove();
35845
35846         function show(field) {
35847             field = field.field;
35848             field.show = true;
35849             content(selection);
35850             field.input.focus();
35851         }
35852
35853         function revert(field) {
35854             d3.event.stopPropagation();
35855             d3.event.preventDefault();
35856             event.change(field.revert());
35857         }
35858
35859         function remove(field) {
35860             d3.event.stopPropagation();
35861             d3.event.preventDefault();
35862             event.change(field.remove());
35863         }
35864     }
35865
35866     presets.preset = function(_) {
35867         if (!arguments.length) return preset;
35868         if (preset && preset.id === _.id) return presets;
35869         preset = _;
35870         fields = null;
35871         return presets;
35872     };
35873
35874     presets.state = function(_) {
35875         if (!arguments.length) return state;
35876         state = _;
35877         return presets;
35878     };
35879
35880     presets.tags = function(_) {
35881         if (!arguments.length) return tags;
35882         tags = _;
35883         // Don't reset fields here.
35884         return presets;
35885     };
35886
35887     presets.entityID = function(_) {
35888         if (!arguments.length) return id;
35889         if (id === _) return presets;
35890         id = _;
35891         fields = null;
35892         return presets;
35893     };
35894
35895     return d3.rebind(presets, event, 'on');
35896 };
35897 iD.ui.PresetIcon = function() {
35898     var preset, geometry;
35899
35900     function presetIcon(selection) {
35901         selection.each(render);
35902     }
35903
35904     function render() {
35905         var selection = d3.select(this),
35906             p = preset.apply(this, arguments),
35907             geom = geometry.apply(this, arguments),
35908             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35909             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35910
35911         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
35912
35913         function tag_classes(p) {
35914             var s = '';
35915             for (var i in p.tags) {
35916                 s += ' tag-' + i;
35917                 if (p.tags[i] !== '*') {
35918                     s += ' tag-' + i + '-' + p.tags[i];
35919                 }
35920             }
35921             return s;
35922         }
35923
35924         var $fill = selection.selectAll('.preset-icon-fill')
35925             .data([0]);
35926
35927         $fill.enter().append('div');
35928
35929         $fill.attr('class', function() {
35930             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35931         });
35932
35933         var $frame = selection.selectAll('.preset-icon-frame')
35934             .data([0]);
35935
35936         $frame.enter()
35937             .append('div')
35938             .call(iD.svg.Icon('#preset-icon-frame'));
35939
35940         $frame.attr('class', function() {
35941             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35942         });
35943
35944
35945         var $icon = selection.selectAll('.preset-icon')
35946             .data([0]);
35947
35948         $icon.enter()
35949             .append('div')
35950             .attr('class', 'preset-icon')
35951             .call(iD.svg.Icon(''));
35952
35953         $icon
35954             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
35955
35956         $icon.selectAll('svg')
35957             .attr('class', function() {
35958                 return 'icon ' + icon + tag_classes(p);
35959             });
35960
35961         $icon.selectAll('use')       // workaround: maki parking-24 broken?
35962             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
35963     }
35964
35965     presetIcon.preset = function(_) {
35966         if (!arguments.length) return preset;
35967         preset = d3.functor(_);
35968         return presetIcon;
35969     };
35970
35971     presetIcon.geometry = function(_) {
35972         if (!arguments.length) return geometry;
35973         geometry = d3.functor(_);
35974         return presetIcon;
35975     };
35976
35977     return presetIcon;
35978 };
35979 iD.ui.PresetList = function(context) {
35980     var event = d3.dispatch('choose'),
35981         id,
35982         currentPreset,
35983         autofocus = false;
35984
35985     function presetList(selection) {
35986         var geometry = context.geometry(id),
35987             presets = context.presets().matchGeometry(geometry);
35988
35989         selection.html('');
35990
35991         var messagewrap = selection.append('div')
35992             .attr('class', 'header fillL cf');
35993
35994         var message = messagewrap.append('h3')
35995             .text(t('inspector.choose'));
35996
35997         if (context.entity(id).isUsed(context.graph())) {
35998             messagewrap.append('button')
35999                 .attr('class', 'preset-choose')
36000                 .on('click', function() { event.choose(currentPreset); })
36001                 .append('span')
36002                 .html('&#9658;');
36003         } else {
36004             messagewrap.append('button')
36005                 .attr('class', 'close')
36006                 .on('click', function() {
36007                     context.enter(iD.modes.Browse(context));
36008                 })
36009                 .call(iD.svg.Icon('#icon-close'));
36010         }
36011
36012         function keydown() {
36013             // hack to let delete shortcut work when search is autofocused
36014             if (search.property('value').length === 0 &&
36015                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
36016                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
36017                 d3.event.preventDefault();
36018                 d3.event.stopPropagation();
36019                 iD.operations.Delete([id], context)();
36020             } else if (search.property('value').length === 0 &&
36021                 (d3.event.ctrlKey || d3.event.metaKey) &&
36022                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
36023                 d3.event.preventDefault();
36024                 d3.event.stopPropagation();
36025                 context.undo();
36026             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
36027                 d3.select(this).on('keydown', null);
36028             }
36029         }
36030
36031         function keypress() {
36032             // enter
36033             var value = search.property('value');
36034             if (d3.event.keyCode === 13 && value.length) {
36035                 list.selectAll('.preset-list-item:first-child').datum().choose();
36036             }
36037         }
36038
36039         function inputevent() {
36040             var value = search.property('value');
36041             list.classed('filtered', value.length);
36042             if (value.length) {
36043                 var results = presets.search(value, geometry);
36044                 message.text(t('inspector.results', {
36045                     n: results.collection.length,
36046                     search: value
36047                 }));
36048                 list.call(drawList, results);
36049             } else {
36050                 list.call(drawList, context.presets().defaults(geometry, 36));
36051                 message.text(t('inspector.choose'));
36052             }
36053         }
36054
36055         var searchWrap = selection.append('div')
36056             .attr('class', 'search-header');
36057
36058         var search = searchWrap.append('input')
36059             .attr('class', 'preset-search-input')
36060             .attr('placeholder', t('inspector.search'))
36061             .attr('type', 'search')
36062             .on('keydown', keydown)
36063             .on('keypress', keypress)
36064             .on('input', inputevent);
36065
36066         searchWrap
36067             .call(iD.svg.Icon('#icon-search', 'pre-text'));
36068
36069         if (autofocus) {
36070             search.node().focus();
36071         }
36072
36073         var listWrap = selection.append('div')
36074             .attr('class', 'inspector-body');
36075
36076         var list = listWrap.append('div')
36077             .attr('class', 'preset-list fillL cf')
36078             .call(drawList, context.presets().defaults(geometry, 36));
36079     }
36080
36081     function drawList(list, presets) {
36082         var collection = presets.collection.map(function(preset) {
36083             return preset.members ? CategoryItem(preset) : PresetItem(preset);
36084         });
36085
36086         var items = list.selectAll('.preset-list-item')
36087             .data(collection, function(d) { return d.preset.id; });
36088
36089         items.enter().append('div')
36090             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
36091             .classed('current', function(item) { return item.preset === currentPreset; })
36092             .each(function(item) {
36093                 d3.select(this).call(item);
36094             })
36095             .style('opacity', 0)
36096             .transition()
36097             .style('opacity', 1);
36098
36099         items.order();
36100
36101         items.exit()
36102             .remove();
36103     }
36104
36105     function CategoryItem(preset) {
36106         var box, sublist, shown = false;
36107
36108         function item(selection) {
36109             var wrap = selection.append('div')
36110                 .attr('class', 'preset-list-button-wrap category col12');
36111
36112             wrap.append('button')
36113                 .attr('class', 'preset-list-button')
36114                 .classed('expanded', false)
36115                 .call(iD.ui.PresetIcon()
36116                     .geometry(context.geometry(id))
36117                     .preset(preset))
36118                 .on('click', function() {
36119                     var isExpanded = d3.select(this).classed('expanded');
36120                     var triangle = isExpanded ? '▶ ' :  '▼ ';
36121                     d3.select(this).classed('expanded', !isExpanded);
36122                     d3.select(this).selectAll('.label').text(triangle + preset.name());
36123                     item.choose();
36124                 })
36125                 .append('div')
36126                 .attr('class', 'label')
36127                 .text(function() {
36128                   return '▶ ' + preset.name();
36129                 });
36130
36131             box = selection.append('div')
36132                 .attr('class', 'subgrid col12')
36133                 .style('max-height', '0px')
36134                 .style('opacity', 0);
36135
36136             box.append('div')
36137                 .attr('class', 'arrow');
36138
36139             sublist = box.append('div')
36140                 .attr('class', 'preset-list fillL3 cf fl');
36141         }
36142
36143         item.choose = function() {
36144             if (!box || !sublist) return;
36145
36146             if (shown) {
36147                 shown = false;
36148                 box.transition()
36149                     .duration(200)
36150                     .style('opacity', '0')
36151                     .style('max-height', '0px')
36152                     .style('padding-bottom', '0px');
36153             } else {
36154                 shown = true;
36155                 sublist.call(drawList, preset.members);
36156                 box.transition()
36157                     .duration(200)
36158                     .style('opacity', '1')
36159                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
36160                     .style('padding-bottom', '20px');
36161             }
36162         };
36163
36164         item.preset = preset;
36165
36166         return item;
36167     }
36168
36169     function PresetItem(preset) {
36170         function item(selection) {
36171             var wrap = selection.append('div')
36172                 .attr('class', 'preset-list-button-wrap col12');
36173
36174             wrap.append('button')
36175                 .attr('class', 'preset-list-button')
36176                 .call(iD.ui.PresetIcon()
36177                     .geometry(context.geometry(id))
36178                     .preset(preset))
36179                 .on('click', item.choose)
36180                 .append('div')
36181                 .attr('class', 'label')
36182                 .text(preset.name());
36183
36184             wrap.call(item.reference.button);
36185             selection.call(item.reference.body);
36186         }
36187
36188         item.choose = function() {
36189             context.presets().choose(preset);
36190
36191             context.perform(
36192                 iD.actions.ChangePreset(id, currentPreset, preset),
36193                 t('operations.change_tags.annotation'));
36194
36195             event.choose(preset);
36196         };
36197
36198         item.help = function() {
36199             d3.event.stopPropagation();
36200             item.reference.toggle();
36201         };
36202
36203         item.preset = preset;
36204         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
36205
36206         return item;
36207     }
36208
36209     presetList.autofocus = function(_) {
36210         if (!arguments.length) return autofocus;
36211         autofocus = _;
36212         return presetList;
36213     };
36214
36215     presetList.entityID = function(_) {
36216         if (!arguments.length) return id;
36217         id = _;
36218         presetList.preset(context.presets().match(context.entity(id), context.graph()));
36219         return presetList;
36220     };
36221
36222     presetList.preset = function(_) {
36223         if (!arguments.length) return currentPreset;
36224         currentPreset = _;
36225         return presetList;
36226     };
36227
36228     return d3.rebind(presetList, event, 'on');
36229 };
36230 iD.ui.RadialMenu = function(context, operations) {
36231     var menu,
36232         center = [0, 0],
36233         tooltip;
36234
36235     var radialMenu = function(selection) {
36236         if (!operations.length)
36237             return;
36238
36239         selection.node().parentNode.focus();
36240
36241         function click(operation) {
36242             d3.event.stopPropagation();
36243             if (operation.disabled())
36244                 return;
36245             operation();
36246             radialMenu.close();
36247         }
36248
36249         menu = selection.append('g')
36250             .attr('class', 'radial-menu')
36251             .attr('transform', 'translate(' + center + ')')
36252             .attr('opacity', 0);
36253
36254         menu.transition()
36255             .attr('opacity', 1);
36256
36257         var r = 50,
36258             a = Math.PI / 4,
36259             a0 = -Math.PI / 4,
36260             a1 = a0 + (operations.length - 1) * a;
36261
36262         menu.append('path')
36263             .attr('class', 'radial-menu-background')
36264             .attr('d', 'M' + r * Math.sin(a0) + ',' +
36265                              r * Math.cos(a0) +
36266                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
36267                              (r * Math.sin(a1) + 1e-3) + ',' +
36268                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
36269             .attr('stroke-width', 50)
36270             .attr('stroke-linecap', 'round');
36271
36272         var button = menu.selectAll()
36273             .data(operations)
36274             .enter()
36275             .append('g')
36276             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
36277             .classed('disabled', function(d) { return d.disabled(); })
36278             .attr('transform', function(d, i) {
36279                 return 'translate(' + iD.geo.roundCoords([
36280                         r * Math.sin(a0 + i * a),
36281                         r * Math.cos(a0 + i * a)]).join(',') + ')';
36282             });
36283
36284         button.append('circle')
36285             .attr('r', 15)
36286             .on('click', click)
36287             .on('mousedown', mousedown)
36288             .on('mouseover', mouseover)
36289             .on('mouseout', mouseout);
36290
36291         button.append('use')
36292             .attr('transform', 'translate(-10,-10)')
36293             .attr('width', '20')
36294             .attr('height', '20')
36295             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
36296
36297         tooltip = d3.select(document.body)
36298             .append('div')
36299             .attr('class', 'tooltip-inner radial-menu-tooltip');
36300
36301         function mousedown() {
36302             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
36303         }
36304
36305         function mouseover(d, i) {
36306             var rect = context.surfaceRect(),
36307                 angle = a0 + i * a,
36308                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
36309                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
36310                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
36311                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
36312
36313             tooltip
36314                 .style('top', null)
36315                 .style('left', null)
36316                 .style('bottom', null)
36317                 .style('right', null)
36318                 .style('display', 'block')
36319                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
36320
36321             if (i === 0) {
36322                 tooltip
36323                     .style('right', right)
36324                     .style('top', top);
36325             } else if (i >= 4) {
36326                 tooltip
36327                     .style('left', left)
36328                     .style('bottom', bottom);
36329             } else {
36330                 tooltip
36331                     .style('left', left)
36332                     .style('top', top);
36333             }
36334         }
36335
36336         function mouseout() {
36337             tooltip.style('display', 'none');
36338         }
36339     };
36340
36341     radialMenu.close = function() {
36342         if (menu) {
36343             menu
36344                 .style('pointer-events', 'none')
36345                 .transition()
36346                 .attr('opacity', 0)
36347                 .remove();
36348         }
36349
36350         if (tooltip) {
36351             tooltip.remove();
36352         }
36353     };
36354
36355     radialMenu.center = function(_) {
36356         if (!arguments.length) return center;
36357         center = _;
36358         return radialMenu;
36359     };
36360
36361     return radialMenu;
36362 };
36363 iD.ui.RawMemberEditor = function(context) {
36364     var id;
36365
36366     function selectMember(d) {
36367         d3.event.preventDefault();
36368         context.enter(iD.modes.Select(context, [d.id]));
36369     }
36370
36371     function changeRole(d) {
36372         var role = d3.select(this).property('value');
36373         context.perform(
36374             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.id, {role: role}), d.index),
36375             t('operations.change_role.annotation'));
36376     }
36377
36378     function deleteMember(d) {
36379         context.perform(
36380             iD.actions.DeleteMember(d.relation.id, d.index),
36381             t('operations.delete_member.annotation'));
36382
36383         if (!context.hasEntity(d.relation.id)) {
36384             context.enter(iD.modes.Browse(context));
36385         }
36386     }
36387
36388     function rawMemberEditor(selection) {
36389         var entity = context.entity(id),
36390             memberships = [];
36391
36392         entity.members.forEach(function(member, index) {
36393             memberships.push({
36394                 index: index,
36395                 id: member.id,
36396                 role: member.role,
36397                 relation: entity,
36398                 member: context.hasEntity(member.id)
36399             });
36400         });
36401
36402         selection.call(iD.ui.Disclosure()
36403             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
36404             .expanded(true)
36405             .on('toggled', toggled)
36406             .content(content));
36407
36408         function toggled(expanded) {
36409             if (expanded) {
36410                 selection.node().parentNode.scrollTop += 200;
36411             }
36412         }
36413
36414         function content($wrap) {
36415             var $list = $wrap.selectAll('.member-list')
36416                 .data([0]);
36417
36418             $list.enter().append('ul')
36419                 .attr('class', 'member-list');
36420
36421             var $items = $list.selectAll('li')
36422                 .data(memberships, function(d) {
36423                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
36424                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
36425                 });
36426
36427             var $enter = $items.enter().append('li')
36428                 .attr('class', 'member-row form-field')
36429                 .classed('member-incomplete', function(d) { return !d.member; });
36430
36431             $enter.each(function(d) {
36432                 if (d.member) {
36433                     var $label = d3.select(this).append('label')
36434                         .attr('class', 'form-label')
36435                         .append('a')
36436                         .attr('href', '#')
36437                         .on('click', selectMember);
36438
36439                     $label.append('span')
36440                         .attr('class', 'member-entity-type')
36441                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
36442
36443                     $label.append('span')
36444                         .attr('class', 'member-entity-name')
36445                         .text(function(d) { return iD.util.displayName(d.member); });
36446
36447                 } else {
36448                     d3.select(this).append('label')
36449                         .attr('class', 'form-label')
36450                         .text(t('inspector.incomplete'));
36451                 }
36452             });
36453
36454             $enter.append('input')
36455                 .attr('class', 'member-role')
36456                 .property('type', 'text')
36457                 .attr('maxlength', 255)
36458                 .attr('placeholder', t('inspector.role'))
36459                 .property('value', function(d) { return d.role; })
36460                 .on('change', changeRole);
36461
36462             $enter.append('button')
36463                 .attr('tabindex', -1)
36464                 .attr('class', 'remove button-input-action member-delete minor')
36465                 .on('click', deleteMember)
36466                 .call(iD.svg.Icon('#operation-delete'));
36467
36468             $items.exit()
36469                 .remove();
36470         }
36471     }
36472
36473     rawMemberEditor.entityID = function(_) {
36474         if (!arguments.length) return id;
36475         id = _;
36476         return rawMemberEditor;
36477     };
36478
36479     return rawMemberEditor;
36480 };
36481 iD.ui.RawMembershipEditor = function(context) {
36482     var id, showBlank;
36483
36484     function selectRelation(d) {
36485         d3.event.preventDefault();
36486         context.enter(iD.modes.Select(context, [d.relation.id]));
36487     }
36488
36489     function changeRole(d) {
36490         var role = d3.select(this).property('value');
36491         context.perform(
36492             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
36493             t('operations.change_role.annotation'));
36494     }
36495
36496     function addMembership(d, role) {
36497         showBlank = false;
36498
36499         if (d.relation) {
36500             context.perform(
36501                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
36502                 t('operations.add_member.annotation'));
36503
36504         } else {
36505             var relation = iD.Relation();
36506
36507             context.perform(
36508                 iD.actions.AddEntity(relation),
36509                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
36510                 t('operations.add.annotation.relation'));
36511
36512             context.enter(iD.modes.Select(context, [relation.id]));
36513         }
36514     }
36515
36516     function deleteMembership(d) {
36517         context.perform(
36518             iD.actions.DeleteMember(d.relation.id, d.index),
36519             t('operations.delete_member.annotation'));
36520     }
36521
36522     function relations(q) {
36523         var newRelation = {
36524                 relation: null,
36525                 value: t('inspector.new_relation')
36526             },
36527             result = [],
36528             graph = context.graph();
36529
36530         context.intersects(context.extent()).forEach(function(entity) {
36531             if (entity.type !== 'relation' || entity.id === id)
36532                 return;
36533
36534             var presetName = context.presets().match(entity, graph).name(),
36535                 entityName = iD.util.displayName(entity) || '';
36536
36537             var value = presetName + ' ' + entityName;
36538             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
36539                 return;
36540
36541             result.push({
36542                 relation: entity,
36543                 value: value
36544             });
36545         });
36546
36547         result.sort(function(a, b) {
36548             return iD.Relation.creationOrder(a.relation, b.relation);
36549         });
36550         result.unshift(newRelation);
36551
36552         return result;
36553     }
36554
36555     function rawMembershipEditor(selection) {
36556         var entity = context.entity(id),
36557             memberships = [];
36558
36559         context.graph().parentRelations(entity).forEach(function(relation) {
36560             relation.members.forEach(function(member, index) {
36561                 if (member.id === entity.id) {
36562                     memberships.push({relation: relation, member: member, index: index});
36563                 }
36564             });
36565         });
36566
36567         selection.call(iD.ui.Disclosure()
36568             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
36569             .expanded(true)
36570             .on('toggled', toggled)
36571             .content(content));
36572
36573         function toggled(expanded) {
36574             if (expanded) {
36575                 selection.node().parentNode.scrollTop += 200;
36576             }
36577         }
36578
36579         function content($wrap) {
36580             var $list = $wrap.selectAll('.member-list')
36581                 .data([0]);
36582
36583             $list.enter().append('ul')
36584                 .attr('class', 'member-list');
36585
36586             var $items = $list.selectAll('li.member-row-normal')
36587                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
36588
36589             var $enter = $items.enter().append('li')
36590                 .attr('class', 'member-row member-row-normal form-field');
36591
36592             var $label = $enter.append('label')
36593                 .attr('class', 'form-label')
36594                 .append('a')
36595                 .attr('href', '#')
36596                 .on('click', selectRelation);
36597
36598             $label.append('span')
36599                 .attr('class', 'member-entity-type')
36600                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
36601
36602             $label.append('span')
36603                 .attr('class', 'member-entity-name')
36604                 .text(function(d) { return iD.util.displayName(d.relation); });
36605
36606             $enter.append('input')
36607                 .attr('class', 'member-role')
36608                 .property('type', 'text')
36609                 .attr('maxlength', 255)
36610                 .attr('placeholder', t('inspector.role'))
36611                 .property('value', function(d) { return d.member.role; })
36612                 .on('change', changeRole);
36613
36614             $enter.append('button')
36615                 .attr('tabindex', -1)
36616                 .attr('class', 'remove button-input-action member-delete minor')
36617                 .on('click', deleteMembership)
36618                 .call(iD.svg.Icon('#operation-delete'));
36619
36620             $items.exit()
36621                 .remove();
36622
36623             if (showBlank) {
36624                 var $new = $list.selectAll('.member-row-new')
36625                     .data([0]);
36626
36627                 $enter = $new.enter().append('li')
36628                     .attr('class', 'member-row member-row-new form-field');
36629
36630                 $enter.append('input')
36631                     .attr('type', 'text')
36632                     .attr('class', 'member-entity-input')
36633                     .call(d3.combobox()
36634                         .minItems(1)
36635                         .fetcher(function(value, callback) {
36636                             callback(relations(value));
36637                         })
36638                         .on('accept', function(d) {
36639                             addMembership(d, $new.select('.member-role').property('value'));
36640                         }));
36641
36642                 $enter.append('input')
36643                     .attr('class', 'member-role')
36644                     .property('type', 'text')
36645                     .attr('maxlength', 255)
36646                     .attr('placeholder', t('inspector.role'))
36647                     .on('change', changeRole);
36648
36649                 $enter.append('button')
36650                     .attr('tabindex', -1)
36651                     .attr('class', 'remove button-input-action member-delete minor')
36652                     .on('click', deleteMembership)
36653                     .call(iD.svg.Icon('#operation-delete'));
36654
36655             } else {
36656                 $list.selectAll('.member-row-new')
36657                     .remove();
36658             }
36659
36660             var $add = $wrap.selectAll('.add-relation')
36661                 .data([0]);
36662
36663             $add.enter()
36664                 .append('button')
36665                 .attr('class', 'add-relation')
36666                 .call(iD.svg.Icon('#icon-plus', 'light'));
36667
36668             $wrap.selectAll('.add-relation')
36669                 .on('click', function() {
36670                     showBlank = true;
36671                     content($wrap);
36672                     $list.selectAll('.member-entity-input').node().focus();
36673                 });
36674         }
36675     }
36676
36677     rawMembershipEditor.entityID = function(_) {
36678         if (!arguments.length) return id;
36679         id = _;
36680         return rawMembershipEditor;
36681     };
36682
36683     return rawMembershipEditor;
36684 };
36685 iD.ui.RawTagEditor = function(context) {
36686     var event = d3.dispatch('change'),
36687         showBlank = false,
36688         state,
36689         preset,
36690         tags,
36691         id;
36692
36693     function rawTagEditor(selection) {
36694         var count = Object.keys(tags).filter(function(d) { return d; }).length;
36695
36696         selection.call(iD.ui.Disclosure()
36697             .title(t('inspector.all_tags') + ' (' + count + ')')
36698             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
36699             .on('toggled', toggled)
36700             .content(content));
36701
36702         function toggled(expanded) {
36703             context.storage('raw_tag_editor.expanded', expanded);
36704             if (expanded) {
36705                 selection.node().parentNode.scrollTop += 200;
36706             }
36707         }
36708     }
36709
36710     function content($wrap) {
36711         var entries = d3.entries(tags);
36712
36713         if (!entries.length || showBlank) {
36714             showBlank = false;
36715             entries.push({key: '', value: ''});
36716         }
36717
36718         var $list = $wrap.selectAll('.tag-list')
36719             .data([0]);
36720
36721         $list.enter().append('ul')
36722             .attr('class', 'tag-list');
36723
36724         var $newTag = $wrap.selectAll('.add-tag')
36725             .data([0]);
36726
36727         $newTag.enter()
36728             .append('button')
36729             .attr('class', 'add-tag')
36730             .call(iD.svg.Icon('#icon-plus', 'light'));
36731
36732         $newTag.on('click', addTag);
36733
36734         var $items = $list.selectAll('li')
36735             .data(entries, function(d) { return d.key; });
36736
36737         // Enter
36738
36739         var $enter = $items.enter().append('li')
36740             .attr('class', 'tag-row cf');
36741
36742         $enter.append('div')
36743             .attr('class', 'key-wrap')
36744             .append('input')
36745             .property('type', 'text')
36746             .attr('class', 'key')
36747             .attr('maxlength', 255);
36748
36749         $enter.append('div')
36750             .attr('class', 'input-wrap-position')
36751             .append('input')
36752             .property('type', 'text')
36753             .attr('class', 'value')
36754             .attr('maxlength', 255);
36755
36756         $enter.append('button')
36757             .attr('tabindex', -1)
36758             .attr('class', 'remove minor')
36759             .call(iD.svg.Icon('#operation-delete'));
36760
36761         if (context.taginfo()) {
36762             $enter.each(bindTypeahead);
36763         }
36764
36765         // Update
36766
36767         $items.order();
36768
36769         $items.each(function(tag) {
36770             var isRelation = (context.entity(id).type === 'relation'),
36771                 reference;
36772             if (isRelation && tag.key === 'type')
36773                 reference = iD.ui.TagReference({rtype: tag.value}, context);
36774             else
36775                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
36776
36777             if (state === 'hover') {
36778                 reference.showing(false);
36779             }
36780
36781             d3.select(this)
36782                 .call(reference.button)
36783                 .call(reference.body);
36784         });
36785
36786         $items.select('input.key')
36787             .value(function(d) { return d.key; })
36788             .on('blur', keyChange)
36789             .on('change', keyChange);
36790
36791         $items.select('input.value')
36792             .value(function(d) { return d.value; })
36793             .on('blur', valueChange)
36794             .on('change', valueChange)
36795             .on('keydown.push-more', pushMore);
36796
36797         $items.select('button.remove')
36798             .on('click', removeTag);
36799
36800         $items.exit()
36801             .each(unbind)
36802             .remove();
36803
36804         function pushMore() {
36805             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
36806                 $list.selectAll('li:last-child input.value').node() === this) {
36807                 addTag();
36808             }
36809         }
36810
36811         function bindTypeahead() {
36812             var row = d3.select(this),
36813                 key = row.selectAll('input.key'),
36814                 value = row.selectAll('input.value');
36815
36816             function sort(value, data) {
36817                 var sameletter = [],
36818                     other = [];
36819                 for (var i = 0; i < data.length; i++) {
36820                     if (data[i].value.substring(0, value.length) === value) {
36821                         sameletter.push(data[i]);
36822                     } else {
36823                         other.push(data[i]);
36824                     }
36825                 }
36826                 return sameletter.concat(other);
36827             }
36828
36829             key.call(d3.combobox()
36830                 .fetcher(function(value, callback) {
36831                     context.taginfo().keys({
36832                         debounce: true,
36833                         geometry: context.geometry(id),
36834                         query: value
36835                     }, function(err, data) {
36836                         if (!err) callback(sort(value, data));
36837                     });
36838                 }));
36839
36840             value.call(d3.combobox()
36841                 .fetcher(function(value, callback) {
36842                     context.taginfo().values({
36843                         debounce: true,
36844                         key: key.value(),
36845                         geometry: context.geometry(id),
36846                         query: value
36847                     }, function(err, data) {
36848                         if (!err) callback(sort(value, data));
36849                     });
36850                 }));
36851         }
36852
36853         function unbind() {
36854             var row = d3.select(this);
36855
36856             row.selectAll('input.key')
36857                 .call(d3.combobox.off);
36858
36859             row.selectAll('input.value')
36860                 .call(d3.combobox.off);
36861         }
36862
36863         function keyChange(d) {
36864             var kOld = d.key,
36865                 kNew = this.value.trim(),
36866                 tag = {};
36867
36868             if (kNew && kNew !== kOld) {
36869                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
36870                     base = match[1],
36871                     suffix = +(match[2] || 1);
36872                 while (tags[kNew]) {  // rename key if already in use
36873                     kNew = base + '_' + suffix++;
36874                 }
36875             }
36876             tag[kOld] = undefined;
36877             tag[kNew] = d.value;
36878             d.key = kNew; // Maintain DOM identity through the subsequent update.
36879             this.value = kNew;
36880             event.change(tag);
36881         }
36882
36883         function valueChange(d) {
36884             var tag = {};
36885             tag[d.key] = this.value;
36886             event.change(tag);
36887         }
36888
36889         function removeTag(d) {
36890             var tag = {};
36891             tag[d.key] = undefined;
36892             event.change(tag);
36893             d3.select(this.parentNode).remove();
36894         }
36895
36896         function addTag() {
36897             // Wrapped in a setTimeout in case it's being called from a blur
36898             // handler. Without the setTimeout, the call to `content` would
36899             // wipe out the pending value change.
36900             setTimeout(function() {
36901                 showBlank = true;
36902                 content($wrap);
36903                 $list.selectAll('li:last-child input.key').node().focus();
36904             }, 0);
36905         }
36906     }
36907
36908     rawTagEditor.state = function(_) {
36909         if (!arguments.length) return state;
36910         state = _;
36911         return rawTagEditor;
36912     };
36913
36914     rawTagEditor.preset = function(_) {
36915         if (!arguments.length) return preset;
36916         preset = _;
36917         return rawTagEditor;
36918     };
36919
36920     rawTagEditor.tags = function(_) {
36921         if (!arguments.length) return tags;
36922         tags = _;
36923         return rawTagEditor;
36924     };
36925
36926     rawTagEditor.entityID = function(_) {
36927         if (!arguments.length) return id;
36928         id = _;
36929         return rawTagEditor;
36930     };
36931
36932     return d3.rebind(rawTagEditor, event, 'on');
36933 };
36934 iD.ui.Restore = function(context) {
36935     return function(selection) {
36936         if (!context.history().lock() || !context.history().restorableChanges())
36937             return;
36938
36939         var modal = iD.ui.modal(selection);
36940
36941         modal.select('.modal')
36942             .attr('class', 'modal fillL col6');
36943
36944         var introModal = modal.select('.content');
36945
36946         introModal.attr('class','cf');
36947
36948         introModal.append('div')
36949             .attr('class', 'modal-section')
36950             .append('h3')
36951             .text(t('restore.heading'));
36952
36953         introModal.append('div')
36954             .attr('class','modal-section')
36955             .append('p')
36956             .text(t('restore.description'));
36957
36958         var buttonWrap = introModal.append('div')
36959             .attr('class', 'modal-actions cf');
36960
36961         var restore = buttonWrap.append('button')
36962             .attr('class', 'restore col6')
36963             .text(t('restore.restore'))
36964             .on('click', function() {
36965                 context.history().restore();
36966                 modal.remove();
36967             });
36968
36969         buttonWrap.append('button')
36970             .attr('class', 'reset col6')
36971             .text(t('restore.reset'))
36972             .on('click', function() {
36973                 context.history().clearSaved();
36974                 modal.remove();
36975             });
36976
36977         restore.node().focus();
36978     };
36979 };
36980 iD.ui.Save = function(context) {
36981     var history = context.history(),
36982         key = iD.ui.cmd('⌘S');
36983
36984     function saving() {
36985         return context.mode().id === 'save';
36986     }
36987
36988     function save() {
36989         d3.event.preventDefault();
36990         if (!context.inIntro() && !saving() && history.hasChanges()) {
36991             context.enter(iD.modes.Save(context));
36992         }
36993     }
36994
36995     return function(selection) {
36996         var tooltip = bootstrap.tooltip()
36997             .placement('bottom')
36998             .html(true)
36999             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
37000
37001         var button = selection.append('button')
37002             .attr('class', 'save col12 disabled')
37003             .attr('tabindex', -1)
37004             .on('click', save)
37005             .call(tooltip);
37006
37007         button.append('span')
37008             .attr('class', 'label')
37009             .text(t('save.title'));
37010
37011         button.append('span')
37012             .attr('class', 'count')
37013             .text('0');
37014
37015         var keybinding = d3.keybinding('undo-redo')
37016             .on(key, save, true);
37017
37018         d3.select(document)
37019             .call(keybinding);
37020
37021         var numChanges = 0;
37022
37023         context.history().on('change.save', function() {
37024             var _ = history.difference().summary().length;
37025             if (_ === numChanges)
37026                 return;
37027             numChanges = _;
37028
37029             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
37030                     'save.help' : 'save.no_changes'), key));
37031
37032             button
37033                 .classed('disabled', numChanges === 0)
37034                 .classed('has-count', numChanges > 0);
37035
37036             button.select('span.count')
37037                 .text(numChanges);
37038         });
37039
37040         context.on('enter.save', function() {
37041             button.property('disabled', saving());
37042             if (saving()) button.call(tooltip.hide);
37043         });
37044     };
37045 };
37046 iD.ui.Scale = function(context) {
37047     var projection = context.projection,
37048         maxLength = 180,
37049         tickHeight = 8;
37050
37051     function scaleDefs(loc1, loc2) {
37052         var lat = (loc2[1] + loc1[1]) / 2,
37053             imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
37054             conversion = (imperial ? 3.28084 : 1),
37055             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
37056             scale = { dist: 0, px: 0, text: '' },
37057             buckets, i, val, dLon;
37058
37059         if (imperial) {
37060             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
37061         } else {
37062             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
37063         }
37064
37065         // determine a user-friendly endpoint for the scale
37066         for (i = 0; i < buckets.length; i++) {
37067             val = buckets[i];
37068             if (dist >= val) {
37069                 scale.dist = Math.floor(dist / val) * val;
37070                 break;
37071             }
37072         }
37073
37074         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
37075         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
37076
37077         if (imperial) {
37078             if (scale.dist >= 5280) {
37079                 scale.dist /= 5280;
37080                 scale.text = String(scale.dist) + ' mi';
37081             } else {
37082                 scale.text = String(scale.dist) + ' ft';
37083             }
37084         } else {
37085             if (scale.dist >= 1000) {
37086                 scale.dist /= 1000;
37087                 scale.text = String(scale.dist) + ' km';
37088             } else {
37089                 scale.text = String(scale.dist) + ' m';
37090             }
37091         }
37092
37093         return scale;
37094     }
37095
37096     function update(selection) {
37097         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
37098         var dims = context.map().dimensions(),
37099             loc1 = projection.invert([0, dims[1]]),
37100             loc2 = projection.invert([maxLength, dims[1]]),
37101             scale = scaleDefs(loc1, loc2);
37102
37103         selection.select('#scalepath')
37104             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
37105
37106         selection.select('#scaletext')
37107             .attr('x', scale.px + 8)
37108             .attr('y', tickHeight)
37109             .text(scale.text);
37110     }
37111
37112     return function(selection) {
37113         var g = selection.append('svg')
37114             .attr('id', 'scale')
37115             .append('g')
37116             .attr('transform', 'translate(10,11)');
37117
37118         g.append('path').attr('id', 'scalepath');
37119         g.append('text').attr('id', 'scaletext');
37120
37121         update(selection);
37122
37123         context.map().on('move.scale', function() {
37124             update(selection);
37125         });
37126     };
37127 };
37128 iD.ui.SelectionList = function(context, selectedIDs) {
37129
37130     function selectEntity(entity) {
37131         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
37132     }
37133
37134
37135     function selectionList(selection) {
37136         selection.classed('selection-list-pane', true);
37137
37138         var header = selection.append('div')
37139             .attr('class', 'header fillL cf');
37140
37141         header.append('h3')
37142             .text(t('inspector.multiselect'));
37143
37144         var listWrap = selection.append('div')
37145             .attr('class', 'inspector-body');
37146
37147         var list = listWrap.append('div')
37148             .attr('class', 'feature-list cf');
37149
37150         context.history().on('change.selection-list', drawList);
37151         drawList();
37152
37153         function drawList() {
37154             var entities = selectedIDs
37155                 .map(function(id) { return context.hasEntity(id); })
37156                 .filter(function(entity) { return entity; });
37157
37158             var items = list.selectAll('.feature-list-item')
37159                 .data(entities, iD.Entity.key);
37160
37161             var enter = items.enter().append('button')
37162                 .attr('class', 'feature-list-item')
37163                 .on('click', selectEntity);
37164
37165             // Enter
37166             var label = enter.append('div')
37167                 .attr('class', 'label')
37168                 .call(iD.svg.Icon('', 'pre-text'));
37169
37170             label.append('span')
37171                 .attr('class', 'entity-type');
37172
37173             label.append('span')
37174                 .attr('class', 'entity-name');
37175
37176             // Update
37177             items.selectAll('use')
37178                 .attr('href', function() {
37179                     var entity = this.parentNode.parentNode.__data__;
37180                     return '#icon-' + context.geometry(entity.id);
37181                 });
37182
37183             items.selectAll('.entity-type')
37184                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
37185
37186             items.selectAll('.entity-name')
37187                 .text(function(entity) { return iD.util.displayName(entity); });
37188
37189             // Exit
37190             items.exit()
37191                 .remove();
37192         }
37193     }
37194
37195     return selectionList;
37196
37197 };
37198 iD.ui.Sidebar = function(context) {
37199     var inspector = iD.ui.Inspector(context),
37200         current;
37201
37202     function sidebar(selection) {
37203         var featureListWrap = selection.append('div')
37204             .attr('class', 'feature-list-pane')
37205             .call(iD.ui.FeatureList(context));
37206
37207         selection.call(iD.ui.Notice(context));
37208
37209         var inspectorWrap = selection.append('div')
37210             .attr('class', 'inspector-hidden inspector-wrap fr');
37211
37212         function hover(id) {
37213             if (!current && context.hasEntity(id)) {
37214                 featureListWrap.classed('inspector-hidden', true);
37215                 inspectorWrap.classed('inspector-hidden', false)
37216                     .classed('inspector-hover', true);
37217
37218                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
37219                     inspector
37220                         .state('hover')
37221                         .entityID(id);
37222
37223                     inspectorWrap.call(inspector);
37224                 }
37225             } else if (!current) {
37226                 featureListWrap.classed('inspector-hidden', false);
37227                 inspectorWrap.classed('inspector-hidden', true);
37228                 inspector.state('hide');
37229             }
37230         }
37231
37232         sidebar.hover = _.throttle(hover, 200);
37233
37234         sidebar.select = function(id, newFeature) {
37235             if (!current && id) {
37236                 featureListWrap.classed('inspector-hidden', true);
37237                 inspectorWrap.classed('inspector-hidden', false)
37238                     .classed('inspector-hover', false);
37239
37240                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
37241                     inspector
37242                         .state('select')
37243                         .entityID(id)
37244                         .newFeature(newFeature);
37245
37246                     inspectorWrap.call(inspector);
37247                 }
37248             } else if (!current) {
37249                 featureListWrap.classed('inspector-hidden', false);
37250                 inspectorWrap.classed('inspector-hidden', true);
37251                 inspector.state('hide');
37252             }
37253         };
37254
37255         sidebar.show = function(component) {
37256             featureListWrap.classed('inspector-hidden', true);
37257             inspectorWrap.classed('inspector-hidden', true);
37258             if (current) current.remove();
37259             current = selection.append('div')
37260                 .attr('class', 'sidebar-component')
37261                 .call(component);
37262         };
37263
37264         sidebar.hide = function() {
37265             featureListWrap.classed('inspector-hidden', false);
37266             inspectorWrap.classed('inspector-hidden', true);
37267             if (current) current.remove();
37268             current = null;
37269         };
37270     }
37271
37272     sidebar.hover = function() {};
37273     sidebar.hover.cancel = function() {};
37274     sidebar.select = function() {};
37275     sidebar.show = function() {};
37276     sidebar.hide = function() {};
37277
37278     return sidebar;
37279 };
37280 iD.ui.SourceSwitch = function(context) {
37281     var keys;
37282
37283     function click() {
37284         d3.event.preventDefault();
37285
37286         if (context.history().hasChanges() &&
37287             !window.confirm(t('source_switch.lose_changes'))) return;
37288
37289         var live = d3.select(this)
37290             .classed('live');
37291
37292         context.connection()
37293             .switch(live ? keys[1] : keys[0]);
37294
37295         context.enter(iD.modes.Browse(context));
37296         context.flush();
37297
37298         d3.select(this)
37299             .text(live ? t('source_switch.dev') : t('source_switch.live'))
37300             .classed('live', !live);
37301     }
37302
37303     var sourceSwitch = function(selection) {
37304         selection.append('a')
37305             .attr('href', '#')
37306             .text(t('source_switch.live'))
37307             .classed('live', true)
37308             .attr('tabindex', -1)
37309             .on('click', click);
37310     };
37311
37312     sourceSwitch.keys = function(_) {
37313         if (!arguments.length) return keys;
37314         keys = _;
37315         return sourceSwitch;
37316     };
37317
37318     return sourceSwitch;
37319 };
37320 iD.ui.Spinner = function(context) {
37321     var connection = context.connection();
37322
37323     return function(selection) {
37324         var img = selection.append('img')
37325             .attr('src', context.imagePath('loader-black.gif'))
37326             .style('opacity', 0);
37327
37328         connection.on('loading.spinner', function() {
37329             img.transition()
37330                 .style('opacity', 1);
37331         });
37332
37333         connection.on('loaded.spinner', function() {
37334             img.transition()
37335                 .style('opacity', 0);
37336         });
37337     };
37338 };
37339 iD.ui.Splash = function(context) {
37340     return function(selection) {
37341         if (context.storage('sawSplash'))
37342              return;
37343
37344         context.storage('sawSplash', true);
37345
37346         var modal = iD.ui.modal(selection);
37347
37348         modal.select('.modal')
37349             .attr('class', 'modal-splash modal col6');
37350
37351         var introModal = modal.select('.content')
37352             .append('div')
37353             .attr('class', 'fillL');
37354
37355         introModal.append('div')
37356             .attr('class','modal-section cf')
37357             .append('h3').text(t('splash.welcome'));
37358
37359         introModal.append('div')
37360             .attr('class','modal-section')
37361             .append('p')
37362             .html(t('splash.text', {
37363                 version: iD.version,
37364                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
37365                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
37366             }));
37367
37368         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
37369
37370         buttons.append('button')
37371             .attr('class', 'col6 walkthrough')
37372             .text(t('splash.walkthrough'))
37373             .on('click', function() {
37374                 d3.select(document.body).call(iD.ui.intro(context));
37375                 modal.close();
37376             });
37377
37378         buttons.append('button')
37379             .attr('class', 'col6 start')
37380             .text(t('splash.start'))
37381             .on('click', modal.close);
37382
37383         modal.select('button.close').attr('class','hide');
37384
37385     };
37386 };
37387 iD.ui.Status = function(context) {
37388     var connection = context.connection(),
37389         errCount = 0;
37390
37391     return function(selection) {
37392
37393         function update() {
37394
37395             connection.status(function(err, apiStatus) {
37396
37397                 selection.html('');
37398
37399                 if (err && errCount++ < 2) return;
37400
37401                 if (err) {
37402                     selection.text(t('status.error'));
37403
37404                 } else if (apiStatus === 'readonly') {
37405                     selection.text(t('status.readonly'));
37406
37407                 } else if (apiStatus === 'offline') {
37408                     selection.text(t('status.offline'));
37409                 }
37410
37411                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
37412                 if (!err) errCount = 0;
37413
37414             });
37415         }
37416
37417         connection.on('auth', function() { update(selection); });
37418         window.setInterval(update, 90000);
37419         update(selection);
37420     };
37421 };
37422 iD.ui.Success = function(context) {
37423     var dispatch = d3.dispatch('cancel'),
37424         changeset;
37425
37426     function success(selection) {
37427         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
37428             ' ' + context.connection().changesetURL(changeset.id);
37429
37430         var header = selection.append('div')
37431             .attr('class', 'header fillL');
37432
37433         header.append('button')
37434             .attr('class', 'fr')
37435             .on('click', function() { dispatch.cancel(); })
37436             .call(iD.svg.Icon('#icon-close'));
37437
37438         header.append('h3')
37439             .text(t('success.just_edited'));
37440
37441         var body = selection.append('div')
37442             .attr('class', 'body save-success fillL');
37443
37444         body.append('p')
37445             .html(t('success.help_html'));
37446
37447         body.append('a')
37448             .attr('class', 'details')
37449             .attr('target', '_blank')
37450             .attr('tabindex', -1)
37451             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37452             .attr('href', t('success.help_link_url'))
37453             .append('span')
37454             .text(t('success.help_link_text'));
37455
37456         var changesetURL = context.connection().changesetURL(changeset.id);
37457
37458         body.append('a')
37459             .attr('class', 'button col12 osm')
37460             .attr('target', '_blank')
37461             .attr('href', changesetURL)
37462             .text(t('success.view_on_osm'));
37463
37464         var sharing = {
37465             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
37466             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
37467             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
37468         };
37469
37470         body.selectAll('.button.social')
37471             .data(d3.entries(sharing))
37472             .enter()
37473             .append('a')
37474             .attr('class', 'button social col4')
37475             .attr('target', '_blank')
37476             .attr('href', function(d) { return d.value; })
37477             .call(bootstrap.tooltip()
37478                 .title(function(d) { return t('success.' + d.key); })
37479                 .placement('bottom'))
37480             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
37481     }
37482
37483     success.changeset = function(_) {
37484         if (!arguments.length) return changeset;
37485         changeset = _;
37486         return success;
37487     };
37488
37489     return d3.rebind(success, dispatch, 'on');
37490 };
37491 iD.ui.TagReference = function(tag, context) {
37492     var tagReference = {},
37493         button,
37494         body,
37495         loaded,
37496         showing;
37497
37498     function findLocal(data) {
37499         var locale = iD.detect().locale.toLowerCase(),
37500             localized;
37501
37502         localized = _.find(data, function(d) {
37503             return d.lang.toLowerCase() === locale;
37504         });
37505         if (localized) return localized;
37506
37507         // try the non-regional version of a language, like
37508         // 'en' if the language is 'en-US'
37509         if (locale.indexOf('-') !== -1) {
37510             var first = locale.split('-')[0];
37511             localized = _.find(data, function(d) {
37512                 return d.lang.toLowerCase() === first;
37513             });
37514             if (localized) return localized;
37515         }
37516
37517         // finally fall back to english
37518         return _.find(data, function(d) {
37519             return d.lang.toLowerCase() === 'en';
37520         });
37521     }
37522
37523     function load(param) {
37524         button.classed('tag-reference-loading', true);
37525
37526         context.taginfo().docs(param, function show(err, data) {
37527             var docs;
37528             if (!err && data) {
37529                 docs = findLocal(data);
37530             }
37531
37532             body.html('');
37533
37534             if (!docs || !docs.description) {
37535                 if (param.hasOwnProperty('value')) {
37536                     load(_.omit(param, 'value'));   // retry with key only
37537                 } else {
37538                     body.append('p').text(t('inspector.no_documentation_key'));
37539                     done();
37540                 }
37541                 return;
37542             }
37543
37544             if (docs.image && docs.image.thumb_url_prefix) {
37545                 body
37546                     .append('img')
37547                     .attr('class', 'wiki-image')
37548                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
37549                     .on('load', function() { done(); })
37550                     .on('error', function() { d3.select(this).remove(); done(); });
37551             } else {
37552                 done();
37553             }
37554
37555             body
37556                 .append('p')
37557                 .text(docs.description);
37558
37559             body
37560                 .append('a')
37561                 .attr('target', '_blank')
37562                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
37563                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37564                 .append('span')
37565                 .text(t('inspector.reference'));
37566         });
37567     }
37568
37569     function done() {
37570         loaded = true;
37571
37572         button.classed('tag-reference-loading', false);
37573
37574         body.transition()
37575             .duration(200)
37576             .style('max-height', '200px')
37577             .style('opacity', '1');
37578
37579         showing = true;
37580     }
37581
37582     function hide(selection) {
37583         selection = selection || body.transition().duration(200);
37584
37585         selection
37586             .style('max-height', '0px')
37587             .style('opacity', '0');
37588
37589         showing = false;
37590     }
37591
37592     tagReference.button = function(selection) {
37593         button = selection.selectAll('.tag-reference-button')
37594             .data([0]);
37595
37596         button.enter()
37597             .append('button')
37598             .attr('class', 'tag-reference-button')
37599             .attr('tabindex', -1)
37600             .call(iD.svg.Icon('#icon-inspect'));
37601
37602         button.on('click', function () {
37603             d3.event.stopPropagation();
37604             d3.event.preventDefault();
37605             if (showing) {
37606                 hide();
37607             } else if (loaded) {
37608                 done();
37609             } else {
37610                 if (context.taginfo()) {
37611                     load(tag);
37612                 }
37613             }
37614         });
37615     };
37616
37617     tagReference.body = function(selection) {
37618         body = selection.selectAll('.tag-reference-body')
37619             .data([0]);
37620
37621         body.enter().append('div')
37622             .attr('class', 'tag-reference-body cf')
37623             .style('max-height', '0')
37624             .style('opacity', '0');
37625
37626         if (showing === false) {
37627             hide(body);
37628         }
37629     };
37630
37631     tagReference.showing = function(_) {
37632         if (!arguments.length) return showing;
37633         showing = _;
37634         return tagReference;
37635     };
37636
37637     return tagReference;
37638 };
37639 // toggles the visibility of ui elements, using a combination of the
37640 // hide class, which sets display=none, and a d3 transition for opacity.
37641 // this will cause blinking when called repeatedly, so check that the
37642 // value actually changes between calls.
37643 iD.ui.Toggle = function(show, callback) {
37644     return function(selection) {
37645         selection
37646             .style('opacity', show ? 0 : 1)
37647             .classed('hide', false)
37648             .transition()
37649             .style('opacity', show ? 1 : 0)
37650             .each('end', function() {
37651                 d3.select(this)
37652                     .classed('hide', !show)
37653                     .style('opacity', null);
37654                 if (callback) callback.apply(this);
37655             });
37656     };
37657 };
37658 iD.ui.UndoRedo = function(context) {
37659     var commands = [{
37660         id: 'undo',
37661         cmd: iD.ui.cmd('⌘Z'),
37662         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
37663         annotation: function() { return context.history().undoAnnotation(); }
37664     }, {
37665         id: 'redo',
37666         cmd: iD.ui.cmd('⌘⇧Z'),
37667         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
37668         annotation: function() { return context.history().redoAnnotation(); }
37669     }];
37670
37671     function saving() {
37672         return context.mode().id === 'save';
37673     }
37674
37675     return function(selection) {
37676         var tooltip = bootstrap.tooltip()
37677             .placement('bottom')
37678             .html(true)
37679             .title(function (d) {
37680                 return iD.ui.tooltipHtml(d.annotation() ?
37681                     t(d.id + '.tooltip', {action: d.annotation()}) :
37682                     t(d.id + '.nothing'), d.cmd);
37683             });
37684
37685         var buttons = selection.selectAll('button')
37686             .data(commands)
37687             .enter().append('button')
37688             .attr('class', 'col6 disabled')
37689             .on('click', function(d) { return d.action(); })
37690             .call(tooltip);
37691
37692         buttons.each(function(d) {
37693             d3.select(this)
37694                 .call(iD.svg.Icon('#icon-' + d.id));
37695         });
37696
37697         var keybinding = d3.keybinding('undo')
37698             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
37699             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
37700
37701         d3.select(document)
37702             .call(keybinding);
37703
37704         context.history()
37705             .on('change.undo_redo', update);
37706
37707         context
37708             .on('enter.undo_redo', update);
37709
37710         function update() {
37711             buttons
37712                 .property('disabled', saving())
37713                 .classed('disabled', function(d) { return !d.annotation(); })
37714                 .each(function() {
37715                     var selection = d3.select(this);
37716                     if (selection.property('tooltipVisible')) {
37717                         selection.call(tooltip.show);
37718                     }
37719                 });
37720         }
37721     };
37722 };
37723 iD.ui.ViewOnOSM = function(context) {
37724     var id;
37725
37726     function viewOnOSM(selection) {
37727         var entity = context.entity(id);
37728
37729         selection.style('display', entity.isNew() ? 'none' : null);
37730
37731         var $link = selection.selectAll('.view-on-osm')
37732             .data([0]);
37733
37734         $link.enter()
37735             .append('a')
37736             .attr('class', 'view-on-osm')
37737             .attr('target', '_blank')
37738             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37739             .append('span')
37740             .text(t('inspector.view_on_osm'));
37741
37742         $link
37743             .attr('href', context.connection().entityURL(entity));
37744     }
37745
37746     viewOnOSM.entityID = function(_) {
37747         if (!arguments.length) return id;
37748         id = _;
37749         return viewOnOSM;
37750     };
37751
37752     return viewOnOSM;
37753 };
37754 iD.ui.Zoom = function(context) {
37755     var zooms = [{
37756         id: 'zoom-in',
37757         icon: 'plus',
37758         title: t('zoom.in'),
37759         action: context.zoomIn,
37760         key: '+'
37761     }, {
37762         id: 'zoom-out',
37763         icon: 'minus',
37764         title: t('zoom.out'),
37765         action: context.zoomOut,
37766         key: '-'
37767     }];
37768
37769     function zoomIn() {
37770         d3.event.preventDefault();
37771         if (!context.inIntro()) context.zoomIn();
37772     }
37773
37774     function zoomOut() {
37775         d3.event.preventDefault();
37776         if (!context.inIntro()) context.zoomOut();
37777     }
37778
37779     function zoomInFurther() {
37780         d3.event.preventDefault();
37781         if (!context.inIntro()) context.zoomInFurther();
37782     }
37783
37784     function zoomOutFurther() {
37785         d3.event.preventDefault();
37786         if (!context.inIntro()) context.zoomOutFurther();
37787     }
37788
37789
37790     return function(selection) {
37791         var button = selection.selectAll('button')
37792             .data(zooms)
37793             .enter().append('button')
37794             .attr('tabindex', -1)
37795             .attr('class', function(d) { return d.id; })
37796             .on('click.editor', function(d) { d.action(); })
37797             .call(bootstrap.tooltip()
37798                 .placement('left')
37799                 .html(true)
37800                 .title(function(d) {
37801                     return iD.ui.tooltipHtml(d.title, d.key);
37802                 }));
37803
37804         button.each(function(d) {
37805             d3.select(this)
37806                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
37807         });
37808
37809         var keybinding = d3.keybinding('zoom');
37810
37811         _.each(['=','ffequals','plus','ffplus'], function(key) {
37812             keybinding.on(key, zoomIn);
37813             keybinding.on('⇧' + key, zoomIn);
37814             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
37815             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
37816         });
37817         _.each(['-','ffminus','_','dash'], function(key) {
37818             keybinding.on(key, zoomOut);
37819             keybinding.on('⇧' + key, zoomOut);
37820             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
37821             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
37822         });
37823
37824         d3.select(document)
37825             .call(keybinding);
37826     };
37827 };
37828 iD.ui.preset.access = function(field) {
37829     var dispatch = d3.dispatch('change'),
37830         items;
37831
37832     function access(selection) {
37833         var wrap = selection.selectAll('.preset-input-wrap')
37834             .data([0]);
37835
37836         wrap.enter().append('div')
37837             .attr('class', 'cf preset-input-wrap')
37838             .append('ul');
37839
37840         items = wrap.select('ul').selectAll('li')
37841             .data(field.keys);
37842
37843         // Enter
37844
37845         var enter = items.enter().append('li')
37846             .attr('class', function(d) { return 'cf preset-access-' + d; });
37847
37848         enter.append('span')
37849             .attr('class', 'col6 label preset-label-access')
37850             .attr('for', function(d) { return 'preset-input-access-' + d; })
37851             .text(function(d) { return field.t('types.' + d); });
37852
37853         enter.append('div')
37854             .attr('class', 'col6 preset-input-access-wrap')
37855             .append('input')
37856             .attr('type', 'text')
37857             .attr('class', 'preset-input-access')
37858             .attr('id', function(d) { return 'preset-input-access-' + d; })
37859             .each(function(d) {
37860                 d3.select(this)
37861                     .call(d3.combobox()
37862                         .data(access.options(d)));
37863             });
37864
37865         // Update
37866
37867         wrap.selectAll('.preset-input-access')
37868             .on('change', change)
37869             .on('blur', change);
37870     }
37871
37872     function change(d) {
37873         var tag = {};
37874         tag[d] = d3.select(this).value() || undefined;
37875         dispatch.change(tag);
37876     }
37877
37878     access.options = function(type) {
37879         var options = ['no', 'permissive', 'private', 'destination'];
37880
37881         if (type !== 'access') {
37882             options.unshift('yes');
37883             options.push('designated');
37884
37885             if (type === 'bicycle') {
37886                 options.push('dismount');
37887             }
37888         }
37889
37890         return options.map(function(option) {
37891             return {
37892                 title: field.t('options.' + option + '.description'),
37893                 value: option
37894             };
37895         });
37896     };
37897
37898     var placeholders = {
37899         footway: {
37900             foot: 'designated',
37901             motor_vehicle: 'no'
37902         },
37903         steps: {
37904             foot: 'yes',
37905             motor_vehicle: 'no',
37906             bicycle: 'no',
37907             horse: 'no'
37908         },
37909         pedestrian: {
37910             foot: 'yes',
37911             motor_vehicle: 'no'
37912         },
37913         cycleway: {
37914             motor_vehicle: 'no',
37915             bicycle: 'designated'
37916         },
37917         bridleway: {
37918             motor_vehicle: 'no',
37919             horse: 'designated'
37920         },
37921         path: {
37922             foot: 'yes',
37923             motor_vehicle: 'no',
37924             bicycle: 'yes',
37925             horse: 'yes'
37926         },
37927         motorway: {
37928             foot: 'no',
37929             motor_vehicle: 'yes',
37930             bicycle: 'no',
37931             horse: 'no'
37932         },
37933         trunk: {
37934             motor_vehicle: 'yes'
37935         },
37936         primary: {
37937             foot: 'yes',
37938             motor_vehicle: 'yes',
37939             bicycle: 'yes',
37940             horse: 'yes'
37941         },
37942         secondary: {
37943             foot: 'yes',
37944             motor_vehicle: 'yes',
37945             bicycle: 'yes',
37946             horse: 'yes'
37947         },
37948         tertiary: {
37949             foot: 'yes',
37950             motor_vehicle: 'yes',
37951             bicycle: 'yes',
37952             horse: 'yes'
37953         },
37954         residential: {
37955             foot: 'yes',
37956             motor_vehicle: 'yes',
37957             bicycle: 'yes',
37958             horse: 'yes'
37959         },
37960         unclassified: {
37961             foot: 'yes',
37962             motor_vehicle: 'yes',
37963             bicycle: 'yes',
37964             horse: 'yes'
37965         },
37966         service: {
37967             foot: 'yes',
37968             motor_vehicle: 'yes',
37969             bicycle: 'yes',
37970             horse: 'yes'
37971         },
37972         motorway_link: {
37973             foot: 'no',
37974             motor_vehicle: 'yes',
37975             bicycle: 'no',
37976             horse: 'no'
37977         },
37978         trunk_link: {
37979             motor_vehicle: 'yes'
37980         },
37981         primary_link: {
37982             foot: 'yes',
37983             motor_vehicle: 'yes',
37984             bicycle: 'yes',
37985             horse: 'yes'
37986         },
37987         secondary_link: {
37988             foot: 'yes',
37989             motor_vehicle: 'yes',
37990             bicycle: 'yes',
37991             horse: 'yes'
37992         },
37993         tertiary_link: {
37994             foot: 'yes',
37995             motor_vehicle: 'yes',
37996             bicycle: 'yes',
37997             horse: 'yes'
37998         }
37999     };
38000
38001     access.tags = function(tags) {
38002         items.selectAll('.preset-input-access')
38003             .value(function(d) { return tags[d] || ''; })
38004             .attr('placeholder', function() {
38005                 return tags.access ? tags.access : field.placeholder();
38006             });
38007
38008         // items.selectAll('#preset-input-access-access')
38009         //     .attr('placeholder', 'yes');
38010
38011         _.forEach(placeholders[tags.highway], function(v, k) {
38012             items.selectAll('#preset-input-access-' + k)
38013                 .attr('placeholder', function() { return (tags.access || v); });
38014         });
38015     };
38016
38017     access.focus = function() {
38018         items.selectAll('.preset-input-access')
38019             .node().focus();
38020     };
38021
38022     return d3.rebind(access, dispatch, 'on');
38023 };
38024 iD.ui.preset.address = function(field, context) {
38025     var dispatch = d3.dispatch('init', 'change'),
38026         wrap,
38027         entity,
38028         isInitialized;
38029
38030     var widths = {
38031         housenumber: 1/3,
38032         street: 2/3,
38033         city: 2/3,
38034         state: 1/4,
38035         postcode: 1/3
38036     };
38037
38038     function getStreets() {
38039         var extent = entity.extent(context.graph()),
38040             l = extent.center(),
38041             box = iD.geo.Extent(l).padByMeters(200);
38042
38043         return context.intersects(box)
38044             .filter(isAddressable)
38045             .map(function(d) {
38046                 var loc = context.projection([
38047                     (extent[0][0] + extent[1][0]) / 2,
38048                     (extent[0][1] + extent[1][1]) / 2]),
38049                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
38050                 return {
38051                     title: d.tags.name,
38052                     value: d.tags.name,
38053                     dist: choice.distance
38054                 };
38055             }).sort(function(a, b) {
38056                 return a.dist - b.dist;
38057             });
38058
38059         function isAddressable(d) {
38060             return d.tags.highway && d.tags.name && d.type === 'way';
38061         }
38062     }
38063
38064     function getCities() {
38065         var extent = entity.extent(context.graph()),
38066             l = extent.center(),
38067             box = iD.geo.Extent(l).padByMeters(200);
38068
38069         return context.intersects(box)
38070             .filter(isAddressable)
38071             .map(function(d) {
38072                 return {
38073                     title: d.tags['addr:city'] || d.tags.name,
38074                     value: d.tags['addr:city'] || d.tags.name,
38075                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38076                 };
38077             }).sort(function(a, b) {
38078                 return a.dist - b.dist;
38079             });
38080
38081         function isAddressable(d) {
38082             if (d.tags.name &&
38083                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
38084                 return true;
38085
38086             if (d.tags.place && d.tags.name && (
38087                     d.tags.place === 'city' ||
38088                     d.tags.place === 'town' ||
38089                     d.tags.place === 'village'))
38090                 return true;
38091
38092             if (d.tags['addr:city']) return true;
38093
38094             return false;
38095         }
38096     }
38097
38098     function getPostCodes() {
38099         var extent = entity.extent(context.graph()),
38100             l = extent.center(),
38101             box = iD.geo.Extent(l).padByMeters(200);
38102
38103         return context.intersects(box)
38104             .filter(isAddressable)
38105             .map(function(d) {
38106                 return {
38107                     title: d.tags['addr:postcode'],
38108                     value: d.tags['addr:postcode'],
38109                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38110                 };
38111             }).sort(function(a, b) {
38112                 return a.dist - b.dist;
38113             });
38114
38115         function isAddressable(d) {
38116             return d.tags['addr:postcode'];
38117         }
38118     }
38119
38120     function address(selection) {
38121         isInitialized = false;
38122
38123         wrap = selection.selectAll('.preset-input-wrap')
38124             .data([0]);
38125
38126         // Enter
38127
38128         wrap.enter()
38129             .append('div')
38130             .attr('class', 'preset-input-wrap');
38131
38132         var center = entity.extent(context.graph()).center(),
38133             addressFormat;
38134
38135         iD.services.nominatim().countryCode(center, function (err, countryCode) {
38136             addressFormat = _.find(iD.data.addressFormats, function (a) {
38137                 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
38138             }) || _.first(iD.data.addressFormats);
38139
38140             function row(r) {
38141                 // Normalize widths.
38142                 var total = _.reduce(r, function(sum, field) {
38143                     return sum + (widths[field] || 0.5);
38144                 }, 0);
38145
38146                 return r.map(function (field) {
38147                     return {
38148                         id: field,
38149                         width: (widths[field] || 0.5) / total
38150                     };
38151                 });
38152             }
38153
38154             wrap.selectAll('div')
38155                 .data(addressFormat.format)
38156                 .enter()
38157                 .append('div')
38158                 .attr('class', 'addr-row')
38159                 .selectAll('input')
38160                 .data(row)
38161                 .enter()
38162                 .append('input')
38163                 .property('type', 'text')
38164                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
38165                 .attr('class', function (d) { return 'addr-' + d.id; })
38166                 .style('width', function (d) { return d.width * 100 + '%'; });
38167
38168             // Update
38169
38170             wrap.selectAll('.addr-street')
38171                 .call(d3.combobox()
38172                     .fetcher(function(value, callback) {
38173                         callback(getStreets());
38174                     }));
38175
38176             wrap.selectAll('.addr-city')
38177                 .call(d3.combobox()
38178                     .fetcher(function(value, callback) {
38179                         callback(getCities());
38180                     }));
38181
38182             wrap.selectAll('.addr-postcode')
38183                 .call(d3.combobox()
38184                     .fetcher(function(value, callback) {
38185                         callback(getPostCodes());
38186                     }));
38187
38188             wrap.selectAll('input')
38189                 .on('blur', change())
38190                 .on('change', change());
38191
38192             wrap.selectAll('input:not(.combobox-input)')
38193                 .on('input', change(true));
38194
38195             dispatch.init();
38196             isInitialized = true;
38197         });
38198     }
38199
38200     function change(onInput) {
38201         return function() {
38202             var tags = {};
38203
38204             wrap.selectAll('input')
38205                 .each(function (field) {
38206                     tags['addr:' + field.id] = this.value || undefined;
38207                 });
38208
38209             dispatch.change(tags, onInput);
38210         };
38211     }
38212
38213     function updateTags(tags) {
38214         wrap.selectAll('input')
38215             .value(function (field) {
38216                 return tags['addr:' + field.id] || '';
38217             });
38218     }
38219
38220     address.entity = function(_) {
38221         if (!arguments.length) return entity;
38222         entity = _;
38223         return address;
38224     };
38225
38226     address.tags = function(tags) {
38227         if (isInitialized) {
38228             updateTags(tags);
38229         } else {
38230             dispatch.on('init', function () {
38231                 updateTags(tags);
38232             });
38233         }
38234     };
38235
38236     address.focus = function() {
38237         var node = wrap.selectAll('input').node();
38238         if (node) node.focus();
38239     };
38240
38241     return d3.rebind(address, dispatch, 'on');
38242 };
38243 iD.ui.preset.check =
38244 iD.ui.preset.defaultcheck = function(field) {
38245     var dispatch = d3.dispatch('change'),
38246         options = field.strings && field.strings.options,
38247         values = [],
38248         texts = [],
38249         entity, value, box, text, label;
38250
38251     if (options) {
38252         for (var k in options) {
38253             values.push(k === 'undefined' ? undefined : k);
38254             texts.push(field.t('options.' + k, { 'default': options[k] }));
38255         }
38256     } else {
38257         values = [undefined, 'yes'];
38258         texts = [t('inspector.unknown'), t('inspector.check.yes')];
38259         if (field.type === 'check') {
38260             values.push('no');
38261             texts.push(t('inspector.check.no'));
38262         }
38263     }
38264
38265     var check = function(selection) {
38266         // hack: pretend oneway field is a oneway_yes field
38267         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
38268         if (field.id === 'oneway') {
38269             for (var key in entity.tags) {
38270                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
38271                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
38272                     break;
38273                 }
38274             }
38275         }
38276
38277         selection.classed('checkselect', 'true');
38278
38279         label = selection.selectAll('.preset-input-wrap')
38280             .data([0]);
38281
38282         var enter = label.enter().append('label')
38283             .attr('class', 'preset-input-wrap');
38284
38285         enter.append('input')
38286             .property('indeterminate', field.type === 'check')
38287             .attr('type', 'checkbox')
38288             .attr('id', 'preset-input-' + field.id);
38289
38290         enter.append('span')
38291             .text(texts[0])
38292             .attr('class', 'value');
38293
38294         box = label.select('input')
38295             .on('click', function() {
38296                 var t = {};
38297                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
38298                 dispatch.change(t);
38299                 d3.event.stopPropagation();
38300             });
38301
38302         text = label.select('span.value');
38303     };
38304
38305     check.entity = function(_) {
38306         if (!arguments.length) return entity;
38307         entity = _;
38308         return check;
38309     };
38310
38311     check.tags = function(tags) {
38312         value = tags[field.key];
38313         box.property('indeterminate', field.type === 'check' && !value);
38314         box.property('checked', value === 'yes');
38315         text.text(texts[values.indexOf(value)]);
38316         label.classed('set', !!value);
38317     };
38318
38319     check.focus = function() {
38320         box.node().focus();
38321     };
38322
38323     return d3.rebind(check, dispatch, 'on');
38324 };
38325 iD.ui.preset.combo =
38326 iD.ui.preset.typeCombo = function(field, context) {
38327     var dispatch = d3.dispatch('change'),
38328         optstrings = field.strings && field.strings.options,
38329         optarray = field.options,
38330         snake_case = (field.snake_case || (field.snake_case === undefined)),
38331         strings = {},
38332         input;
38333
38334     function snake(s) {
38335         return s.replace(/\s+/g, '_');
38336     }
38337
38338     function unsnake(s) {
38339         return s.replace(/_+/g, ' ');
38340     }
38341
38342     function clean(s) {
38343         return s.split(';')
38344             .map(function(s) { return s.trim(); })
38345             .join(';');
38346     }
38347
38348     function optString() {
38349         return _.find(_.keys(strings), function(k) {
38350                 return strings[k] === input.value();
38351             });
38352     }
38353
38354     function combo(selection) {
38355         var combobox = d3.combobox();
38356
38357         input = selection.selectAll('input')
38358             .data([0]);
38359
38360         var enter = input.enter()
38361             .append('input')
38362             .attr('type', 'text')
38363             .attr('id', 'preset-input-' + field.id);
38364
38365         if (optstrings) { enter.attr('readonly', 'readonly'); }
38366
38367         input
38368             .call(combobox)
38369             .on('change', change)
38370             .on('blur', change)
38371             .each(function() {
38372                 if (optstrings) {
38373                     _.each(optstrings, function(v, k) {
38374                         strings[k] = field.t('options.' + k, { 'default': v });
38375                     });
38376                     stringsLoaded();
38377                 } else if (optarray) {
38378                     _.each(optarray, function(k) {
38379                         strings[k] = (snake_case ? unsnake(k) : k);
38380                     });
38381                     stringsLoaded();
38382                 } else if (context.taginfo()) {
38383                     context.taginfo().values({key: field.key}, function(err, data) {
38384                         if (!err) {
38385                             _.each(_.pluck(data, 'value'), function(k) {
38386                                 strings[k] = (snake_case ? unsnake(k) : k);
38387                             });
38388                             stringsLoaded();
38389                         }
38390                     });
38391                 }
38392             });
38393
38394         function stringsLoaded() {
38395             var keys = _.keys(strings),
38396                 strs = [],
38397                 placeholders;
38398
38399             combobox.data(keys.map(function(k) {
38400                 var s = strings[k],
38401                     o = {};
38402                 o.title = o.value = s;
38403                 if (s.length < 20) { strs.push(s); }
38404                 return o;
38405             }));
38406
38407             placeholders = strs.length > 1 ? strs : keys;
38408             input.attr('placeholder', field.placeholder() ||
38409                 (placeholders.slice(0, 3).join(', ') + '...'));
38410         }
38411     }
38412
38413     function change() {
38414         var value = optString() || clean(input.value());
38415
38416         if (snake_case) {
38417             value = snake(value);
38418         }
38419         if (field.type === 'typeCombo' && !value) {
38420             value = 'yes';
38421         }
38422
38423         var t = {};
38424         t[field.key] = value || undefined;
38425         dispatch.change(t);
38426     }
38427
38428     combo.tags = function(tags) {
38429         var key = tags[field.key],
38430             optstring = optString(),
38431             value = strings[key] || key || '';
38432
38433         if (field.type === 'typeCombo' && value.toLowerCase() === 'yes') {
38434             value = '';
38435         }
38436         if (!optstring && snake_case) {
38437             value = unsnake(value);
38438         }
38439         input.value(value);
38440     };
38441
38442     combo.focus = function() {
38443         input.node().focus();
38444     };
38445
38446     return d3.rebind(combo, dispatch, 'on');
38447 };
38448 iD.ui.preset.cycleway = function(field) {
38449     var dispatch = d3.dispatch('change'),
38450         items;
38451
38452     function cycleway(selection) {
38453         var wrap = selection.selectAll('.preset-input-wrap')
38454             .data([0]);
38455
38456         wrap.enter().append('div')
38457             .attr('class', 'cf preset-input-wrap')
38458             .append('ul');
38459
38460         items = wrap.select('ul').selectAll('li')
38461             .data(field.keys);
38462
38463         // Enter
38464
38465         var enter = items.enter().append('li')
38466             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
38467
38468         enter.append('span')
38469             .attr('class', 'col6 label preset-label-cycleway')
38470             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
38471             .text(function(d) { return field.t('types.' + d); });
38472
38473         enter.append('div')
38474             .attr('class', 'col6 preset-input-cycleway-wrap')
38475             .append('input')
38476             .attr('type', 'text')
38477             .attr('class', 'preset-input-cycleway')
38478             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
38479             .each(function(d) {
38480                 d3.select(this)
38481                     .call(d3.combobox()
38482                         .data(cycleway.options(d)));
38483             });
38484
38485         // Update
38486
38487         wrap.selectAll('.preset-input-cycleway')
38488             .on('change', change)
38489             .on('blur', change);
38490     }
38491
38492     function change() {
38493         var inputs = d3.selectAll('.preset-input-cycleway')[0],
38494             left = d3.select(inputs[0]).value(),
38495             right = d3.select(inputs[1]).value(),
38496             tag = {};
38497         if (left === 'none' || left === '') { left = undefined; }
38498         if (right === 'none' || right === '') { right = undefined; }
38499
38500         // Always set both left and right as changing one can affect the other
38501         tag = {
38502             cycleway: undefined,
38503             'cycleway:left': left,
38504             'cycleway:right': right
38505         };
38506
38507         // If the left and right tags match, use the cycleway tag to tag both
38508         // sides the same way
38509         if (left === right) {
38510             tag = {
38511                 cycleway: left,
38512                 'cycleway:left': undefined,
38513                 'cycleway:right': undefined
38514             };
38515         }
38516
38517         dispatch.change(tag);
38518     }
38519
38520     cycleway.options = function() {
38521         return d3.keys(field.strings.options).map(function(option) {
38522             return {
38523                 title: field.t('options.' + option + '.description'),
38524                 value: option
38525             };
38526         });
38527     };
38528
38529     cycleway.tags = function(tags) {
38530         items.selectAll('.preset-input-cycleway')
38531             .value(function(d) {
38532                 // If cycleway is set, always return that
38533                 if (tags.cycleway) {
38534                     return tags.cycleway;
38535                 }
38536                 return tags[d] || '';
38537             })
38538             .attr('placeholder', field.placeholder());
38539     };
38540
38541     cycleway.focus = function() {
38542         items.selectAll('.preset-input-cycleway')
38543             .node().focus();
38544     };
38545
38546     return d3.rebind(cycleway, dispatch, 'on');
38547 };
38548 iD.ui.preset.text =
38549 iD.ui.preset.number =
38550 iD.ui.preset.tel =
38551 iD.ui.preset.email =
38552 iD.ui.preset.url = function(field) {
38553
38554     var dispatch = d3.dispatch('change'),
38555         input;
38556
38557     function i(selection) {
38558         input = selection.selectAll('input')
38559             .data([0]);
38560
38561         input.enter().append('input')
38562             .attr('type', field.type)
38563             .attr('id', 'preset-input-' + field.id)
38564             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
38565
38566         input
38567             .on('input', change(true))
38568             .on('blur', change())
38569             .on('change', change());
38570
38571         if (field.type === 'number') {
38572             input.attr('type', 'text');
38573
38574             var spinControl = selection.selectAll('.spin-control')
38575                 .data([0]);
38576
38577             var enter = spinControl.enter().append('div')
38578                 .attr('class', 'spin-control');
38579
38580             enter.append('button')
38581                 .datum(1)
38582                 .attr('class', 'increment');
38583
38584             enter.append('button')
38585                 .datum(-1)
38586                 .attr('class', 'decrement');
38587
38588             spinControl.selectAll('button')
38589                 .on('click', function(d) {
38590                     d3.event.preventDefault();
38591                     var num = parseInt(input.node().value || 0, 10);
38592                     if (!isNaN(num)) input.node().value = num + d;
38593                     change()();
38594                 });
38595         }
38596     }
38597
38598     function change(onInput) {
38599         return function() {
38600             var t = {};
38601             t[field.key] = input.value() || undefined;
38602             dispatch.change(t, onInput);
38603         };
38604     }
38605
38606     i.tags = function(tags) {
38607         input.value(tags[field.key] || '');
38608     };
38609
38610     i.focus = function() {
38611         input.node().focus();
38612     };
38613
38614     return d3.rebind(i, dispatch, 'on');
38615 };
38616 iD.ui.preset.localized = function(field, context) {
38617     var dispatch = d3.dispatch('change', 'input'),
38618         wikipedia = iD.services.wikipedia(),
38619         input, localizedInputs, wikiTitles,
38620         entity;
38621
38622     function i(selection) {
38623         input = selection.selectAll('.localized-main')
38624             .data([0]);
38625
38626         input.enter().append('input')
38627             .attr('type', 'text')
38628             .attr('id', 'preset-input-' + field.id)
38629             .attr('class', 'localized-main')
38630             .attr('placeholder', field.placeholder());
38631
38632         if (field.id === 'name') {
38633             var preset = context.presets().match(entity, context.graph());
38634             input.call(d3.combobox().fetcher(
38635                 iD.util.SuggestNames(preset, iD.data.suggestions)
38636             ));
38637         }
38638
38639         input
38640             .on('input', change(true))
38641             .on('blur', change())
38642             .on('change', change());
38643
38644         var translateButton = selection.selectAll('.localized-add')
38645             .data([0]);
38646
38647         translateButton.enter()
38648             .append('button')
38649             .attr('class', 'button-input-action localized-add minor')
38650             .call(iD.svg.Icon('#icon-plus'))
38651             .call(bootstrap.tooltip()
38652                 .title(t('translate.translate'))
38653                 .placement('left'));
38654
38655         translateButton
38656             .on('click', addNew);
38657
38658         localizedInputs = selection.selectAll('.localized-wrap')
38659             .data([0]);
38660
38661         localizedInputs.enter().append('div')
38662             .attr('class', 'localized-wrap');
38663     }
38664
38665     function addNew() {
38666         d3.event.preventDefault();
38667         var data = localizedInputs.selectAll('div.entry').data();
38668         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
38669         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
38670         var isLangEn = defaultLang.indexOf('en') > -1;
38671         if (isLangEn || langExists) {
38672           defaultLang = '';
38673         }
38674         data.push({ lang: defaultLang, value: '' });
38675         localizedInputs.call(render, data);
38676     }
38677
38678     function change(onInput) {
38679         return function() {
38680             var t = {};
38681             t[field.key] = d3.select(this).value() || undefined;
38682             dispatch.change(t, onInput);
38683         };
38684     }
38685
38686     function key(lang) { return field.key + ':' + lang; }
38687
38688     function changeLang(d) {
38689         var lang = d3.select(this).value(),
38690             t = {},
38691             language = _.find(iD.data.wikipedia, function(d) {
38692                 return d[0].toLowerCase() === lang.toLowerCase() ||
38693                     d[1].toLowerCase() === lang.toLowerCase();
38694             });
38695
38696         if (language) lang = language[2];
38697
38698         if (d.lang && d.lang !== lang) {
38699             t[key(d.lang)] = undefined;
38700         }
38701
38702         var value = d3.select(this.parentNode)
38703             .selectAll('.localized-value')
38704             .value();
38705
38706         if (lang && value) {
38707             t[key(lang)] = value;
38708         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
38709             t[key(lang)] = wikiTitles[d.lang];
38710         }
38711
38712         d.lang = lang;
38713         dispatch.change(t);
38714     }
38715
38716     function changeValue(d) {
38717         if (!d.lang) return;
38718         var t = {};
38719         t[key(d.lang)] = d3.select(this).value() || undefined;
38720         dispatch.change(t);
38721     }
38722
38723     function fetcher(value, cb) {
38724         var v = value.toLowerCase();
38725
38726         cb(iD.data.wikipedia.filter(function(d) {
38727             return d[0].toLowerCase().indexOf(v) >= 0 ||
38728             d[1].toLowerCase().indexOf(v) >= 0 ||
38729             d[2].toLowerCase().indexOf(v) >= 0;
38730         }).map(function(d) {
38731             return { value: d[1] };
38732         }));
38733     }
38734
38735     function render(selection, data) {
38736         var wraps = selection.selectAll('div.entry').
38737             data(data, function(d) { return d.lang; });
38738
38739         var innerWrap = wraps.enter()
38740             .insert('div', ':first-child');
38741
38742         innerWrap.attr('class', 'entry')
38743             .each(function() {
38744                 var wrap = d3.select(this);
38745                 var langcombo = d3.combobox().fetcher(fetcher);
38746
38747                 var label = wrap.append('label')
38748                     .attr('class','form-label')
38749                     .text(t('translate.localized_translation_label'))
38750                     .attr('for','localized-lang');
38751
38752                 label.append('button')
38753                     .attr('class', 'minor remove')
38754                     .on('click', function(d){
38755                         d3.event.preventDefault();
38756                         var t = {};
38757                         t[key(d.lang)] = undefined;
38758                         dispatch.change(t);
38759                         d3.select(this.parentNode.parentNode)
38760                             .style('top','0')
38761                             .style('max-height','240px')
38762                             .transition()
38763                             .style('opacity', '0')
38764                             .style('max-height','0px')
38765                             .remove();
38766                     })
38767                     .call(iD.svg.Icon('#operation-delete'));
38768
38769                 wrap.append('input')
38770                     .attr('class', 'localized-lang')
38771                     .attr('type', 'text')
38772                     .attr('placeholder',t('translate.localized_translation_language'))
38773                     .on('blur', changeLang)
38774                     .on('change', changeLang)
38775                     .call(langcombo);
38776
38777                 wrap.append('input')
38778                     .on('blur', changeValue)
38779                     .on('change', changeValue)
38780                     .attr('type', 'text')
38781                     .attr('placeholder', t('translate.localized_translation_name'))
38782                     .attr('class', 'localized-value');
38783             });
38784
38785         innerWrap
38786             .style('margin-top', '0px')
38787             .style('max-height', '0px')
38788             .style('opacity', '0')
38789             .transition()
38790             .duration(200)
38791             .style('margin-top', '10px')
38792             .style('max-height', '240px')
38793             .style('opacity', '1')
38794             .each('end', function() {
38795                 d3.select(this)
38796                     .style('max-height', '')
38797                     .style('overflow', 'visible');
38798             });
38799
38800         wraps.exit()
38801             .transition()
38802             .duration(200)
38803             .style('max-height','0px')
38804             .style('opacity', '0')
38805             .style('top','-10px')
38806             .remove();
38807
38808         var entry = selection.selectAll('.entry');
38809
38810         entry.select('.localized-lang')
38811             .value(function(d) {
38812                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
38813                 return lang ? lang[1] : d.lang;
38814             });
38815
38816         entry.select('.localized-value')
38817             .value(function(d) { return d.value; });
38818     }
38819
38820     i.tags = function(tags) {
38821         // Fetch translations from wikipedia
38822         if (tags.wikipedia && !wikiTitles) {
38823             wikiTitles = {};
38824             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
38825             if (wm && wm[0] && wm[1]) {
38826                 wikipedia.translations(wm[1], wm[2], function(d) {
38827                     wikiTitles = d;
38828                 });
38829             }
38830         }
38831
38832         input.value(tags[field.key] || '');
38833
38834         var postfixed = [], k, m;
38835         for (k in tags) {
38836             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
38837             if (m && m[1] === field.key && m[2]) {
38838                 postfixed.push({ lang: m[2], value: tags[k] });
38839             }
38840         }
38841
38842         localizedInputs.call(render, postfixed.reverse());
38843     };
38844
38845     i.focus = function() {
38846         input.node().focus();
38847     };
38848
38849     i.entity = function(_) {
38850         entity = _;
38851     };
38852
38853     return d3.rebind(i, dispatch, 'on');
38854 };
38855 iD.ui.preset.maxspeed = function(field, context) {
38856     var dispatch = d3.dispatch('change'),
38857         entity,
38858         imperial,
38859         unitInput,
38860         combobox,
38861         input;
38862
38863     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
38864         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
38865
38866     function maxspeed(selection) {
38867         combobox = d3.combobox();
38868         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
38869
38870         input = selection.selectAll('#preset-input-' + field.id)
38871             .data([0]);
38872
38873         input.enter().append('input')
38874             .attr('type', 'text')
38875             .attr('id', 'preset-input-' + field.id)
38876             .attr('placeholder', field.placeholder());
38877
38878         input
38879             .call(combobox)
38880             .on('change', change)
38881             .on('blur', change);
38882
38883         var childNodes = context.graph().childNodes(context.entity(entity.id)),
38884             loc = childNodes[~~(childNodes.length/2)].loc;
38885
38886         imperial = _.any(iD.data.imperial.features, function(f) {
38887             return _.any(f.geometry.coordinates, function(d) {
38888                 return iD.geo.pointInPolygon(loc, d[0]);
38889             });
38890         });
38891
38892         unitInput = selection.selectAll('input.maxspeed-unit')
38893             .data([0]);
38894
38895         unitInput.enter().append('input')
38896             .attr('type', 'text')
38897             .attr('class', 'maxspeed-unit');
38898
38899         unitInput
38900             .on('blur', changeUnits)
38901             .on('change', changeUnits)
38902             .call(unitCombobox);
38903
38904         function changeUnits() {
38905             imperial = unitInput.value() === 'mph';
38906             unitInput.value(imperial ? 'mph' : 'km/h');
38907             setSuggestions();
38908             change();
38909         }
38910
38911     }
38912
38913     function setSuggestions() {
38914         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
38915         unitInput.value(imperial ? 'mph' : 'km/h');
38916     }
38917
38918     function comboValues(d) {
38919         return {
38920             value: d.toString(),
38921             title: d.toString()
38922         };
38923     }
38924
38925     function change() {
38926         var tag = {},
38927             value = input.value();
38928
38929         if (!value) {
38930             tag[field.key] = undefined;
38931         } else if (isNaN(value) || !imperial) {
38932             tag[field.key] = value;
38933         } else {
38934             tag[field.key] = value + ' mph';
38935         }
38936
38937         dispatch.change(tag);
38938     }
38939
38940     maxspeed.tags = function(tags) {
38941         var value = tags[field.key];
38942
38943         if (value && value.indexOf('mph') >= 0) {
38944             value = parseInt(value, 10);
38945             imperial = true;
38946         } else if (value) {
38947             imperial = false;
38948         }
38949
38950         setSuggestions();
38951
38952         input.value(value || '');
38953     };
38954
38955     maxspeed.focus = function() {
38956         input.node().focus();
38957     };
38958
38959     maxspeed.entity = function(_) {
38960         entity = _;
38961     };
38962
38963     return d3.rebind(maxspeed, dispatch, 'on');
38964 };
38965 iD.ui.preset.radio = function(field) {
38966     var dispatch = d3.dispatch('change'),
38967         labels, radios, placeholder;
38968
38969     function radio(selection) {
38970         selection.classed('preset-radio', true);
38971
38972         var wrap = selection.selectAll('.preset-input-wrap')
38973             .data([0]);
38974
38975         var buttonWrap = wrap.enter().append('div')
38976             .attr('class', 'preset-input-wrap toggle-list');
38977
38978         buttonWrap.append('span')
38979             .attr('class', 'placeholder');
38980
38981         placeholder = selection.selectAll('.placeholder');
38982
38983         labels = wrap.selectAll('label')
38984             .data(field.options || field.keys);
38985
38986         var enter = labels.enter().append('label');
38987
38988         enter.append('input')
38989             .attr('type', 'radio')
38990             .attr('name', field.id)
38991             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
38992             .attr('checked', false);
38993
38994         enter.append('span')
38995             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
38996
38997         radios = labels.selectAll('input')
38998             .on('change', change);
38999     }
39000
39001     function change() {
39002         var t = {};
39003         if (field.key) t[field.key] = undefined;
39004         radios.each(function(d) {
39005             var active = d3.select(this).property('checked');
39006             if (field.key) {
39007                 if (active) t[field.key] = d;
39008             } else {
39009                 t[d] = active ? 'yes' : undefined;
39010             }
39011         });
39012         dispatch.change(t);
39013     }
39014
39015     radio.tags = function(tags) {
39016         function checked(d) {
39017             if (field.key) {
39018                 return tags[field.key] === d;
39019             } else {
39020                 return !!(tags[d] && tags[d] !== 'no');
39021             }
39022         }
39023
39024         labels.classed('active', checked);
39025         radios.property('checked', checked);
39026         var selection = radios.filter(function() { return this.checked; });
39027         if (selection.empty()) {
39028             placeholder.text(t('inspector.none'));
39029         } else {
39030             placeholder.text(selection.attr('value'));
39031         }
39032     };
39033
39034     radio.focus = function() {
39035         radios.node().focus();
39036     };
39037
39038     return d3.rebind(radio, dispatch, 'on');
39039 };
39040 iD.ui.preset.restrictions = function(field, context) {
39041     var dispatch = d3.dispatch('change'),
39042         hover = iD.behavior.Hover(context),
39043         vertexID,
39044         fromNodeID;
39045
39046
39047     function restrictions(selection) {
39048         // if form field is hidden or has detached from dom, clean up.
39049         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
39050             selection.call(restrictions.off);
39051             return;
39052         }
39053
39054         var wrap = selection.selectAll('.preset-input-wrap')
39055             .data([0]);
39056
39057         var enter = wrap.enter()
39058             .append('div')
39059             .attr('class', 'preset-input-wrap');
39060
39061         enter
39062             .append('div')
39063             .attr('class', 'restriction-help');
39064
39065
39066         var intersection = iD.geo.Intersection(context.graph(), vertexID),
39067             graph = intersection.graph,
39068             vertex = graph.entity(vertexID),
39069             filter = d3.functor(true),
39070             extent = iD.geo.Extent(),
39071             projection = iD.geo.RawMercator();
39072
39073         var d = wrap.dimensions(),
39074             c = [d[0] / 2, d[1] / 2],
39075             z = 24;
39076
39077         projection
39078             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
39079
39080         var s = projection(vertex.loc);
39081
39082         projection
39083             .translate([c[0] - s[0], c[1] - s[1]])
39084             .clipExtent([[0, 0], d]);
39085
39086         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
39087             drawVertices = iD.svg.Vertices(projection, context),
39088             drawLines = iD.svg.Lines(projection, context),
39089             drawTurns = iD.svg.Turns(projection, context);
39090
39091         enter
39092             .call(drawLayers)
39093             .selectAll('.surface')
39094             .call(hover);
39095
39096
39097         var surface = wrap.selectAll('.surface');
39098
39099         surface
39100             .dimensions(d)
39101             .call(drawVertices, graph, [vertex], filter, extent, z)
39102             .call(drawLines, graph, intersection.ways, filter)
39103             .call(drawTurns, graph, intersection.turns(fromNodeID));
39104
39105         surface
39106             .on('click.restrictions', click)
39107             .on('mouseover.restrictions', mouseover)
39108             .on('mouseout.restrictions', mouseout);
39109
39110         surface
39111             .selectAll('.selected')
39112             .classed('selected', false);
39113
39114         if (fromNodeID) {
39115             surface
39116                 .selectAll('.' + intersection.highways[fromNodeID].id)
39117                 .classed('selected', true);
39118         }
39119
39120         mouseout();
39121
39122         context.history()
39123             .on('change.restrictions', render);
39124
39125         d3.select(window)
39126             .on('resize.restrictions', function() {
39127                 wrap.dimensions(null);
39128                 render();
39129             });
39130
39131         function click() {
39132             var datum = d3.event.target.__data__;
39133             if (datum instanceof iD.Entity) {
39134                 fromNodeID = intersection.adjacentNodeId(datum.id);
39135                 render();
39136             } else if (datum instanceof iD.geo.Turn) {
39137                 if (datum.restriction) {
39138                     context.perform(
39139                         iD.actions.UnrestrictTurn(datum, projection),
39140                         t('operations.restriction.annotation.delete'));
39141                 } else {
39142                     context.perform(
39143                         iD.actions.RestrictTurn(datum, projection),
39144                         t('operations.restriction.annotation.create'));
39145                 }
39146             }
39147         }
39148
39149         function mouseover() {
39150             var datum = d3.event.target.__data__;
39151             if (datum instanceof iD.geo.Turn) {
39152                 var graph = context.graph(),
39153                     presets = context.presets(),
39154                     preset;
39155
39156                 if (datum.restriction) {
39157                     preset = presets.match(graph.entity(datum.restriction), graph);
39158                 } else {
39159                     preset = presets.item('type/restriction/' +
39160                         iD.geo.inferRestriction(
39161                             graph,
39162                             datum.from,
39163                             datum.via,
39164                             datum.to,
39165                             projection));
39166                 }
39167
39168                 wrap.selectAll('.restriction-help')
39169                     .text(t('operations.restriction.help.' +
39170                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
39171                         {restriction: preset.name()}));
39172             }
39173         }
39174
39175         function mouseout() {
39176             wrap.selectAll('.restriction-help')
39177                 .text(t('operations.restriction.help.' +
39178                     (fromNodeID ? 'toggle' : 'select')));
39179         }
39180
39181         function render() {
39182             if (context.hasEntity(vertexID)) {
39183                 restrictions(selection);
39184             }
39185         }
39186     }
39187
39188     restrictions.entity = function(_) {
39189         if (!vertexID || vertexID !== _.id) {
39190             fromNodeID = null;
39191             vertexID = _.id;
39192         }
39193     };
39194
39195     restrictions.tags = function() {};
39196     restrictions.focus = function() {};
39197
39198     restrictions.off = function(selection) {
39199         selection.selectAll('.surface')
39200             .call(hover.off)
39201             .on('click.restrictions', null)
39202             .on('mouseover.restrictions', null)
39203             .on('mouseout.restrictions', null);
39204
39205         context.history()
39206             .on('change.restrictions', null);
39207
39208         d3.select(window)
39209             .on('resize.restrictions', null);
39210     };
39211
39212     return d3.rebind(restrictions, dispatch, 'on');
39213 };
39214 iD.ui.preset.textarea = function(field) {
39215     var dispatch = d3.dispatch('change'),
39216         input;
39217
39218     function i(selection) {
39219         input = selection.selectAll('textarea')
39220             .data([0]);
39221
39222         input.enter().append('textarea')
39223             .attr('id', 'preset-input-' + field.id)
39224             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
39225             .attr('maxlength', 255);
39226
39227         input
39228             .on('input', change(true))
39229             .on('blur', change())
39230             .on('change', change());
39231     }
39232
39233     function change(onInput) {
39234         return function() {
39235             var t = {};
39236             t[field.key] = input.value() || undefined;
39237             dispatch.change(t, onInput);
39238         };
39239     }
39240
39241     i.tags = function(tags) {
39242         input.value(tags[field.key] || '');
39243     };
39244
39245     i.focus = function() {
39246         input.node().focus();
39247     };
39248
39249     return d3.rebind(i, dispatch, 'on');
39250 };
39251 iD.ui.preset.wikipedia = function(field, context) {
39252     var dispatch = d3.dispatch('change'),
39253         wikipedia = iD.services.wikipedia(),
39254         link, entity, lang, title;
39255
39256     function i(selection) {
39257         var langcombo = d3.combobox()
39258             .fetcher(function(value, cb) {
39259                 var v = value.toLowerCase();
39260
39261                 cb(iD.data.wikipedia.filter(function(d) {
39262                     return d[0].toLowerCase().indexOf(v) >= 0 ||
39263                         d[1].toLowerCase().indexOf(v) >= 0 ||
39264                         d[2].toLowerCase().indexOf(v) >= 0;
39265                 }).map(function(d) {
39266                     return { value: d[1] };
39267                 }));
39268             });
39269
39270         var titlecombo = d3.combobox()
39271             .fetcher(function(value, cb) {
39272
39273                 if (!value) value = context.entity(entity.id).tags.name || '';
39274                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
39275
39276                 searchfn(language()[2], value, function(query, data) {
39277                     cb(data.map(function(d) {
39278                         return { value: d };
39279                     }));
39280                 });
39281             });
39282
39283         lang = selection.selectAll('input.wiki-lang')
39284             .data([0]);
39285
39286         lang.enter().append('input')
39287             .attr('type', 'text')
39288             .attr('class', 'wiki-lang')
39289             .value('English');
39290
39291         lang
39292             .call(langcombo)
39293             .on('blur', changeLang)
39294             .on('change', changeLang);
39295
39296         title = selection.selectAll('input.wiki-title')
39297             .data([0]);
39298
39299         title.enter().append('input')
39300             .attr('type', 'text')
39301             .attr('class', 'wiki-title')
39302             .attr('id', 'preset-input-' + field.id);
39303
39304         title
39305             .call(titlecombo)
39306             .on('blur', change)
39307             .on('change', change);
39308
39309         link = selection.selectAll('a.wiki-link')
39310             .data([0]);
39311
39312         link.enter().append('a')
39313             .attr('class', 'wiki-link button-input-action minor')
39314             .attr('target', '_blank')
39315             .call(iD.svg.Icon('#icon-out-link', 'inline'));
39316     }
39317
39318     function language() {
39319         var value = lang.value().toLowerCase();
39320         var locale = iD.detect().locale.toLowerCase();
39321         var localeLanguage;
39322         return _.find(iD.data.wikipedia, function(d) {
39323             if (d[2] === locale) localeLanguage = d;
39324             return d[0].toLowerCase() === value ||
39325                 d[1].toLowerCase() === value ||
39326                 d[2] === value;
39327         }) || localeLanguage || ['English', 'English', 'en'];
39328     }
39329
39330     function changeLang() {
39331         lang.value(language()[1]);
39332         change();
39333     }
39334
39335     function change() {
39336         var value = title.value(),
39337             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
39338             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39339             anchor;
39340
39341         if (l) {
39342             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
39343             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
39344             if (m[3]) {
39345                 try {
39346                     // Best-effort `anchordecode:` implementation
39347                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
39348                 } catch (e) {
39349                     anchor = decodeURIComponent(m[3]);
39350                 }
39351                 value += '#' + anchor.replace(/_/g, ' ');
39352             }
39353             value = value.slice(0, 1).toUpperCase() + value.slice(1);
39354             lang.value(l[1]);
39355             title.value(value);
39356         }
39357
39358         var t = {};
39359         t[field.key] = value ? language()[2] + ':' + value : undefined;
39360         dispatch.change(t);
39361     }
39362
39363     i.tags = function(tags) {
39364         var value = tags[field.key] || '',
39365             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
39366             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39367             anchor = m && m[3];
39368
39369         // value in correct format
39370         if (l) {
39371             lang.value(l[1]);
39372             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
39373             if (anchor) {
39374                 try {
39375                     // Best-effort `anchorencode:` implementation
39376                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
39377                 } catch (e) {
39378                     anchor = anchor.replace(/ /g, '_');
39379                 }
39380             }
39381             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
39382                       m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
39383
39384         // unrecognized value format
39385         } else {
39386             title.value(value);
39387             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
39388         }
39389     };
39390
39391     i.entity = function(_) {
39392         entity = _;
39393     };
39394
39395     i.focus = function() {
39396         title.node().focus();
39397     };
39398
39399     return d3.rebind(i, dispatch, 'on');
39400 };
39401 iD.ui.intro.area = function(context, reveal) {
39402     var event = d3.dispatch('done'),
39403         timeout;
39404
39405     var step = {
39406         title: 'intro.areas.title'
39407     };
39408
39409     step.enter = function() {
39410         var playground = [-85.63552, 41.94159],
39411             corner = [-85.63565411045074, 41.9417715536927];
39412         context.map().centerZoom(playground, 19);
39413         reveal('button.add-area',
39414             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
39415             { tooltipClass: 'intro-areas-add' });
39416
39417         context.on('enter.intro', addArea);
39418
39419         function addArea(mode) {
39420             if (mode.id !== 'add-area') return;
39421             context.on('enter.intro', drawArea);
39422
39423             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
39424             var pointBox = iD.ui.intro.pad(corner, padding, context);
39425             reveal(pointBox, t('intro.areas.corner'));
39426
39427             context.map().on('move.intro', function() {
39428                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
39429                 pointBox = iD.ui.intro.pad(corner, padding, context);
39430                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
39431             });
39432         }
39433
39434         function drawArea(mode) {
39435             if (mode.id !== 'draw-area') return;
39436             context.on('enter.intro', enterSelect);
39437
39438             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
39439             var pointBox = iD.ui.intro.pad(playground, padding, context);
39440             reveal(pointBox, t('intro.areas.place'));
39441
39442             context.map().on('move.intro', function() {
39443                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
39444                 pointBox = iD.ui.intro.pad(playground, padding, context);
39445                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
39446             });
39447         }
39448
39449         function enterSelect(mode) {
39450             if (mode.id !== 'select') return;
39451             context.map().on('move.intro', null);
39452             context.on('enter.intro', null);
39453
39454             timeout = setTimeout(function() {
39455                 reveal('.preset-search-input',
39456                     t('intro.areas.search',
39457                     { name: context.presets().item('leisure/playground').name() }));
39458                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39459             }, 500);
39460         }
39461
39462         function keySearch() {
39463             var first = d3.select('.preset-list-item:first-child');
39464             if (first.classed('preset-leisure-playground')) {
39465                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
39466                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39467                 d3.select('.preset-search-input').on('keyup.intro', null);
39468             }
39469         }
39470
39471         function selectedPreset() {
39472             reveal('.pane',
39473                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39474             context.on('exit.intro', event.done);
39475         }
39476     };
39477
39478     step.exit = function() {
39479         window.clearTimeout(timeout);
39480         context.on('enter.intro', null);
39481         context.on('exit.intro', null);
39482         context.history().on('change.intro', null);
39483         context.map().on('move.intro', null);
39484         d3.select('.preset-search-input').on('keyup.intro', null);
39485     };
39486
39487     return d3.rebind(step, event, 'on');
39488 };
39489 iD.ui.intro.line = function(context, reveal) {
39490     var event = d3.dispatch('done'),
39491         timeouts = [];
39492
39493     var step = {
39494         title: 'intro.lines.title'
39495     };
39496
39497     function timeout(f, t) {
39498         timeouts.push(window.setTimeout(f, t));
39499     }
39500
39501     function eventCancel() {
39502         d3.event.stopPropagation();
39503         d3.event.preventDefault();
39504     }
39505
39506     step.enter = function() {
39507         var centroid = [-85.62830, 41.95699];
39508         var midpoint = [-85.62975395449628, 41.95787501510204];
39509         var start = [-85.6297754121684, 41.95805253325314];
39510         var intersection = [-85.62974496187628, 41.95742515554585];
39511
39512         context.map().centerZoom(start, 18);
39513         reveal('button.add-line',
39514             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
39515             { tooltipClass: 'intro-lines-add' });
39516
39517         context.on('enter.intro', addLine);
39518
39519         function addLine(mode) {
39520             if (mode.id !== 'add-line') return;
39521             context.on('enter.intro', drawLine);
39522
39523             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
39524             var pointBox = iD.ui.intro.pad(start, padding, context);
39525             reveal(pointBox, t('intro.lines.start'));
39526
39527             context.map().on('move.intro', function() {
39528                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
39529                 pointBox = iD.ui.intro.pad(start, padding, context);
39530                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
39531             });
39532         }
39533
39534         function drawLine(mode) {
39535             if (mode.id !== 'draw-line') return;
39536             context.history().on('change.intro', addIntersection);
39537             context.on('enter.intro', retry);
39538
39539             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
39540             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
39541             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
39542
39543             context.map().on('move.intro', function() {
39544                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
39545                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
39546                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
39547             });
39548         }
39549
39550         // ended line before creating intersection
39551         function retry(mode) {
39552             if (mode.id !== 'select') return;
39553             var pointBox = iD.ui.intro.pad(intersection, 30, context),
39554                 ids = mode.selectedIDs();
39555             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
39556             d3.select(window).on('mousedown.intro', eventCancel, true);
39557
39558             timeout(function() {
39559                 context.replace(iD.actions.DeleteMultiple(ids));
39560                 step.exit();
39561                 step.enter();
39562             }, 3000);
39563         }
39564
39565         function addIntersection(changes) {
39566             if ( _.any(changes.created(), function(d) {
39567                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
39568             })) {
39569                 context.history().on('change.intro', null);
39570                 context.on('enter.intro', enterSelect);
39571
39572                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
39573                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
39574                 reveal(pointBox, t('intro.lines.finish'));
39575
39576                 context.map().on('move.intro', function() {
39577                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
39578                     pointBox = iD.ui.intro.pad(centroid, padding, context);
39579                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
39580                 });
39581             }
39582         }
39583
39584         function enterSelect(mode) {
39585             if (mode.id !== 'select') return;
39586             context.map().on('move.intro', null);
39587             context.on('enter.intro', null);
39588             d3.select('#curtain').style('pointer-events', 'all');
39589
39590             presetCategory();
39591         }
39592
39593         function presetCategory() {
39594             timeout(function() {
39595                 d3.select('#curtain').style('pointer-events', 'none');
39596                 var road = d3.select('.preset-category-road .preset-list-button');
39597                 reveal(road.node(), t('intro.lines.road'));
39598                 road.one('click.intro', roadCategory);
39599             }, 500);
39600         }
39601
39602         function roadCategory() {
39603             timeout(function() {
39604                 var grid = d3.select('.subgrid');
39605                 reveal(grid.node(), t('intro.lines.residential'));
39606                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
39607                     .one('click.intro', retryPreset);
39608                 grid.selectAll('.preset-highway-residential .preset-list-button')
39609                     .one('click.intro', roadDetails);
39610             }, 500);
39611         }
39612
39613         // selected wrong road type
39614         function retryPreset() {
39615             timeout(function() {
39616                 var preset = d3.select('.entity-editor-pane .preset-list-button');
39617                 reveal(preset.node(), t('intro.lines.wrong_preset'));
39618                 preset.one('click.intro', presetCategory);
39619             }, 500);
39620         }
39621
39622         function roadDetails() {
39623             reveal('.pane',
39624                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39625             context.on('exit.intro', event.done);
39626         }
39627
39628     };
39629
39630     step.exit = function() {
39631         d3.select(window).on('mousedown.intro', null, true);
39632         d3.select('#curtain').style('pointer-events', 'none');
39633         timeouts.forEach(window.clearTimeout);
39634         context.on('enter.intro', null);
39635         context.on('exit.intro', null);
39636         context.map().on('move.intro', null);
39637         context.history().on('change.intro', null);
39638     };
39639
39640     return d3.rebind(step, event, 'on');
39641 };
39642 iD.ui.intro.navigation = function(context, reveal) {
39643     var event = d3.dispatch('done'),
39644         timeouts = [];
39645
39646     var step = {
39647         title: 'intro.navigation.title'
39648     };
39649
39650     function set(f, t) {
39651         timeouts.push(window.setTimeout(f, t));
39652     }
39653
39654     function eventCancel() {
39655         d3.event.stopPropagation();
39656         d3.event.preventDefault();
39657     }
39658
39659     step.enter = function() {
39660         var rect = context.surfaceRect(),
39661             map = {
39662                 left: rect.left + 10,
39663                 top: rect.top + 70,
39664                 width: rect.width - 70,
39665                 height: rect.height - 170
39666             };
39667
39668         context.map().centerZoom([-85.63591, 41.94285], 19);
39669
39670         reveal(map, t('intro.navigation.drag'));
39671
39672         context.map().on('move.intro', _.debounce(function() {
39673             context.map().on('move.intro', null);
39674             townhall();
39675             context.on('enter.intro', inspectTownHall);
39676         }, 400));
39677
39678         function townhall() {
39679             var hall = [-85.63645945147184, 41.942986488012565];
39680
39681             var point = context.projection(hall);
39682             if (point[0] < 0 || point[0] > rect.width ||
39683                 point[1] < 0 || point[1] > rect.height) {
39684                 context.map().center(hall);
39685             }
39686
39687             var box = iD.ui.intro.pointBox(hall, context);
39688             reveal(box, t('intro.navigation.select'));
39689
39690             context.map().on('move.intro', function() {
39691                 var box = iD.ui.intro.pointBox(hall, context);
39692                 reveal(box, t('intro.navigation.select'), {duration: 0});
39693             });
39694         }
39695
39696         function inspectTownHall(mode) {
39697             if (mode.id !== 'select') return;
39698             context.on('enter.intro', null);
39699             context.map().on('move.intro', null);
39700             set(function() {
39701                 reveal('.entity-editor-pane',
39702                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
39703                 context.on('exit.intro', streetSearch);
39704             }, 700);
39705         }
39706
39707         function streetSearch() {
39708             context.on('exit.intro', null);
39709             reveal('.search-header input',
39710                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
39711             d3.select('.search-header input').on('keyup.intro', searchResult);
39712         }
39713
39714         function searchResult() {
39715             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
39716                 firstName = first.select('.entity-name'),
39717                 name = t('intro.graph.spring_st');
39718
39719             if (!firstName.empty() && firstName.text() === name) {
39720                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
39721                 context.on('exit.intro', selectedStreet);
39722                 d3.select('.search-header input')
39723                     .on('keydown.intro', eventCancel, true)
39724                     .on('keyup.intro', null);
39725             }
39726         }
39727
39728         function selectedStreet() {
39729             var springSt = [-85.63585099140167, 41.942506848938926];
39730             context.map().center(springSt);
39731             context.on('exit.intro', event.done);
39732             set(function() {
39733                 reveal('.entity-editor-pane',
39734                     t('intro.navigation.chosen', {
39735                         name: t('intro.graph.spring_st'),
39736                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
39737                     }));
39738             }, 400);
39739         }
39740     };
39741
39742     step.exit = function() {
39743         timeouts.forEach(window.clearTimeout);
39744         context.map().on('move.intro', null);
39745         context.on('enter.intro', null);
39746         context.on('exit.intro', null);
39747         d3.select('.search-header input')
39748             .on('keydown.intro', null)
39749             .on('keyup.intro', null);
39750     };
39751
39752     return d3.rebind(step, event, 'on');
39753 };
39754 iD.ui.intro.point = function(context, reveal) {
39755     var event = d3.dispatch('done'),
39756         timeouts = [];
39757
39758     var step = {
39759         title: 'intro.points.title'
39760     };
39761
39762     function setTimeout(f, t) {
39763         timeouts.push(window.setTimeout(f, t));
39764     }
39765
39766     function eventCancel() {
39767         d3.event.stopPropagation();
39768         d3.event.preventDefault();
39769     }
39770
39771     step.enter = function() {
39772         context.map().centerZoom([-85.63279, 41.94394], 19);
39773         reveal('button.add-point',
39774             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
39775             { tooltipClass: 'intro-points-add' });
39776
39777         var corner = [-85.632481,41.944094];
39778
39779         context.on('enter.intro', addPoint);
39780
39781         function addPoint(mode) {
39782             if (mode.id !== 'add-point') return;
39783             context.on('enter.intro', enterSelect);
39784
39785             var pointBox = iD.ui.intro.pad(corner, 150, context);
39786             reveal(pointBox, t('intro.points.place'));
39787
39788             context.map().on('move.intro', function() {
39789                 pointBox = iD.ui.intro.pad(corner, 150, context);
39790                 reveal(pointBox, t('intro.points.place'), {duration: 0});
39791             });
39792         }
39793
39794         function enterSelect(mode) {
39795             if (mode.id !== 'select') return;
39796             context.map().on('move.intro', null);
39797             context.on('enter.intro', null);
39798
39799             setTimeout(function() {
39800                 reveal('.preset-search-input',
39801                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
39802                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39803             }, 500);
39804         }
39805
39806         function keySearch() {
39807             var first = d3.select('.preset-list-item:first-child');
39808             if (first.classed('preset-amenity-cafe')) {
39809                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
39810                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39811                 d3.select('.preset-search-input')
39812                     .on('keydown.intro', eventCancel, true)
39813                     .on('keyup.intro', null);
39814             }
39815         }
39816
39817         function selectedPreset() {
39818             setTimeout(function() {
39819                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
39820                 context.history().on('change.intro', closeEditor);
39821                 context.on('exit.intro', selectPoint);
39822             }, 400);
39823         }
39824
39825         function closeEditor() {
39826             d3.select('.preset-search-input').on('keydown.intro', null);
39827             context.history().on('change.intro', null);
39828             reveal('.entity-editor-pane',
39829                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39830         }
39831
39832         function selectPoint() {
39833             context.on('exit.intro', null);
39834             context.history().on('change.intro', null);
39835             context.on('enter.intro', enterReselect);
39836
39837             var pointBox = iD.ui.intro.pad(corner, 150, context);
39838             reveal(pointBox, t('intro.points.reselect'));
39839
39840             context.map().on('move.intro', function() {
39841                 pointBox = iD.ui.intro.pad(corner, 150, context);
39842                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
39843             });
39844         }
39845
39846         function enterReselect(mode) {
39847             if (mode.id !== 'select') return;
39848             context.map().on('move.intro', null);
39849             context.on('enter.intro', null);
39850
39851             setTimeout(function() {
39852                 reveal('.entity-editor-pane',
39853                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39854                 context.on('exit.intro', deletePoint);
39855             }, 500);
39856         }
39857
39858         function deletePoint() {
39859             context.on('exit.intro', null);
39860             context.on('enter.intro', enterDelete);
39861
39862             var pointBox = iD.ui.intro.pad(corner, 150, context);
39863             reveal(pointBox, t('intro.points.reselect_delete'));
39864
39865             context.map().on('move.intro', function() {
39866                 pointBox = iD.ui.intro.pad(corner, 150, context);
39867                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
39868             });
39869         }
39870
39871         function enterDelete(mode) {
39872             if (mode.id !== 'select') return;
39873             context.map().on('move.intro', null);
39874             context.on('enter.intro', null);
39875             context.on('exit.intro', deletePoint);
39876             context.map().on('move.intro', deletePoint);
39877             context.history().on('change.intro', deleted);
39878
39879             setTimeout(function() {
39880                 var node = d3.select('.radial-menu-item-delete').node();
39881                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
39882                 reveal(pointBox,
39883                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
39884             }, 300);
39885         }
39886
39887         function deleted(changed) {
39888             if (changed.deleted().length) event.done();
39889         }
39890
39891     };
39892
39893     step.exit = function() {
39894         timeouts.forEach(window.clearTimeout);
39895         context.on('exit.intro', null);
39896         context.on('enter.intro', null);
39897         context.map().on('move.intro', null);
39898         context.history().on('change.intro', null);
39899         d3.select('.preset-search-input')
39900             .on('keyup.intro', null)
39901             .on('keydown.intro', null);
39902     };
39903
39904     return d3.rebind(step, event, 'on');
39905 };
39906 iD.ui.intro.startEditing = function(context, reveal) {
39907     var event = d3.dispatch('done', 'startEditing'),
39908         modal,
39909         timeouts = [];
39910
39911     var step = {
39912         title: 'intro.startediting.title'
39913     };
39914
39915     function timeout(f, t) {
39916         timeouts.push(window.setTimeout(f, t));
39917     }
39918
39919     step.enter = function() {
39920         reveal('.map-control.help-control',
39921             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
39922
39923         timeout(function() {
39924             reveal('#bar button.save', t('intro.startediting.save'));
39925         }, 5000);
39926
39927         timeout(function() {
39928             reveal('#surface');
39929         }, 10000);
39930
39931         timeout(function() {
39932             modal = iD.ui.modal(context.container());
39933
39934             modal.select('.modal')
39935                 .attr('class', 'modal-splash modal col6');
39936
39937             modal.selectAll('.close').remove();
39938
39939             var startbutton = modal.select('.content')
39940                 .attr('class', 'fillL')
39941                     .append('button')
39942                         .attr('class', 'modal-section huge-modal-button')
39943                         .on('click', function() {
39944                             modal.remove();
39945                         });
39946
39947                 startbutton.append('div')
39948                     .attr('class','illustration');
39949                 startbutton.append('h2')
39950                     .text(t('intro.startediting.start'));
39951
39952             event.startEditing();
39953         }, 10500);
39954     };
39955
39956     step.exit = function() {
39957         if (modal) modal.remove();
39958         timeouts.forEach(window.clearTimeout);
39959     };
39960
39961     return d3.rebind(step, event, 'on');
39962 };
39963 iD.presets = function() {
39964
39965     // an iD.presets.Collection with methods for
39966     // loading new data and returning defaults
39967
39968     var all = iD.presets.Collection([]),
39969         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
39970         fields = {},
39971         universal = [],
39972         recent = iD.presets.Collection([]);
39973
39974     // Index of presets by (geometry, tag key).
39975     var index = {
39976         point: {},
39977         vertex: {},
39978         line: {},
39979         area: {},
39980         relation: {}
39981     };
39982
39983     all.match = function(entity, resolver) {
39984         var geometry = entity.geometry(resolver),
39985             geometryMatches = index[geometry],
39986             best = -1,
39987             match;
39988
39989         for (var k in entity.tags) {
39990             var keyMatches = geometryMatches[k];
39991             if (!keyMatches) continue;
39992
39993             for (var i = 0; i < keyMatches.length; i++) {
39994                 var score = keyMatches[i].matchScore(entity);
39995                 if (score > best) {
39996                     best = score;
39997                     match = keyMatches[i];
39998                 }
39999             }
40000         }
40001
40002         return match || all.item(geometry);
40003     };
40004
40005     // Because of the open nature of tagging, iD will never have a complete
40006     // list of tags used in OSM, so we want it to have logic like "assume
40007     // that a closed way with an amenity tag is an area, unless the amenity
40008     // is one of these specific types". This function computes a structure
40009     // that allows testing of such conditions, based on the presets designated
40010     // as as supporting (or not supporting) the area geometry.
40011     //
40012     // The returned object L is a whitelist/blacklist of tags. A closed way
40013     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
40014     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
40015     // and the subkeys form the blacklist.
40016     all.areaKeys = function() {
40017         var areaKeys = {},
40018             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
40019             presets = _.reject(all.collection, 'suggestion');
40020
40021         // whitelist
40022         presets.forEach(function(d) {
40023             for (var key in d.tags) break;
40024             if (!key) return;
40025             if (ignore.indexOf(key) !== -1) return;
40026
40027             if (d.geometry.indexOf('area') !== -1) {
40028                 areaKeys[key] = areaKeys[key] || {};
40029             }
40030         });
40031
40032         // blacklist
40033         presets.forEach(function(d) {
40034             for (var key in d.tags) break;
40035             if (!key) return;
40036             if (ignore.indexOf(key) !== -1) return;
40037
40038             var value = d.tags[key];
40039             if (d.geometry.indexOf('area') === -1 && key in areaKeys && value !== '*') {
40040                 areaKeys[key][value] = true;
40041             }
40042         });
40043
40044         return areaKeys;
40045     };
40046
40047     all.load = function(d) {
40048
40049         if (d.fields) {
40050             _.forEach(d.fields, function(d, id) {
40051                 fields[id] = iD.presets.Field(id, d);
40052                 if (d.universal) universal.push(fields[id]);
40053             });
40054         }
40055
40056         if (d.presets) {
40057             _.forEach(d.presets, function(d, id) {
40058                 all.collection.push(iD.presets.Preset(id, d, fields));
40059             });
40060         }
40061
40062         if (d.categories) {
40063             _.forEach(d.categories, function(d, id) {
40064                 all.collection.push(iD.presets.Category(id, d, all));
40065             });
40066         }
40067
40068         if (d.defaults) {
40069             var getItem = _.bind(all.item, all);
40070             defaults = {
40071                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
40072                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
40073                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
40074                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
40075                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
40076             };
40077         }
40078
40079         for (var i = 0; i < all.collection.length; i++) {
40080             var preset = all.collection[i],
40081                 geometry = preset.geometry;
40082
40083             for (var j = 0; j < geometry.length; j++) {
40084                 var g = index[geometry[j]];
40085                 for (var k in preset.tags) {
40086                     (g[k] = g[k] || []).push(preset);
40087                 }
40088             }
40089         }
40090
40091         return all;
40092     };
40093
40094     all.field = function(id) {
40095         return fields[id];
40096     };
40097
40098     all.universal = function() {
40099         return universal;
40100     };
40101
40102     all.defaults = function(geometry, n) {
40103         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
40104             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
40105         return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
40106     };
40107
40108     all.choose = function(preset) {
40109         if (!preset.isFallback()) {
40110             recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
40111         }
40112         return all;
40113     };
40114
40115     return all;
40116 };
40117 iD.presets.Category = function(id, category, all) {
40118     category = _.clone(category);
40119
40120     category.id = id;
40121
40122     category.members = iD.presets.Collection(category.members.map(function(id) {
40123         return all.item(id);
40124     }));
40125
40126     category.matchGeometry = function(geometry) {
40127         return category.geometry.indexOf(geometry) >= 0;
40128     };
40129
40130     category.matchScore = function() { return -1; };
40131
40132     category.name = function() {
40133         return t('presets.categories.' + id + '.name', {'default': id});
40134     };
40135
40136     category.terms = function() {
40137         return [];
40138     };
40139
40140     return category;
40141 };
40142 iD.presets.Collection = function(collection) {
40143
40144     var maxSearchResults = 50,
40145         maxSuggestionResults = 10;
40146
40147     var presets = {
40148
40149         collection: collection,
40150
40151         item: function(id) {
40152             return _.find(collection, function(d) {
40153                 return d.id === id;
40154             });
40155         },
40156
40157         matchGeometry: function(geometry) {
40158             return iD.presets.Collection(collection.filter(function(d) {
40159                 return d.matchGeometry(geometry);
40160             }));
40161         },
40162
40163         search: function(value, geometry) {
40164             if (!value) return this;
40165
40166             value = value.toLowerCase();
40167
40168             var searchable = _.filter(collection, function(a) {
40169                     return a.searchable !== false && a.suggestion !== true;
40170                 }),
40171                 suggestions = _.filter(collection, function(a) {
40172                     return a.suggestion === true;
40173                 });
40174
40175             function leading(a) {
40176                 var index = a.indexOf(value);
40177                 return index === 0 || a[index - 1] === ' ';
40178             }
40179
40180             // matches value to preset.name
40181             var leading_name = _.filter(searchable, function(a) {
40182                     return leading(a.name().toLowerCase());
40183                 }).sort(function(a, b) {
40184                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
40185                     if (i === 0) return a.name().length - b.name().length;
40186                     else return i;
40187                 });
40188
40189             // matches value to preset.terms values
40190             var leading_terms = _.filter(searchable, function(a) {
40191                     return _.any(a.terms() || [], leading);
40192                 });
40193
40194             // matches value to preset.tags values
40195             var leading_tag_values = _.filter(searchable, function(a) {
40196                     return _.any(_.without(_.values(a.tags || {}), '*'), leading);
40197                 });
40198
40199
40200             // finds close matches to value in preset.name
40201             var levenstein_name = searchable.map(function(a) {
40202                     return {
40203                         preset: a,
40204                         dist: iD.util.editDistance(value, a.name().toLowerCase())
40205                     };
40206                 }).filter(function(a) {
40207                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
40208                 }).sort(function(a, b) {
40209                     return a.dist - b.dist;
40210                 }).map(function(a) {
40211                     return a.preset;
40212                 });
40213
40214             // finds close matches to value in preset.terms
40215             var leventstein_terms = _.filter(searchable, function(a) {
40216                     return _.any(a.terms() || [], function(b) {
40217                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
40218                     });
40219                 });
40220
40221             function suggestionName(name) {
40222                 var nameArray = name.split(' - ');
40223                 if (nameArray.length > 1) {
40224                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
40225                 }
40226                 return name.toLowerCase();
40227             }
40228
40229             var leading_suggestions = _.filter(suggestions, function(a) {
40230                     return leading(suggestionName(a.name()));
40231                 }).sort(function(a, b) {
40232                     a = suggestionName(a.name());
40233                     b = suggestionName(b.name());
40234                     var i = a.indexOf(value) - b.indexOf(value);
40235                     if (i === 0) return a.length - b.length;
40236                     else return i;
40237                 });
40238
40239             var leven_suggestions = suggestions.map(function(a) {
40240                     return {
40241                         preset: a,
40242                         dist: iD.util.editDistance(value, suggestionName(a.name()))
40243                     };
40244                 }).filter(function(a) {
40245                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
40246                 }).sort(function(a, b) {
40247                     return a.dist - b.dist;
40248                 }).map(function(a) {
40249                     return a.preset;
40250                 });
40251
40252             var other = presets.item(geometry);
40253
40254             var results = leading_name.concat(
40255                             leading_terms,
40256                             leading_tag_values,
40257                             leading_suggestions.slice(0, maxSuggestionResults+5),
40258                             levenstein_name,
40259                             leventstein_terms,
40260                             leven_suggestions.slice(0, maxSuggestionResults)
40261                         ).slice(0, maxSearchResults-1);
40262
40263             return iD.presets.Collection(_.unique(
40264                     results.concat(other)
40265                 ));
40266         }
40267     };
40268
40269     return presets;
40270 };
40271 iD.presets.Field = function(id, field) {
40272     field = _.clone(field);
40273
40274     field.id = id;
40275
40276     field.matchGeometry = function(geometry) {
40277         return !field.geometry || field.geometry === geometry;
40278     };
40279
40280     field.t = function(scope, options) {
40281         return t('presets.fields.' + id + '.' + scope, options);
40282     };
40283
40284     field.label = function() {
40285         return field.t('label', {'default': id});
40286     };
40287
40288     var placeholder = field.placeholder;
40289     field.placeholder = function() {
40290         return field.t('placeholder', {'default': placeholder});
40291     };
40292
40293     return field;
40294 };
40295 iD.presets.Preset = function(id, preset, fields) {
40296     preset = _.clone(preset);
40297
40298     preset.id = id;
40299     preset.fields = (preset.fields || []).map(getFields);
40300     preset.geometry = (preset.geometry || []);
40301
40302     function getFields(f) {
40303         return fields[f];
40304     }
40305
40306     preset.matchGeometry = function(geometry) {
40307         return preset.geometry.indexOf(geometry) >= 0;
40308     };
40309
40310     var matchScore = preset.matchScore || 1;
40311     preset.matchScore = function(entity) {
40312         var tags = preset.tags,
40313             score = 0;
40314
40315         for (var t in tags) {
40316             if (entity.tags[t] === tags[t]) {
40317                 score += matchScore;
40318             } else if (tags[t] === '*' && t in entity.tags) {
40319                 score += matchScore / 2;
40320             } else {
40321                 return -1;
40322             }
40323         }
40324
40325         return score;
40326     };
40327
40328     preset.t = function(scope, options) {
40329         return t('presets.presets.' + id + '.' + scope, options);
40330     };
40331
40332     var name = preset.name;
40333     preset.name = function() {
40334         if (preset.suggestion) {
40335             id = id.split('/');
40336             id = id[0] + '/' + id[1];
40337             return name + ' - ' + t('presets.presets.' + id + '.name');
40338         }
40339         return preset.t('name', {'default': name});
40340     };
40341
40342     preset.terms = function() {
40343         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
40344     };
40345
40346     preset.isFallback = function() {
40347         var tagCount = Object.keys(preset.tags).length;
40348         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
40349     };
40350
40351     preset.reference = function(geometry) {
40352         var key = Object.keys(preset.tags)[0],
40353             value = preset.tags[key];
40354
40355         if (geometry === 'relation' && key === 'type') {
40356             return { rtype: value };
40357         } else if (value === '*') {
40358             return { key: key };
40359         } else {
40360             return { key: key, value: value };
40361         }
40362     };
40363
40364     var removeTags = preset.removeTags || preset.tags;
40365     preset.removeTags = function(tags, geometry) {
40366         tags = _.omit(tags, _.keys(removeTags));
40367
40368         for (var f in preset.fields) {
40369             var field = preset.fields[f];
40370             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
40371                 delete tags[field.key];
40372             }
40373         }
40374
40375         delete tags.area;
40376         return tags;
40377     };
40378
40379     var applyTags = preset.addTags || preset.tags;
40380     preset.applyTags = function(tags, geometry) {
40381         var k;
40382
40383         tags = _.clone(tags);
40384
40385         for (k in applyTags) {
40386             if (applyTags[k] === '*') {
40387                 tags[k] = 'yes';
40388             } else {
40389                 tags[k] = applyTags[k];
40390             }
40391         }
40392
40393         // Add area=yes if necessary.
40394         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
40395         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
40396         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
40397         if (geometry === 'area') {
40398             var needsAreaTag = true;
40399             if (preset.geometry.indexOf('line') === -1) {
40400                 for (k in applyTags) {
40401                     if (k in iD.areaKeys) {
40402                         needsAreaTag = false;
40403                         break;
40404                     }
40405                 }
40406             }
40407             if (needsAreaTag) {
40408                 tags.area = 'yes';
40409             }
40410         }
40411
40412         for (var f in preset.fields) {
40413             var field = preset.fields[f];
40414             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
40415                 tags[field.key] = field.default;
40416             }
40417         }
40418
40419         return tags;
40420     };
40421
40422     return preset;
40423 };
40424 iD.validations = {};
40425 iD.validations.DeprecatedTag = function() {
40426
40427     var validation = function(changes) {
40428         var warnings = [];
40429         for (var i = 0; i < changes.created.length; i++) {
40430             var change = changes.created[i],
40431                 deprecatedTags = change.deprecatedTags();
40432
40433             if (!_.isEmpty(deprecatedTags)) {
40434                 var tags = iD.util.tagText({ tags: deprecatedTags });
40435                 warnings.push({
40436                     id: 'deprecated_tags',
40437                     message: t('validations.deprecated_tags', { tags: tags }),
40438                     entity: change
40439                 });
40440             }
40441         }
40442         return warnings;
40443     };
40444
40445     return validation;
40446 };
40447 iD.validations.ManyDeletions = function() {
40448     var threshold = 100;
40449
40450     var validation = function(changes) {
40451         var warnings = [];
40452         if (changes.deleted.length > threshold) {
40453             warnings.push({
40454                 id: 'many_deletions',
40455                 message: t('validations.many_deletions', { n: changes.deleted.length })
40456             });
40457         }
40458         return warnings;
40459     };
40460
40461     return validation;
40462 };
40463 iD.validations.MissingTag = function() {
40464
40465     var validation = function(changes, graph) {
40466         var warnings = [];
40467         for (var i = 0; i < changes.created.length; i++) {
40468             var change = changes.created[i],
40469                 geometry = change.geometry(graph);
40470
40471             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !change.isUsed(graph)) {
40472                 warnings.push({
40473                     id: 'missing_tag',
40474                     message: t('validations.untagged_' + geometry),
40475                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
40476                     entity: change
40477                 });
40478             }
40479         }
40480         return warnings;
40481     };
40482
40483     return validation;
40484 };
40485 iD.validations.TagSuggestsArea = function() {
40486
40487     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
40488     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
40489     function tagSuggestsArea(tags) {
40490         if (_.isEmpty(tags)) return false;
40491
40492         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
40493         for (var i = 0; i < presence.length; i++) {
40494             if (tags[presence[i]] !== undefined) {
40495                 return presence[i] + '=' + tags[presence[i]];
40496             }
40497         }
40498
40499         if (tags.building && tags.building === 'yes') return 'building=yes';
40500     }
40501
40502     var validation = function(changes, graph) {
40503         var warnings = [];
40504         for (var i = 0; i < changes.created.length; i++) {
40505             var change = changes.created[i],
40506                 geometry = change.geometry(graph),
40507                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
40508
40509             if (suggestion) {
40510                 warnings.push({
40511                     id: 'tag_suggests_area',
40512                     message: t('validations.tag_suggests_area', { tag: suggestion }),
40513                     entity: change
40514                 });
40515             }
40516         }
40517         return warnings;
40518     };
40519
40520     return validation;
40521 };
40522 })();
40523 window.locale = { _current: 'en' };
40524
40525 locale.current = function(_) {
40526     if (!arguments.length) return locale._current;
40527     if (locale[_] !== undefined) locale._current = _;
40528     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
40529     return locale;
40530 };
40531
40532 function t(s, o, loc) {
40533     loc = loc || locale._current;
40534
40535     var path = s.split(".").reverse(),
40536         rep = locale[loc];
40537
40538     while (rep !== undefined && path.length) rep = rep[path.pop()];
40539
40540     if (rep !== undefined) {
40541         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
40542         return rep;
40543     }
40544
40545     if (loc !== 'en') {
40546         return t(s, o, 'en');
40547     }
40548
40549     if (o && 'default' in o) {
40550         return o['default'];
40551     }
40552
40553     var missing = 'Missing ' + loc + ' translation: ' + s;
40554     if (typeof console !== "undefined") console.error(missing);
40555
40556     return missing;
40557 }
40558 iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]},"n185964961":{"id":"n185964961","loc":[-85.6406588,41.942601]},"n185964962":{"id":"n185964962","loc":[-85.6394548,41.94261]},"n185970607":{"id":"n185970607","loc":[-85.641094,41.94006]},"n185970614":{"id":"n185970614","loc":[-85.641825,41.941316]},"n185970616":{"id":"n185970616","loc":[-85.641838,41.941556]},"n185973650":{"id":"n185973650","loc":[-85.639918,41.940064]},"n185973660":{"id":"n185973660","loc":[-85.640645,41.941339]},"n185973659":{"id":"n185973659","loc":[-85.6406115,41.9400658]},"n185974479":{"id":"n185974479","loc":[-85.639402,41.941344]},"n185974481":{"id":"n185974481","loc":[-85.643071,41.941288]},"n185976259":{"id":"n185976259","loc":[-85.642213,41.940043]},"n185976261":{"id":"n185976261","loc":[-85.643056,41.94001]},"n185964959":{"id":"n185964959","loc":[-85.6431031,41.9425754]},"n185964960":{"id":"n185964960","loc":[-85.6418749,41.9425864]},"n185981481":{"id":"n185981481","loc":[-85.6386827,41.9400828]},"n185981482":{"id":"n185981482","loc":[-85.6393664,41.9400854]},"n2138493844":{"id":"n2138493844","loc":[-85.6427969,41.940522]},"n2138493845":{"id":"n2138493845","loc":[-85.6425891,41.9405228]},"n2138493846":{"id":"n2138493846","loc":[-85.6425868,41.9402875]},"n2138493847":{"id":"n2138493847","loc":[-85.6427969,41.9402858]},"n2138493848":{"id":"n2138493848","loc":[-85.6425708,41.9405234]},"n2138493849":{"id":"n2138493849","loc":[-85.642568,41.9402855]},"n2138493850":{"id":"n2138493850","loc":[-85.6423157,41.9402886]},"n2138493851":{"id":"n2138493851","loc":[-85.6423212,41.9404362]},"n2138493852":{"id":"n2138493852","loc":[-85.6422923,41.9404578]},"n2138493853":{"id":"n2138493853","loc":[-85.6422868,41.9404834]},"n2138493854":{"id":"n2138493854","loc":[-85.6423226,41.9405091]},"n2138493855":{"id":"n2138493855","loc":[-85.6423847,41.9405111]},"n2138493856":{"id":"n2138493856","loc":[-85.6424081,41.9405265]},"n2140155811":{"id":"n2140155811","loc":[-85.6419547,41.9410956]},"n2140155814":{"id":"n2140155814","loc":[-85.6427577,41.9410884]},"n2140155816":{"id":"n2140155816","loc":[-85.6427545,41.9410052]},"n2140155818":{"id":"n2140155818","loc":[-85.6428057,41.9410028]},"n2140155821":{"id":"n2140155821","loc":[-85.6427993,41.9407339]},"n2140155823":{"id":"n2140155823","loc":[-85.6427385,41.9407339]},"n2140155825":{"id":"n2140155825","loc":[-85.6427417,41.9406435]},"n2140155827":{"id":"n2140155827","loc":[-85.6419515,41.9406482]},"n2140155828":{"id":"n2140155828","loc":[-85.6429368,41.9412407]},"n2140155829":{"id":"n2140155829","loc":[-85.6417756,41.9412526]},"n2140155830":{"id":"n2140155830","loc":[-85.641766,41.9405983]},"n2140155831":{"id":"n2140155831","loc":[-85.6419803,41.9405983]},"n2140155832":{"id":"n2140155832","loc":[-85.6419611,41.9401366]},"n2140155833":{"id":"n2140155833","loc":[-85.6429336,41.94012]},"n2140155834":{"id":"n2140155834","loc":[-85.6430697,41.9411732]},"n2140155835":{"id":"n2140155835","loc":[-85.6428411,41.9409974]},"n2140155837":{"id":"n2140155837","loc":[-85.6428388,41.9407211]},"n2140155839":{"id":"n2140155839","loc":[-85.6430624,41.9405521]},"n2140155840":{"id":"n2140155840","loc":[-85.6427323,41.9412396]},"n2140155842":{"id":"n2140155842","loc":[-85.6418147,41.9412457]},"n2140155844":{"id":"n2140155844","loc":[-85.641813,41.9411319]},"n2140155845":{"id":"n2140155845","loc":[-85.6418394,41.9411111]},"n2140155847":{"id":"n2140155847","loc":[-85.6418838,41.9410977]},"n2140155849":{"id":"n2140155849","loc":[-85.6427324,41.9410921]},"n2140155851":{"id":"n2140155851","loc":[-85.6427798,41.9412945]},"n2140155852":{"id":"n2140155852","loc":[-85.6427701,41.9411777]},"n2140155854":{"id":"n2140155854","loc":[-85.6427323,41.9411572]},"n2140155856":{"id":"n2140155856","loc":[-85.6418478,41.9411666]},"n2165942818":{"id":"n2165942818","loc":[-85.6437533,41.9415029]},"n2165942819":{"id":"n2165942819","loc":[-85.6437623,41.9421195]},"n2168510551":{"id":"n2168510551","loc":[-85.6423795,41.9422615]},"n2168510552":{"id":"n2168510552","loc":[-85.6423744,41.9419439]},"n2168510553":{"id":"n2168510553","loc":[-85.642518,41.9419427]},"n2168510554":{"id":"n2168510554","loc":[-85.6425186,41.9419801]},"n2168510555":{"id":"n2168510555","loc":[-85.6428314,41.9419773]},"n2168510556":{"id":"n2168510556","loc":[-85.6428368,41.9423116]},"n2168510557":{"id":"n2168510557","loc":[-85.6424947,41.9423146]},"n2168510558":{"id":"n2168510558","loc":[-85.6424938,41.9422605]},"n2189046007":{"id":"n2189046007","loc":[-85.6410866,41.9424327]},"n2189046009":{"id":"n2189046009","loc":[-85.6410805,41.9420061]},"n2189046011":{"id":"n2189046011","loc":[-85.6412443,41.9420048]},"n2189046012":{"id":"n2189046012","loc":[-85.6412505,41.9424314]},"n2189046014":{"id":"n2189046014","loc":[-85.6413311,41.942968]},"n2189046016":{"id":"n2189046016","loc":[-85.6413281,41.942713]},"n2189046018":{"id":"n2189046018","loc":[-85.641521,41.9427117]},"n2189046021":{"id":"n2189046021","loc":[-85.6415234,41.9429236]},"n2189046022":{"id":"n2189046022","loc":[-85.6415045,41.9429238]},"n2189046025":{"id":"n2189046025","loc":[-85.641505,41.9429668]},"n2189046053":{"id":"n2189046053","loc":[-85.6385988,41.942412]},"n2189046054":{"id":"n2189046054","loc":[-85.6385985,41.9423311]},"n2189046055":{"id":"n2189046055","loc":[-85.6387617,41.9423308]},"n2189046056":{"id":"n2189046056","loc":[-85.6387616,41.9423026]},"n2189046058":{"id":"n2189046058","loc":[-85.6388215,41.9423025]},"n2189046059":{"id":"n2189046059","loc":[-85.6388219,41.9424115]},"n2189046060":{"id":"n2189046060","loc":[-85.6391096,41.9424486]},"n2189046061":{"id":"n2189046061","loc":[-85.6391105,41.9423673]},"n2189046063":{"id":"n2189046063","loc":[-85.6392911,41.9423684]},"n2189046065":{"id":"n2189046065","loc":[-85.6392903,41.9424497]},"n2189046067":{"id":"n2189046067","loc":[-85.6397927,41.9423876]},"n2189046069":{"id":"n2189046069","loc":[-85.6397897,41.9422981]},"n2189046070":{"id":"n2189046070","loc":[-85.6399702,41.9422947]},"n2189046072":{"id":"n2189046072","loc":[-85.6399732,41.9423843]},"n2189046074":{"id":"n2189046074","loc":[-85.6396331,41.9430227]},"n2189046075":{"id":"n2189046075","loc":[-85.6398673,41.9430189]},"n2189046077":{"id":"n2189046077","loc":[-85.6398656,41.9429637]},"n2189046079":{"id":"n2189046079","loc":[-85.6398885,41.9429633]},"n2189046082":{"id":"n2189046082","loc":[-85.6398832,41.942779]},"n2189046083":{"id":"n2189046083","loc":[-85.6398513,41.9427796]},"n2189046085":{"id":"n2189046085","loc":[-85.6398502,41.9427401]},"n2189046087":{"id":"n2189046087","loc":[-85.6397889,41.9427411]},"n2189046089":{"id":"n2189046089","loc":[-85.6397892,41.942753]},"n2189046090":{"id":"n2189046090","loc":[-85.6396983,41.9427544]},"n2189046092":{"id":"n2189046092","loc":[-85.6396993,41.9427882]},"n2189046094":{"id":"n2189046094","loc":[-85.6396746,41.9427886]},"n2189046096":{"id":"n2189046096","loc":[-85.6396758,41.9428296]},"n2189046097":{"id":"n2189046097","loc":[-85.6397007,41.9428292]},"n2189046099":{"id":"n2189046099","loc":[-85.6397018,41.9428686]},"n2189046103":{"id":"n2189046103","loc":[-85.6396289,41.9428697]},"n2189046112":{"id":"n2189046112","loc":[-85.6435683,41.9429457]},"n2189046113":{"id":"n2189046113","loc":[-85.643568,41.9427766]},"n2189046115":{"id":"n2189046115","loc":[-85.6434011,41.9427767]},"n2189046116":{"id":"n2189046116","loc":[-85.6434012,41.9428631]},"n2189046117":{"id":"n2189046117","loc":[-85.643448,41.9428631]},"n2189046118":{"id":"n2189046118","loc":[-85.6434481,41.9429457]},"n2189046119":{"id":"n2189046119","loc":[-85.6428363,41.9429809]},"n2189046120":{"id":"n2189046120","loc":[-85.6429171,41.9429791]},"n2189046121":{"id":"n2189046121","loc":[-85.642914,41.9429041]},"n2189046122":{"id":"n2189046122","loc":[-85.6429385,41.9429035]},"n2189046123":{"id":"n2189046123","loc":[-85.6429348,41.9428126]},"n2189046124":{"id":"n2189046124","loc":[-85.6427746,41.9428163]},"n2189046125":{"id":"n2189046125","loc":[-85.6427783,41.942906]},"n2189046126":{"id":"n2189046126","loc":[-85.6428332,41.9429047]},"n2189046127":{"id":"n2189046127","loc":[-85.6423018,41.9428859]},"n2189046128":{"id":"n2189046128","loc":[-85.6422987,41.9427208]},"n2189046130":{"id":"n2189046130","loc":[-85.6424218,41.9427195]},"n2189046131":{"id":"n2189046131","loc":[-85.6424246,41.9428684]},"n2189046132":{"id":"n2189046132","loc":[-85.6423845,41.9428689]},"n2189046133":{"id":"n2189046133","loc":[-85.6423848,41.942885]},"n2189046134":{"id":"n2189046134","loc":[-85.641533,41.9429392]},"n2189046135":{"id":"n2189046135","loc":[-85.6416096,41.9428768]},"n2189046137":{"id":"n2189046137","loc":[-85.6416763,41.9429221]},"n2189046138":{"id":"n2189046138","loc":[-85.6415997,41.9429845]},"n2189046139":{"id":"n2189046139","loc":[-85.6420598,41.9428016]},"n2189046140":{"id":"n2189046140","loc":[-85.6420593,41.9427415]},"n2189046141":{"id":"n2189046141","loc":[-85.6421957,41.9427409]},"n2189046142":{"id":"n2189046142","loc":[-85.6421963,41.9428182]},"n2189046143":{"id":"n2189046143","loc":[-85.6421281,41.9428185]},"n2189046144":{"id":"n2189046144","loc":[-85.6421279,41.9428013]},"n2189046145":{"id":"n2189046145","loc":[-85.6409429,41.9429345]},"n2189046146":{"id":"n2189046146","loc":[-85.6410354,41.9429334]},"n2189046147":{"id":"n2189046147","loc":[-85.6410325,41.9427972]},"n2189046148":{"id":"n2189046148","loc":[-85.640997,41.9427976]},"n2189046149":{"id":"n2189046149","loc":[-85.6409963,41.9427643]},"n2189046150":{"id":"n2189046150","loc":[-85.6408605,41.9427659]},"n2189046152":{"id":"n2189046152","loc":[-85.6408623,41.9428482]},"n2189046153":{"id":"n2189046153","loc":[-85.640941,41.9428473]},"n2189152992":{"id":"n2189152992","loc":[-85.6437661,41.9422257]},"n2189152993":{"id":"n2189152993","loc":[-85.643768,41.9424067]},"n2189152994":{"id":"n2189152994","loc":[-85.6432176,41.9417705]},"n2189152995":{"id":"n2189152995","loc":[-85.6432097,41.941327]},"n2189152996":{"id":"n2189152996","loc":[-85.6436493,41.9413226]},"n2189152997":{"id":"n2189152997","loc":[-85.6436563,41.9417164]},"n2189152998":{"id":"n2189152998","loc":[-85.6435796,41.9417171]},"n2189152999":{"id":"n2189152999","loc":[-85.6435805,41.9417669]},"n2189153000":{"id":"n2189153000","loc":[-85.6438202,41.9414953]},"n2189153001":{"id":"n2189153001","loc":[-85.6438173,41.9413175]},"n2189153004":{"id":"n2189153004","loc":[-85.6432535,41.9418466]},"n2189153005":{"id":"n2189153005","loc":[-85.6433935,41.9418599]},"n2189153006":{"id":"n2189153006","loc":[-85.6434831,41.9418986]},"n2189153007":{"id":"n2189153007","loc":[-85.6435678,41.9419774]},"n2189153008":{"id":"n2189153008","loc":[-85.6435987,41.9420282]},"n2189153009":{"id":"n2189153009","loc":[-85.643438,41.9419573]},"n2189153010":{"id":"n2189153010","loc":[-85.6435284,41.9424676]},"n2189153011":{"id":"n2189153011","loc":[-85.6436207,41.9423631]},"n2189153012":{"id":"n2189153012","loc":[-85.6434957,41.9422973]},"n2189153013":{"id":"n2189153013","loc":[-85.6434457,41.9422458]},"n2189153014":{"id":"n2189153014","loc":[-85.6433976,41.9421772]},"n2189153015":{"id":"n2189153015","loc":[-85.6433861,41.9420785]},"n2189153016":{"id":"n2189153016","loc":[-85.6433765,41.9420313]},"n2189153017":{"id":"n2189153017","loc":[-85.6432207,41.9420284]},"n2189153018":{"id":"n2189153018","loc":[-85.6432245,41.9422759]},"n2189153019":{"id":"n2189153019","loc":[-85.6432649,41.9423474]},"n2189153020":{"id":"n2189153020","loc":[-85.6433226,41.9424132]},"n2189153021":{"id":"n2189153021","loc":[-85.6434111,41.9424704]},"n2189153022":{"id":"n2189153022","loc":[-85.6434591,41.9424347]},"n2189153025":{"id":"n2189153025","loc":[-85.6437669,41.9423073]},"n2189153026":{"id":"n2189153026","loc":[-85.6436611,41.942293]},"n2189153027":{"id":"n2189153027","loc":[-85.6435784,41.9422473]},"n2189153028":{"id":"n2189153028","loc":[-85.6435245,41.9421443]},"n2189153029":{"id":"n2189153029","loc":[-85.6435149,41.9420613]},"n2189153030":{"id":"n2189153030","loc":[-85.6433528,41.9419269]},"n2189153031":{"id":"n2189153031","loc":[-85.6432535,41.9419191]},"n2189153032":{"id":"n2189153032","loc":[-85.6430868,41.9419198]},"n2189153033":{"id":"n2189153033","loc":[-85.6434894,41.9420033]},"n2189153034":{"id":"n2189153034","loc":[-85.6432974,41.9419225]},"n2189153035":{"id":"n2189153035","loc":[-85.6433055,41.9421632]},"n2189153036":{"id":"n2189153036","loc":[-85.6433538,41.9422849]},"n2189153037":{"id":"n2189153037","loc":[-85.6434718,41.9423887]},"n2189153038":{"id":"n2189153038","loc":[-85.6436134,41.9422667]},"n2189153040":{"id":"n2189153040","loc":[-85.6438759,41.9414017]},"n2189153041":{"id":"n2189153041","loc":[-85.6438181,41.9413687]},"n2189153042":{"id":"n2189153042","loc":[-85.6436821,41.9413044]},"n2189153043":{"id":"n2189153043","loc":[-85.6435899,41.9412862]},"n2189153044":{"id":"n2189153044","loc":[-85.6433169,41.9417268]},"n2189153045":{"id":"n2189153045","loc":[-85.643301,41.9412859]},"n2189153046":{"id":"n2189153046","loc":[-85.6435531,41.9416981]},"n2189153047":{"id":"n2189153047","loc":[-85.6435427,41.9412863]},"n185948706":{"id":"n185948706","loc":[-85.6369439,41.940122]},"n185949348":{"id":"n185949348","loc":[-85.640039,41.931135]},"n185949870":{"id":"n185949870","loc":[-85.643195,41.949261]},"n185954680":{"id":"n185954680","loc":[-85.6337802,41.9401143]},"n185954784":{"id":"n185954784","loc":[-85.6487485,41.942527]},"n185958670":{"id":"n185958670","loc":[-85.637255,41.940104]},"n185958672":{"id":"n185958672","loc":[-85.636996,41.941355]},"n185960207":{"id":"n185960207","loc":[-85.634992,41.940118]},"n185963163":{"id":"n185963163","loc":[-85.638831,41.93398]},"n185963165":{"id":"n185963165","loc":[-85.640073,41.933968]},"n185963167":{"id":"n185963167","loc":[-85.641225,41.933972]},"n185963168":{"id":"n185963168","loc":[-85.642386,41.933952]},"n185964695":{"id":"n185964695","loc":[-85.6443608,41.9425645]},"n185964697":{"id":"n185964697","loc":[-85.644384,41.939941]},"n185964963":{"id":"n185964963","loc":[-85.6382347,41.9426146]},"n185964965":{"id":"n185964965","loc":[-85.637022,41.942622]},"n185964967":{"id":"n185964967","loc":[-85.6363706,41.9426606]},"n185964968":{"id":"n185964968","loc":[-85.6357988,41.9427748]},"n185964969":{"id":"n185964969","loc":[-85.6355409,41.9428465]},"n185964970":{"id":"n185964970","loc":[-85.6348729,41.9430443]},"n185966958":{"id":"n185966958","loc":[-85.641946,41.946413]},"n185966960":{"id":"n185966960","loc":[-85.643148,41.946389]},"n185967774":{"id":"n185967774","loc":[-85.641889,41.943852]},"n185967775":{"id":"n185967775","loc":[-85.641922,41.945121]},"n185967776":{"id":"n185967776","loc":[-85.641927,41.947544]},"n185967777":{"id":"n185967777","loc":[-85.641982,41.947622]},"n185969289":{"id":"n185969289","loc":[-85.63928,41.929221]},"n185969704":{"id":"n185969704","loc":[-85.6388186,41.9350099]},"n185969706":{"id":"n185969706","loc":[-85.6400709,41.9349957]},"n185969708":{"id":"n185969708","loc":[-85.6412214,41.9349827]},"n185969710":{"id":"n185969710","loc":[-85.6423509,41.934974]},"n185970602":{"id":"n185970602","loc":[-85.641293,41.931817]},"n185970604":{"id":"n185970604","loc":[-85.641258,41.932705]},"n185970605":{"id":"n185970605","loc":[-85.641148,41.936984]},"n185970606":{"id":"n185970606","loc":[-85.641112,41.938169]},"n185970906":{"id":"n185970906","loc":[-85.639454,41.943871]},"n185970908":{"id":"n185970908","loc":[-85.6394635,41.9450504]},"n185970909":{"id":"n185970909","loc":[-85.6394914,41.9451911]},"n185971368":{"id":"n185971368","loc":[-85.635769,41.940122]},"n185971978":{"id":"n185971978","loc":[-85.640003,41.936988]},"n185971980":{"id":"n185971980","loc":[-85.642299,41.936988]},"n185973633":{"id":"n185973633","loc":[-85.639023,41.92861]},"n185973635":{"id":"n185973635","loc":[-85.639153,41.928969]},"n185973637":{"id":"n185973637","loc":[-85.639213,41.929088]},"n185973639":{"id":"n185973639","loc":[-85.63935,41.929396]},"n185973641":{"id":"n185973641","loc":[-85.640143,41.931462]},"n185973644":{"id":"n185973644","loc":[-85.64019,41.931788]},"n185973646":{"id":"n185973646","loc":[-85.6401365,41.9327199]},"n185973648":{"id":"n185973648","loc":[-85.639983,41.938174]},"n185974477":{"id":"n185974477","loc":[-85.638206,41.941331]},"n185975928":{"id":"n185975928","loc":[-85.640683,41.94513]},"n185975930":{"id":"n185975930","loc":[-85.643102,41.945103]},"n185976255":{"id":"n185976255","loc":[-85.642424,41.931817]},"n185976257":{"id":"n185976257","loc":[-85.64242,41.932699]},"n185976258":{"id":"n185976258","loc":[-85.6422621,41.9381489]},"n185977452":{"id":"n185977452","loc":[-85.6457497,41.9398834]},"n185978772":{"id":"n185978772","loc":[-85.646656,41.939869]},"n185981472":{"id":"n185981472","loc":[-85.6388962,41.9321266]},"n185981474":{"id":"n185981474","loc":[-85.6388769,41.9327334]},"n185981476":{"id":"n185981476","loc":[-85.638829,41.934116]},"n185981478":{"id":"n185981478","loc":[-85.63876,41.937002]},"n185981480":{"id":"n185981480","loc":[-85.638682,41.93819]},"n185981999":{"id":"n185981999","loc":[-85.638194,41.9400866]},"n185982001":{"id":"n185982001","loc":[-85.646302,41.93988]},"n185982877":{"id":"n185982877","loc":[-85.640676,41.943867]},"n185982879":{"id":"n185982879","loc":[-85.640734,41.945887]},"n185985823":{"id":"n185985823","loc":[-85.643106,41.943841]},"n185985824":{"id":"n185985824","loc":[-85.643145,41.947641]},"n185985825":{"id":"n185985825","loc":[-85.643219,41.950829]},"n1475301385":{"id":"n1475301385","loc":[-85.6360612,41.9427042]},"n1475301397":{"id":"n1475301397","loc":[-85.6366651,41.9426328]},"n2139795811":{"id":"n2139795811","loc":[-85.6469154,41.9425427]},"n2139795830":{"id":"n2139795830","loc":[-85.6443194,41.9399444]},"n2139795834":{"id":"n2139795834","loc":[-85.6453506,41.9399002]},"n2139795837":{"id":"n2139795837","loc":[-85.645806,41.9398831]},"n2139858932":{"id":"n2139858932","loc":[-85.6351721,41.9429557]},"n2140019000":{"id":"n2140019000","loc":[-85.6359935,41.9427224]},"n2165942817":{"id":"n2165942817","loc":[-85.6442017,41.9414993]},"n2165942820":{"id":"n2165942820","loc":[-85.6442107,41.9421159]},"n2189152990":{"id":"n2189152990","loc":[-85.6442328,41.942404]},"n2189152991":{"id":"n2189152991","loc":[-85.6442309,41.9422229]},"n2189153002":{"id":"n2189153002","loc":[-85.6441329,41.9413147]},"n2189153003":{"id":"n2189153003","loc":[-85.6441357,41.9414925]},"n2189153023":{"id":"n2189153023","loc":[-85.6443453,41.9423074]},"n2189153024":{"id":"n2189153024","loc":[-85.6442318,41.9423045]},"n2189153039":{"id":"n2189153039","loc":[-85.6441343,41.9414025]},"w208643102":{"id":"w208643102","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153034","n2189153035","n2189153036","n2189153037","n2189153038"]},"w17966942":{"id":"w17966942","tags":{"highway":"residential","name":"Millard St"},"nodes":["n185954680","n185960207","n185971368","n185948706","n185958670","n185981999","n185981481","n185981482","n185973650","n185973659","n185970607","n185976259","n185976261","n2139795830","n185964697","n2139795834","n185977452","n2139795837","n185982001","n185978772"]},"w208643105":{"id":"w208643105","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153046","n2189153047"]},"w208631637":{"id":"w208631637","tags":{"area":"yes","building":"yes"},"nodes":["n2189046014","n2189046016","n2189046018","n2189046021","n2189046022","n2189046025","n2189046014"]},"w208643096":{"id":"w208643096","tags":{"amenity":"parking","area":"yes","fee":"no"},"nodes":["n2189152990","n2189153024","n2189152991","n2189152992","n2189153025","n2189152993","n2189152990"]},"w208631656":{"id":"w208631656","tags":{"area":"yes","building":"yes"},"nodes":["n2189046134","n2189046135","n2189046137","n2189046138","n2189046134"]},"w204003417":{"id":"w204003417","tags":{"area":"yes","building":"school"},"nodes":["n2140155811","n2140155814","n2140155816","n2140155818","n2140155821","n2140155823","n2140155825","n2140155827","n2140155811"]},"w208631654":{"id":"w208631654","tags":{"area":"yes","building":"yes"},"nodes":["n2189046127","n2189046128","n2189046130","n2189046131","n2189046132","n2189046133","n2189046127"]},"w17966327":{"id":"w17966327","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976261","n2140155839","n2140155834","n185974481","n2189153032","n185964959"]},"w41785752":{"id":"w41785752","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60","access":"yes"},"nodes":["n185954784","n2139795811","n185964695","n185964959","n185964960","n185964961","n185964962","n185964963","n185964965","n1475301397","n185964967","n1475301385","n2140019000","n185964968","n185964969","n2139858932","n185964970"]},"w203841842":{"id":"w203841842","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493848","n2138493849","n2138493850","n2138493851","n2138493852","n2138493853","n2138493854","n2138493855","n2138493856","n2138493848"]},"w208643103":{"id":"w208643103","tags":{"highway":"service"},"nodes":["n2189153039","n2189153040","n2189153041","n2189153042","n2189153043","n2189153047","n2189153045","n185974481"]},"w208643098":{"id":"w208643098","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153000","n2189153041","n2189153001","n2189153002","n2189153039","n2189153003","n2189153000"]},"w208631646":{"id":"w208631646","tags":{"area":"yes","building":"yes"},"nodes":["n2189046067","n2189046069","n2189046070","n2189046072","n2189046067"]},"w208631653":{"id":"w208631653","tags":{"area":"yes","building":"yes"},"nodes":["n2189046119","n2189046120","n2189046121","n2189046122","n2189046123","n2189046124","n2189046125","n2189046126","n2189046119"]},"w17966041":{"id":"w17966041","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973659","n185973660","n185964961"]},"w208631645":{"id":"w208631645","tags":{"area":"yes","building":"yes"},"nodes":["n2189046060","n2189046061","n2189046063","n2189046065","n2189046060"]},"w206803397":{"id":"w206803397","tags":{"area":"yes","building":"yes"},"nodes":["n2168510551","n2168510552","n2168510553","n2168510554","n2168510555","n2168510556","n2168510557","n2168510558","n2168510551"]},"w17965792":{"id":"w17965792","tags":{"highway":"residential","name":"N Hooker Ave"},"nodes":["n185964962","n185970906","n185970908","n185970909"]},"w208631651":{"id":"w208631651","tags":{"area":"yes","building":"yes"},"nodes":["n2189046112","n2189046113","n2189046115","n2189046116","n2189046117","n2189046118","n2189046112"]},"w208631643":{"id":"w208631643","tags":{"area":"yes","building":"yes"},"nodes":["n2189046053","n2189046054","n2189046055","n2189046056","n2189046058","n2189046059","n2189046053"]},"w17966878":{"id":"w17966878","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981472","n185981474","n185963163","n185981476","n185969704","n185981478","n185981480","n185981481"]},"w17966102":{"id":"w17966102","tags":{"highway":"residential","name":"South St"},"nodes":["n185958672","n185974477","n185974479","n185973660","n185970614"]},"w208631660":{"id":"w208631660","tags":{"area":"yes","building":"yes"},"nodes":["n2189046145","n2189046146","n2189046147","n2189046148","n2189046149","n2189046150","n2189046152","n2189046153","n2189046145"]},"w208643101":{"id":"w208643101","tags":{"highway":"service"},"nodes":["n2189153023","n2189153024","n2189153025","n2189153026","n2189153038","n2189153027","n2189153028","n2189153029","n2189153033","n2189153009","n2189153030","n2189153034","n2189153031","n2189153032"]},"w204000205":{"id":"w204000205","tags":{"highway":"residential","name":"South St","oneway":"yes"},"nodes":["n185974481","n2140155851","n185970614"]},"w203841841":{"id":"w203841841","tags":{"area":"yes","leisure":"pitch","pitch":"basketball"},"nodes":["n2138493844","n2138493845","n2138493846","n2138493847","n2138493844"]},"w17965444":{"id":"w17965444","tags":{"highway":"residential","name":"N Grant Ave"},"nodes":["n185964960","n185967774","n185967775","n185966958","n185967776","n185967777"]},"w208631648":{"id":"w208631648","tags":{"area":"yes","building":"yes"},"nodes":["n2189046074","n2189046075","n2189046077","n2189046079","n2189046082","n2189046083","n2189046085","n2189046087","n2189046089","n2189046090","n2189046092","n2189046094","n2189046096","n2189046097","n2189046099","n2189046103","n2189046074"]},"w208643100":{"id":"w208643100","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153010","n2189153011","n2189153012","n2189153013","n2189153014","n2189153015","n2189153016","n2189153017","n2189153018","n2189153019","n2189153020","n2189153021","n2189153022","n2189153010"]},"w17965749":{"id":"w17965749","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970614","n185970616","n185964960"]},"w206574482":{"id":"w206574482","tags":{"amenity":"library","area":"yes","building":"yes","name":"Three Rivers Public Library"},"nodes":["n2165942817","n2165942818","n2165942819","n2165942820","n2165942817"]},"w208643097":{"id":"w208643097","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189152994","n2189152995","n2189152996","n2189152997","n2189152998","n2189152999","n2189152994"]},"w17966879":{"id":"w17966879","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981482","n185974479","n185964962"]},"w17966325":{"id":"w17966325","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976255","n185976257","n185963168","n185969710","n185971980","n185976258","n185954700","n185976259"]},"w17967390":{"id":"w17967390","tags":{"highway":"residential","name":"N Douglas Ave"},"nodes":["n185964959","n185985823","n185975930","n185966960","n185985824","n185949870","n185985825"]},"w208631635":{"id":"w208631635","tags":{"area":"yes","building":"yes"},"nodes":["n2189046007","n2189046009","n2189046011","n2189046012","n2189046007"]},"w208643099":{"id":"w208643099","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153031","n2189153004","n2189153005","n2189153006","n2189153007","n2189153008","n2189153029","n2189153033","n2189153009","n2189153030","n2189153031"]},"w208631658":{"id":"w208631658","tags":{"area":"yes","building":"yes"},"nodes":["n2189046139","n2189046140","n2189046141","n2189046142","n2189046143","n2189046144","n2189046139"]},"w208643104":{"id":"w208643104","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153044","n2189153045"]},"w17966039":{"id":"w17966039","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973633","n185973635","n185973637","n185969289","n185973639","n185949348","n185973641","n185973644","n185973646","n185963165","n185969706","n185971978","n185973648","n185973650"]},"w204003420":{"id":"w204003420","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140155840","n2140155842","n2140155844","n2140155845","n2140155847","n2140155849","n2140155854","n2140155840"]},"w204003419":{"id":"w204003419","tags":{"highway":"service"},"nodes":["n2140155834","n2140155835","n2140155837","n2140155839"]},"w204003418":{"id":"w204003418","tags":{"amenity":"school","area":"yes","name":"Andrews Elementary School"},"nodes":["n2140155828","n2140155829","n2140155830","n2140155831","n2140155832","n2140155833","n2140155828"]},"w17965747":{"id":"w17965747","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970602","n185970604","n185963167","n185969708","n185970605","n185970606","n185970607"]},"w17967073":{"id":"w17967073","tags":{"highway":"residential","name":"N Lincoln Ave"},"nodes":["n185964961","n185982877","n185975928","n185982879"]},"w204003421":{"id":"w204003421","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2140155851","n2140155852","n2140155854","n2140155856"]},"r1943857":{"id":"r1943857","tags":{"modifier":"Business","name":"US 131 Business (Three Rivers, MI)","network":"US:US","ref":"131","route":"road","type":"route"},"members":[{"id":"w17966509","type":"way","role":"forward"},{"id":"w143497377","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w41785752","type":"way","role":""},{"id":"w17965146","type":"way","role":"forward"},{"id":"w17964031","type":"way","role":"forward"}]},"r270277":{"id":"r270277","tags":{"network":"US:MI","ref":"60","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-60_%28Michigan_highway%29"},"members":[{"id":"w17751087","type":"way","role":"east"},{"id":"w117148312","type":"way","role":"east"},{"id":"w40942155","type":"way","role":"west"},{"id":"w17751017","type":"way","role":""},{"id":"w17751083","type":"way","role":""},{"id":"w17747780","type":"way","role":""},{"id":"w41068082","type":"way","role":""},{"id":"w197025212","type":"way","role":""},{"id":"w17743874","type":"way","role":""},{"id":"w17751044","type":"way","role":""},{"id":"w17752167","type":"way","role":""},{"id":"w17751089","type":"way","role":""},{"id":"w17743879","type":"way","role":""},{"id":"w17751064","type":"way","role":""},{"id":"w197057073","type":"way","role":""},{"id":"w167699963","type":"way","role":""},{"id":"w167699972","type":"way","role":""},{"id":"w17967584","type":"way","role":""},{"id":"w167699964","type":"way","role":""},{"id":"w17967582","type":"way","role":"west"},{"id":"w41260270","type":"way","role":"west"},{"id":"w17965146","type":"way","role":"west"},{"id":"w41785752","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150836","type":"way","role":""},{"id":"w134150802","type":"way","role":""},{"id":"w41074896","type":"way","role":""},{"id":"w17966773","type":"way","role":""},{"id":"w17967415","type":"way","role":""},{"id":"w41074899","type":"way","role":""},{"id":"w17967581","type":"way","role":""},{"id":"w41074902","type":"way","role":""},{"id":"w41074906","type":"way","role":""},{"id":"w209707997","type":"way","role":""},{"id":"w209707998","type":"way","role":""},{"id":"w17964798","type":"way","role":""},{"id":"w17966034","type":"way","role":""},{"id":"w17967593","type":"way","role":""},{"id":"w41074888","type":"way","role":""},{"id":"w17733772","type":"way","role":""},{"id":"w41074813","type":"way","role":""},{"id":"w17742213","type":"way","role":""},{"id":"w17746863","type":"way","role":""},{"id":"w17745772","type":"way","role":""},{"id":"w17742222","type":"way","role":""},{"id":"w17745922","type":"way","role":""},{"id":"w17742198","type":"way","role":""},{"id":"w17747675","type":"way","role":""},{"id":"w17739927","type":"way","role":""},{"id":"w17745708","type":"way","role":""},{"id":"w41006323","type":"way","role":""},{"id":"w17744233","type":"way","role":""},{"id":"w17739436","type":"way","role":""},{"id":"w17742201","type":"way","role":""},{"id":"w151418616","type":"way","role":""},{"id":"w17750062","type":"way","role":""},{"id":"w17742227","type":"way","role":"east"},{"id":"w41006348","type":"way","role":"east"},{"id":"w41260984","type":"way","role":""},{"id":"w17832427","type":"way","role":""},{"id":"w17838408","type":"way","role":""},{"id":"w17835846","type":"way","role":""},{"id":"w17832923","type":"way","role":""},{"id":"w17839388","type":"way","role":""},{"id":"w17838390","type":"way","role":""},{"id":"w17831272","type":"way","role":""},{"id":"w17828581","type":"way","role":""},{"id":"w38240686","type":"way","role":""},{"id":"w17838405","type":"way","role":"east"},{"id":"w123323711","type":"way","role":"east"},{"id":"w17830167","type":"way","role":"east"},{"id":"w99011909","type":"way","role":"east"},{"id":"w41911361","type":"way","role":"east"},{"id":"w41911355","type":"way","role":"east"},{"id":"w41911356","type":"way","role":"east"},{"id":"w117148326","type":"way","role":"west"},{"id":"w41911352","type":"way","role":"west"},{"id":"w41911353","type":"way","role":"west"},{"id":"w41911354","type":"way","role":"west"},{"id":"w41911360","type":"way","role":"west"},{"id":"w38240676","type":"way","role":"west"},{"id":"w123323710","type":"way","role":"west"},{"id":"w41260271","type":"way","role":"east"},{"id":"w41260273","type":"way","role":"east"},{"id":"w17964031","type":"way","role":"east"},{"id":"w41006344","type":"way","role":"west"},{"id":"w41006351","type":"way","role":"west"}]},"n367813436":{"id":"n367813436","loc":[-85.63605205663384,41.94305506683346],"tags":{"amenity":"fire_station","name":"Three Rivers Fire Department"}},"n185948708":{"id":"n185948708","loc":[-85.6369828,41.9408789]},"n185948710":{"id":"n185948710","loc":[-85.6370184,41.9411346]},"n185954691":{"id":"n185954691","loc":[-85.634476,41.941475]},"n185954692":{"id":"n185954692","loc":[-85.635008,41.941846]},"n185954693":{"id":"n185954693","loc":[-85.635362,41.941962]},"n185954695":{"id":"n185954695","loc":[-85.63578,41.941978]},"n185972903":{"id":"n185972903","loc":[-85.63295,41.9430062]},"n185964971":{"id":"n185964971","loc":[-85.6346811,41.9431023]},"n1819805854":{"id":"n1819805854","loc":[-85.6331275,41.9404837]},"n1819805918":{"id":"n1819805918","loc":[-85.6331168,41.942798]},"n1819805762":{"id":"n1819805762","loc":[-85.6333034,41.9424123]},"n1819805907":{"id":"n1819805907","loc":[-85.6334819,41.9419121]},"n1819805915":{"id":"n1819805915","loc":[-85.6334554,41.9413588]},"n1819848888":{"id":"n1819848888","loc":[-85.6331625,41.942679]},"n1819848930":{"id":"n1819848930","loc":[-85.6338684,41.9431252]},"n1819858505":{"id":"n1819858505","loc":[-85.6346782,41.9429092]},"n1819858507":{"id":"n1819858507","loc":[-85.6339003,41.9414534]},"n1819858508":{"id":"n1819858508","loc":[-85.6345709,41.9427742]},"n1819858509":{"id":"n1819858509","loc":[-85.63419,41.9417322]},"n1819858511":{"id":"n1819858511","loc":[-85.6340666,41.9415652]},"n1819858512":{"id":"n1819858512","loc":[-85.6343295,41.9423027]},"n1819858514":{"id":"n1819858514","loc":[-85.6343241,41.942207]},"n1819858521":{"id":"n1819858521","loc":[-85.633391,41.941231]},"n1819858528":{"id":"n1819858528","loc":[-85.6343027,41.9419716]},"n185954683":{"id":"n185954683","loc":[-85.6335412,41.940147]},"n185954685":{"id":"n185954685","loc":[-85.6334296,41.9403023]},"n185954687":{"id":"n185954687","loc":[-85.6333988,41.9404704]},"n185954689":{"id":"n185954689","loc":[-85.6335511,41.9410225]},"n185954690":{"id":"n185954690","loc":[-85.6336721,41.9411669]},"n1820938802":{"id":"n1820938802","loc":[-85.6330671,41.941845]},"n1821006702":{"id":"n1821006702","loc":[-85.6344047,41.9395496]},"n2130304133":{"id":"n2130304133","loc":[-85.6349025,41.9427659]},"n2130304136":{"id":"n2130304136","loc":[-85.6346027,41.9422017]},"n2130304138":{"id":"n2130304138","loc":[-85.6348577,41.9421517]},"n2130304140":{"id":"n2130304140","loc":[-85.6348419,41.9422694]},"n2130304142":{"id":"n2130304142","loc":[-85.6349071,41.9423135]},"n2130304144":{"id":"n2130304144","loc":[-85.6350495,41.9423312]},"n2130304146":{"id":"n2130304146","loc":[-85.6351009,41.9422812]},"n2130304147":{"id":"n2130304147","loc":[-85.6351227,41.9421532]},"n2130304148":{"id":"n2130304148","loc":[-85.635526,41.9421547]},"n2130304149":{"id":"n2130304149","loc":[-85.6355339,41.9425768]},"n2130304150":{"id":"n2130304150","loc":[-85.6351582,41.9426562]},"n2130304151":{"id":"n2130304151","loc":[-85.6351207,41.9427032]},"n2138493807":{"id":"n2138493807","loc":[-85.6350923,41.9415216]},"n2138493808":{"id":"n2138493808","loc":[-85.6353603,41.9411061]},"n2138493809":{"id":"n2138493809","loc":[-85.6354421,41.9410942]},"n2138493810":{"id":"n2138493810","loc":[-85.6355079,41.9411044]},"n2138493811":{"id":"n2138493811","loc":[-85.6355693,41.9411246]},"n2138493812":{"id":"n2138493812","loc":[-85.6355829,41.9411061]},"n2138493813":{"id":"n2138493813","loc":[-85.6355624,41.9409777]},"n2138493814":{"id":"n2138493814","loc":[-85.6355011,41.9409152]},"n2138493815":{"id":"n2138493815","loc":[-85.635383,41.9409219]},"n2138493816":{"id":"n2138493816","loc":[-85.635299,41.9409658]},"n2138493817":{"id":"n2138493817","loc":[-85.6351695,41.941204]},"n2138493818":{"id":"n2138493818","loc":[-85.6348879,41.9415166]},"n2138493819":{"id":"n2138493819","loc":[-85.634897,41.9415757]},"n2138493820":{"id":"n2138493820","loc":[-85.6349606,41.9416399]},"n2138493821":{"id":"n2138493821","loc":[-85.6350219,41.9416669]},"n2138493822":{"id":"n2138493822","loc":[-85.6351241,41.9416314]},"n2138493823":{"id":"n2138493823","loc":[-85.6350855,41.9415622]},"n2138493824":{"id":"n2138493824","loc":[-85.6350401,41.9413603]},"n2138493825":{"id":"n2138493825","loc":[-85.6352206,41.9410765]},"n2138493826":{"id":"n2138493826","loc":[-85.6343865,41.9415594]},"n2138493827":{"id":"n2138493827","loc":[-85.6343506,41.9415873]},"n2138493828":{"id":"n2138493828","loc":[-85.6344158,41.9417557]},"n2138493829":{"id":"n2138493829","loc":[-85.6344614,41.9417968]},"n2138493830":{"id":"n2138493830","loc":[-85.6345005,41.9418186]},"n2138493831":{"id":"n2138493831","loc":[-85.6345965,41.9418162]},"n2138493832":{"id":"n2138493832","loc":[-85.6347317,41.9417242]},"n2138493833":{"id":"n2138493833","loc":[-85.6346722,41.941775]},"n2139858909":{"id":"n2139858909","loc":[-85.633403,41.9391006]},"n2139858910":{"id":"n2139858910","loc":[-85.6332973,41.9393967]},"n2139858911":{"id":"n2139858911","loc":[-85.633205,41.9396742]},"n2139858912":{"id":"n2139858912","loc":[-85.6332203,41.9397772]},"n2139858913":{"id":"n2139858913","loc":[-85.6333453,41.939936]},"n2139858914":{"id":"n2139858914","loc":[-85.6333761,41.9400018]},"n2139858915":{"id":"n2139858915","loc":[-85.63328,41.9402249]},"n2139858916":{"id":"n2139858916","loc":[-85.6332357,41.9403523]},"n2139858917":{"id":"n2139858917","loc":[-85.6332838,41.9405831]},"n2139858918":{"id":"n2139858918","loc":[-85.6333643,41.9408744]},"n2139858919":{"id":"n2139858919","loc":[-85.6334394,41.9410519]},"n2139858920":{"id":"n2139858920","loc":[-85.6335815,41.9411717]},"n2139858921":{"id":"n2139858921","loc":[-85.6337478,41.9412734]},"n2139858922":{"id":"n2139858922","loc":[-85.6343174,41.9415268]},"n2139858923":{"id":"n2139858923","loc":[-85.6343886,41.9417397]},"n2139858924":{"id":"n2139858924","loc":[-85.6344407,41.9418015]},"n2139858925":{"id":"n2139858925","loc":[-85.6345139,41.9418366]},"n2139858926":{"id":"n2139858926","loc":[-85.6344846,41.942005]},"n2139858927":{"id":"n2139858927","loc":[-85.6345775,41.9422218]},"n2139858928":{"id":"n2139858928","loc":[-85.6348771,41.9427814]},"n2139858929":{"id":"n2139858929","loc":[-85.6349487,41.9427995]},"n2139858930":{"id":"n2139858930","loc":[-85.6350415,41.9427874]},"n2139858931":{"id":"n2139858931","loc":[-85.6351246,41.9428589]},"n2139858978":{"id":"n2139858978","loc":[-85.6349658,41.9431481]},"n2139858979":{"id":"n2139858979","loc":[-85.6350081,41.9431287]},"n2139858980":{"id":"n2139858980","loc":[-85.6349967,41.9430997]},"n2139858981":{"id":"n2139858981","loc":[-85.6352158,41.9430352]},"n2139858982":{"id":"n2139858982","loc":[-85.6348174,41.94267]},"n2139858983":{"id":"n2139858983","loc":[-85.6346142,41.9425989]},"n2139858984":{"id":"n2139858984","loc":[-85.6344938,41.9423809]},"n2139858985":{"id":"n2139858985","loc":[-85.6344856,41.9422997]},"n2139870380":{"id":"n2139870380","loc":[-85.6346707,41.9417955]},"n2139870381":{"id":"n2139870381","loc":[-85.6345949,41.9418311]},"n2139870382":{"id":"n2139870382","loc":[-85.6343322,41.9418659]},"n2139870383":{"id":"n2139870383","loc":[-85.6342072,41.941885]},"n2139870384":{"id":"n2139870384","loc":[-85.6341325,41.9418919]},"n2139870385":{"id":"n2139870385","loc":[-85.6341314,41.9422028]},"n2139870386":{"id":"n2139870386","loc":[-85.6340472,41.9423271]},"n2139870387":{"id":"n2139870387","loc":[-85.6342185,41.9427933]},"n2139870388":{"id":"n2139870388","loc":[-85.6340605,41.9423924]},"n2139870389":{"id":"n2139870389","loc":[-85.6339889,41.9424069]},"n2139870390":{"id":"n2139870390","loc":[-85.633971,41.942356]},"n2139870391":{"id":"n2139870391","loc":[-85.63361,41.9424235]},"n2139870392":{"id":"n2139870392","loc":[-85.6337137,41.9426819]},"n2139870393":{"id":"n2139870393","loc":[-85.6336977,41.9428632]},"n2139870394":{"id":"n2139870394","loc":[-85.6338823,41.9428647]},"n2139870395":{"id":"n2139870395","loc":[-85.6339412,41.9430069]},"n2139870396":{"id":"n2139870396","loc":[-85.6338873,41.9430353]},"n2139870397":{"id":"n2139870397","loc":[-85.6337676,41.942815]},"n2139870398":{"id":"n2139870398","loc":[-85.6336822,41.9423505]},"n2139870399":{"id":"n2139870399","loc":[-85.634037,41.9422725]},"n2139870400":{"id":"n2139870400","loc":[-85.6340294,41.9422518]},"n2139870401":{"id":"n2139870401","loc":[-85.6336726,41.9423312]},"n2139870402":{"id":"n2139870402","loc":[-85.6342188,41.9425715]},"n2139870403":{"id":"n2139870403","loc":[-85.6342524,41.942565]},"n2139870404":{"id":"n2139870404","loc":[-85.6341438,41.942299]},"n2139870405":{"id":"n2139870405","loc":[-85.6341149,41.9423061]},"n2139870407":{"id":"n2139870407","loc":[-85.6340846,41.9431458]},"n2139870408":{"id":"n2139870408","loc":[-85.6339436,41.9429032]},"n2139870409":{"id":"n2139870409","loc":[-85.6343143,41.9428207]},"n2139870410":{"id":"n2139870410","loc":[-85.6343507,41.94277]},"n2139870411":{"id":"n2139870411","loc":[-85.6341527,41.942254]},"n2139870412":{"id":"n2139870412","loc":[-85.6340925,41.9422199]},"n2139870413":{"id":"n2139870413","loc":[-85.6335435,41.9423433]},"n2139870414":{"id":"n2139870414","loc":[-85.6335023,41.9423975]},"n2139870415":{"id":"n2139870415","loc":[-85.6335086,41.9424552]},"n2139870416":{"id":"n2139870416","loc":[-85.6336296,41.942665]},"n2139870417":{"id":"n2139870417","loc":[-85.6341396,41.9428596]},"n2139870418":{"id":"n2139870418","loc":[-85.6339701,41.9424487]},"n2139870419":{"id":"n2139870419","loc":[-85.6335514,41.9425294]},"n2139870420":{"id":"n2139870420","loc":[-85.6337406,41.9424929]},"n2139870421":{"id":"n2139870421","loc":[-85.6338939,41.9428687]},"n2139870422":{"id":"n2139870422","loc":[-85.6341323,41.9419538]},"n2139870423":{"id":"n2139870423","loc":[-85.6340321,41.9420376]},"n2139870424":{"id":"n2139870424","loc":[-85.6337648,41.942238]},"n2139870425":{"id":"n2139870425","loc":[-85.6337604,41.9422685]},"n2139870426":{"id":"n2139870426","loc":[-85.6337682,41.9422928]},"n2139870427":{"id":"n2139870427","loc":[-85.6338086,41.9423862]},"n2139870428":{"id":"n2139870428","loc":[-85.6349465,41.9416631]},"n2139870429":{"id":"n2139870429","loc":[-85.6351097,41.9416973]},"n2139870430":{"id":"n2139870430","loc":[-85.6353371,41.9416798]},"n2139870431":{"id":"n2139870431","loc":[-85.6349627,41.9422506]},"n2139870432":{"id":"n2139870432","loc":[-85.634979,41.9421815]},"n2139870433":{"id":"n2139870433","loc":[-85.634885,41.9421679]},"n2139870434":{"id":"n2139870434","loc":[-85.6348689,41.9422377]},"n2139870435":{"id":"n2139870435","loc":[-85.6349779,41.9419486]},"n2139870436":{"id":"n2139870436","loc":[-85.6349505,41.9418933]},"n2139870437":{"id":"n2139870437","loc":[-85.6347327,41.9419505]},"n2139870438":{"id":"n2139870438","loc":[-85.6347614,41.9420087]},"n2139870439":{"id":"n2139870439","loc":[-85.6351889,41.9416912]},"n2139870440":{"id":"n2139870440","loc":[-85.6351092,41.9418426]},"n2139870441":{"id":"n2139870441","loc":[-85.635086,41.9419659]},"n2139870442":{"id":"n2139870442","loc":[-85.6350584,41.9421466]},"n2139870443":{"id":"n2139870443","loc":[-85.6350993,41.9421606]},"n2139870444":{"id":"n2139870444","loc":[-85.6350993,41.9422132]},"n2139870445":{"id":"n2139870445","loc":[-85.6350794,41.9422855]},"n2139870446":{"id":"n2139870446","loc":[-85.6350474,41.9423159]},"n2139870447":{"id":"n2139870447","loc":[-85.6349251,41.9422998]},"n2139870448":{"id":"n2139870448","loc":[-85.634911,41.9422755]},"n2139870449":{"id":"n2139870449","loc":[-85.6349157,41.9422553]},"n2139870450":{"id":"n2139870450","loc":[-85.6347213,41.9419324]},"n2139870451":{"id":"n2139870451","loc":[-85.6349535,41.9418771]},"n2139870452":{"id":"n2139870452","loc":[-85.6350135,41.9419421]},"n2139870453":{"id":"n2139870453","loc":[-85.6348584,41.9418997]},"n2139870454":{"id":"n2139870454","loc":[-85.6348113,41.9418101]},"n2139870455":{"id":"n2139870455","loc":[-85.6347306,41.9417449]},"n2139870456":{"id":"n2139870456","loc":[-85.6349123,41.941776]},"n2139870457":{"id":"n2139870457","loc":[-85.6349423,41.9421448]},"n2139870458":{"id":"n2139870458","loc":[-85.6349436,41.9420652]},"n2139870459":{"id":"n2139870459","loc":[-85.6349136,41.9419963]},"n2139870460":{"id":"n2139870460","loc":[-85.6349814,41.9419789]},"n2139989328":{"id":"n2139989328","loc":[-85.6334188,41.9421725]},"n2139989330":{"id":"n2139989330","loc":[-85.6335087,41.9416308]},"n2139989335":{"id":"n2139989335","loc":[-85.6336856,41.9429371]},"n2139989337":{"id":"n2139989337","loc":[-85.6333713,41.9427217]},"n2139989339":{"id":"n2139989339","loc":[-85.6332912,41.9425383]},"n2139989341":{"id":"n2139989341","loc":[-85.6339369,41.9409198]},"n2139989344":{"id":"n2139989344","loc":[-85.634097,41.9409469]},"n2139989346":{"id":"n2139989346","loc":[-85.634137,41.9412852]},"n2139989348":{"id":"n2139989348","loc":[-85.6344536,41.9414151]},"n2139989350":{"id":"n2139989350","loc":[-85.6350794,41.9412392]},"n2139989351":{"id":"n2139989351","loc":[-85.6352541,41.9409387]},"n2139989353":{"id":"n2139989353","loc":[-85.6357198,41.9408007]},"n2139989355":{"id":"n2139989355","loc":[-85.6357235,41.9427088]},"n2139989357":{"id":"n2139989357","loc":[-85.6337119,41.9421256]},"n2139989359":{"id":"n2139989359","loc":[-85.6336913,41.9420655]},"n2139989360":{"id":"n2139989360","loc":[-85.633582,41.9420867]},"n2139989362":{"id":"n2139989362","loc":[-85.6336058,41.9421491]},"n2139989364":{"id":"n2139989364","loc":[-85.6339685,41.9410995]},"n2139989366":{"id":"n2139989366","loc":[-85.6339067,41.9411383]},"n2139989368":{"id":"n2139989368","loc":[-85.6339685,41.9411972]},"n2139989370":{"id":"n2139989370","loc":[-85.6340398,41.9411619]},"n2139870379":{"id":"n2139870379","loc":[-85.6348391,41.9416651]},"n2140006363":{"id":"n2140006363","loc":[-85.6353144,41.9430345]},"n2140006364":{"id":"n2140006364","loc":[-85.6349191,41.9431422]},"n2140018997":{"id":"n2140018997","loc":[-85.63645945147184,41.942986488012565],"tags":{"amenity":"townhall","name":"Three Rivers City Hall"}},"n2140018998":{"id":"n2140018998","loc":[-85.6370319,41.9427919]},"n2140018999":{"id":"n2140018999","loc":[-85.6360687,41.9427808]},"n2199856288":{"id":"n2199856288","loc":[-85.6344968,41.9407307]},"n2199856289":{"id":"n2199856289","loc":[-85.634492,41.9406036]},"n2199856290":{"id":"n2199856290","loc":[-85.634891,41.9406001]},"n2199856291":{"id":"n2199856291","loc":[-85.6348894,41.9405288]},"n2199856292":{"id":"n2199856292","loc":[-85.6349166,41.94053]},"n2199856293":{"id":"n2199856293","loc":[-85.6349166,41.9404956]},"n2199856294":{"id":"n2199856294","loc":[-85.6350219,41.9404956]},"n2199856295":{"id":"n2199856295","loc":[-85.6350251,41.94053]},"n2199856296":{"id":"n2199856296","loc":[-85.6350538,41.9405288]},"n2199856297":{"id":"n2199856297","loc":[-85.6350602,41.94079]},"n2199856298":{"id":"n2199856298","loc":[-85.6351703,41.9407912]},"n2199856299":{"id":"n2199856299","loc":[-85.6351688,41.9409171]},"n2199856300":{"id":"n2199856300","loc":[-85.6347889,41.9409135]},"n2199856301":{"id":"n2199856301","loc":[-85.6347921,41.94079]},"n2199856302":{"id":"n2199856302","loc":[-85.6348942,41.9407888]},"n2199856303":{"id":"n2199856303","loc":[-85.6348926,41.9407283]},"n185951869":{"id":"n185951869","loc":[-85.6387639,41.957288]},"n185958643":{"id":"n185958643","loc":[-85.636746,41.929221]},"n185958645":{"id":"n185958645","loc":[-85.636791,41.929363]},"n185958647":{"id":"n185958647","loc":[-85.6375975,41.9314987]},"n185958649":{"id":"n185958649","loc":[-85.637669,41.931667]},"n185958651":{"id":"n185958651","loc":[-85.637728,41.931901]},"n185958653":{"id":"n185958653","loc":[-85.637724,41.932187]},"n185958656":{"id":"n185958656","loc":[-85.637732,41.932761]},"n185958658":{"id":"n185958658","loc":[-85.637688,41.93398]},"n185958660":{"id":"n185958660","loc":[-85.637685,41.934223]},"n185958662":{"id":"n185958662","loc":[-85.6376468,41.9350232]},"n185958664":{"id":"n185958664","loc":[-85.637564,41.937028]},"n185958666":{"id":"n185958666","loc":[-85.637458,41.938197]},"n185958668":{"id":"n185958668","loc":[-85.637424,41.938692]},"n185964972":{"id":"n185964972","loc":[-85.6341901,41.9432732]},"n185971361":{"id":"n185971361","loc":[-85.635762,41.938208]},"n185971364":{"id":"n185971364","loc":[-85.635732,41.9384]},"n185971366":{"id":"n185971366","loc":[-85.635736,41.938697]},"n185972775":{"id":"n185972775","loc":[-85.635638,42.070357]},"n185972777":{"id":"n185972777","loc":[-85.635724,42.069929]},"n185972779":{"id":"n185972779","loc":[-85.635804,42.069248]},"n185972781":{"id":"n185972781","loc":[-85.635869,42.068361]},"n185972783":{"id":"n185972783","loc":[-85.635883,42.067582]},"n185972785":{"id":"n185972785","loc":[-85.635875,42.067114]},"n185972787":{"id":"n185972787","loc":[-85.635778,42.065359]},"n185972788":{"id":"n185972788","loc":[-85.635728,42.063416]},"n185972789":{"id":"n185972789","loc":[-85.635665,42.062491]},"n185972790":{"id":"n185972790","loc":[-85.635617,42.061928]},"n185972791":{"id":"n185972791","loc":[-85.635614,42.061898]},"n185972793":{"id":"n185972793","loc":[-85.635379,42.060288]},"n185972795":{"id":"n185972795","loc":[-85.635092,42.05799]},"n185972797":{"id":"n185972797","loc":[-85.634843,42.055781]},"n185972798":{"id":"n185972798","loc":[-85.634817,42.055549]},"n185972800":{"id":"n185972800","loc":[-85.634708,42.053942]},"n185972802":{"id":"n185972802","loc":[-85.634447,42.051809]},"n185972805":{"id":"n185972805","loc":[-85.634241,42.04946]},"n185972807":{"id":"n185972807","loc":[-85.633787,42.045926]},"n185972809":{"id":"n185972809","loc":[-85.633811,42.045645]},"n185972811":{"id":"n185972811","loc":[-85.63373,42.043626]},"n185972813":{"id":"n185972813","loc":[-85.633698,42.042184]},"n185972814":{"id":"n185972814","loc":[-85.63369,42.04181]},"n185972815":{"id":"n185972815","loc":[-85.633681,42.040714]},"n185972816":{"id":"n185972816","loc":[-85.633571,42.036322]},"n185972817":{"id":"n185972817","loc":[-85.633537,42.034044]},"n185972819":{"id":"n185972819","loc":[-85.633481,42.030785]},"n185972821":{"id":"n185972821","loc":[-85.633452,42.027538]},"n185972824":{"id":"n185972824","loc":[-85.633438,42.027427]},"n185972826":{"id":"n185972826","loc":[-85.633342,42.022656]},"n185972830":{"id":"n185972830","loc":[-85.63327,42.020724]},"n185972832":{"id":"n185972832","loc":[-85.633198,42.019106]},"n185972834":{"id":"n185972834","loc":[-85.633249,42.018363]},"n185972835":{"id":"n185972835","loc":[-85.633139,42.012944]},"n185972836":{"id":"n185972836","loc":[-85.63309,42.008284]},"n185972839":{"id":"n185972839","loc":[-85.63298,42.00005]},"n185972845":{"id":"n185972845","loc":[-85.6325369,41.9764959]},"n185972847":{"id":"n185972847","loc":[-85.6327549,41.9750005]},"n185972849":{"id":"n185972849","loc":[-85.6329374,41.9742527]},"n185972851":{"id":"n185972851","loc":[-85.6331387,41.9736039]},"n185972862":{"id":"n185972862","loc":[-85.6383589,41.9585023]},"n185972868":{"id":"n185972868","loc":[-85.6393633,41.9551716]},"n185972878":{"id":"n185972878","loc":[-85.639377,41.95335]},"n185972882":{"id":"n185972882","loc":[-85.6389179,41.9516944]},"n185972885":{"id":"n185972885","loc":[-85.6387444,41.9512105]},"n185972891":{"id":"n185972891","loc":[-85.636421,41.946392]},"n185972895":{"id":"n185972895","loc":[-85.635965,41.945809]},"n185972897":{"id":"n185972897","loc":[-85.635683,41.945449]},"n185972899":{"id":"n185972899","loc":[-85.635281,41.9450252]},"n185972905":{"id":"n185972905","loc":[-85.6324428,41.9425743]},"n185985217":{"id":"n185985217","loc":[-85.638243,41.943674]},"n185985219":{"id":"n185985219","loc":[-85.638228,41.943747]},"n185985221":{"id":"n185985221","loc":[-85.638163,41.943797]},"n185985222":{"id":"n185985222","loc":[-85.638089,41.943832]},"n185985223":{"id":"n185985223","loc":[-85.637969,41.943841]},"n185985225":{"id":"n185985225","loc":[-85.637841,41.943833]},"n185985227":{"id":"n185985227","loc":[-85.637601,41.943789]},"n185985229":{"id":"n185985229","loc":[-85.637449,41.943754]},"n185985231":{"id":"n185985231","loc":[-85.637342,41.943734]},"n185985233":{"id":"n185985233","loc":[-85.637218,41.943703]},"n185985235":{"id":"n185985235","loc":[-85.637151,41.943663]},"n185985238":{"id":"n185985238","loc":[-85.637118,41.943615]},"n185985240":{"id":"n185985240","loc":[-85.637073,41.943494]},"n185990434":{"id":"n185990434","loc":[-85.6329028,41.9984292],"tags":{"railway":"level_crossing"}},"n1475284023":{"id":"n1475284023","loc":[-85.6336163,41.9435806],"tags":{"railway":"level_crossing"}},"n1475293222":{"id":"n1475293222","loc":[-85.6394045,41.953658],"tags":{"railway":"level_crossing"}},"n1475293226":{"id":"n1475293226","loc":[-85.6364975,41.9638663],"tags":{"railway":"level_crossing"}},"n1475293234":{"id":"n1475293234","loc":[-85.6390449,41.9565145]},"n1475293240":{"id":"n1475293240","loc":[-85.636943,41.9473114]},"n1475293252":{"id":"n1475293252","loc":[-85.6392115,41.9559003]},"n1475293254":{"id":"n1475293254","loc":[-85.6348931,41.9685127],"tags":{"railway":"level_crossing"}},"n1475293260":{"id":"n1475293260","loc":[-85.6375999,41.9485401]},"n1475293261":{"id":"n1475293261","loc":[-85.6391256,41.9523817],"tags":{"railway":"level_crossing"}},"n1475293264":{"id":"n1475293264","loc":[-85.6394155,41.9546493],"tags":{"railway":"level_crossing"}},"n1819805614":{"id":"n1819805614","loc":[-85.6345652,41.9363097]},"n1819805618":{"id":"n1819805618","loc":[-85.6295334,41.9426862]},"n1819805622":{"id":"n1819805622","loc":[-85.6308208,41.9430773]},"n1819805626":{"id":"n1819805626","loc":[-85.6274734,41.9406592]},"n1819805629":{"id":"n1819805629","loc":[-85.6296943,41.9430533]},"n1819805632":{"id":"n1819805632","loc":[-85.6340931,41.9354477]},"n1819805636":{"id":"n1819805636","loc":[-85.6304131,41.9436598]},"n1819805639":{"id":"n1819805639","loc":[-85.6304882,41.9426623]},"n1819805641":{"id":"n1819805641","loc":[-85.6336103,41.9367487]},"n1819805643":{"id":"n1819805643","loc":[-85.6300376,41.9418084]},"n1819805645":{"id":"n1819805645","loc":[-85.6365286,41.9336679]},"n1819805647":{"id":"n1819805647","loc":[-85.632016,41.9429221]},"n1819805666":{"id":"n1819805666","loc":[-85.6314753,41.9442663]},"n1819805669":{"id":"n1819805669","loc":[-85.6268619,41.9402203]},"n1819805673":{"id":"n1819805673","loc":[-85.6296728,41.9412099]},"n1819805676":{"id":"n1819805676","loc":[-85.6354557,41.932766]},"n1819805680":{"id":"n1819805680","loc":[-85.632752,41.9431012]},"n1819805683":{"id":"n1819805683","loc":[-85.631147,41.9432014]},"n1819805687":{"id":"n1819805687","loc":[-85.635284,41.9343942]},"n1819805690":{"id":"n1819805690","loc":[-85.6249736,41.9405794]},"n1819805694":{"id":"n1819805694","loc":[-85.6294153,41.9417925]},"n1819805698":{"id":"n1819805698","loc":[-85.6323486,41.9426986]},"n1819805702":{"id":"n1819805702","loc":[-85.6340287,41.9373871]},"n1819805707":{"id":"n1819805707","loc":[-85.6353698,41.9316326]},"n1819805711":{"id":"n1819805711","loc":[-85.6284176,41.940356]},"n1819805715":{"id":"n1819805715","loc":[-85.6291471,41.9412897]},"n1819805718":{"id":"n1819805718","loc":[-85.6311105,41.943979]},"n1819805722":{"id":"n1819805722","loc":[-85.6320868,41.9400128]},"n1819805724":{"id":"n1819805724","loc":[-85.635166,41.9324627]},"n1819805727":{"id":"n1819805727","loc":[-85.6344686,41.9350567]},"n1819805728":{"id":"n1819805728","loc":[-85.6357132,41.9332369]},"n1819805731":{"id":"n1819805731","loc":[-85.629984,41.9434444]},"n1819805760":{"id":"n1819805760","loc":[-85.6330996,41.9378784]},"n1819805766":{"id":"n1819805766","loc":[-85.625274,41.9411141]},"n1819805770":{"id":"n1819805770","loc":[-85.6326321,41.9412173]},"n1819805774":{"id":"n1819805774","loc":[-85.6347047,41.9312096]},"n1819805777":{"id":"n1819805777","loc":[-85.6363569,41.9339552]},"n1819805780":{"id":"n1819805780","loc":[-85.6327392,41.941926]},"n1819805783":{"id":"n1819805783","loc":[-85.6357239,41.9338435]},"n1819805786":{"id":"n1819805786","loc":[-85.6356595,41.9346576]},"n1819805789":{"id":"n1819805789","loc":[-85.6316469,41.9436598]},"n1819805792":{"id":"n1819805792","loc":[-85.6350587,41.9354557]},"n1819805795":{"id":"n1819805795","loc":[-85.6360028,41.9322791]},"n1819805798":{"id":"n1819805798","loc":[-85.63125,41.9443062]},"n1819805802":{"id":"n1819805802","loc":[-85.6263362,41.9408109]},"n1819805805":{"id":"n1819805805","loc":[-85.6315075,41.9438753]},"n1819805808":{"id":"n1819805808","loc":[-85.6340008,41.9316051]},"n1819805810":{"id":"n1819805810","loc":[-85.6345545,41.9320557]},"n1819805812":{"id":"n1819805812","loc":[-85.6250809,41.9408587]},"n1819805814":{"id":"n1819805814","loc":[-85.6257783,41.9400926]},"n1819805834":{"id":"n1819805834","loc":[-85.6326408,41.9424363]},"n1819805838":{"id":"n1819805838","loc":[-85.6365607,41.9334365]},"n1819805842":{"id":"n1819805842","loc":[-85.6288253,41.9410343]},"n1819805846":{"id":"n1819805846","loc":[-85.6279133,41.9402921]},"n1819805849":{"id":"n1819805849","loc":[-85.6289433,41.9405156]},"n1819805852":{"id":"n1819805852","loc":[-85.6313787,41.9439152]},"n1819805858":{"id":"n1819805858","loc":[-85.6300805,41.9420398]},"n1819805861":{"id":"n1819805861","loc":[-85.6321941,41.9396297]},"n1819805864":{"id":"n1819805864","loc":[-85.6329129,41.9393903]},"n1819805868":{"id":"n1819805868","loc":[-85.632001,41.9434922]},"n1819805870":{"id":"n1819805870","loc":[-85.6314903,41.9431535]},"n1819805873":{"id":"n1819805873","loc":[-85.6251667,41.9401166]},"n1819805876":{"id":"n1819805876","loc":[-85.63287,41.939941]},"n1819805878":{"id":"n1819805878","loc":[-85.6307886,41.9437317]},"n1819805880":{"id":"n1819805880","loc":[-85.6321727,41.940348]},"n1819805883":{"id":"n1819805883","loc":[-85.6265872,41.940113]},"n1819805885":{"id":"n1819805885","loc":[-85.6268404,41.9406672]},"n1819805887":{"id":"n1819805887","loc":[-85.6325267,41.9389035]},"n1819805889":{"id":"n1819805889","loc":[-85.6364964,41.933189]},"n1819805911":{"id":"n1819805911","loc":[-85.6248663,41.9401804]},"n1819805922":{"id":"n1819805922","loc":[-85.633267,41.9387199]},"n1819805925":{"id":"n1819805925","loc":[-85.6293402,41.9408428]},"n1819848849":{"id":"n1819848849","loc":[-85.6464957,41.9695178]},"n1819848850":{"id":"n1819848850","loc":[-85.6497642,41.9611355]},"n1819848851":{"id":"n1819848851","loc":[-85.6480943,41.9624818]},"n1819848854":{"id":"n1819848854","loc":[-85.6500362,41.9657367]},"n1819848855":{"id":"n1819848855","loc":[-85.6493673,41.9783496]},"n1819848856":{"id":"n1819848856","loc":[-85.6457409,41.9548007]},"n1819848857":{"id":"n1819848857","loc":[-85.651313,41.9760426]},"n1819848858":{"id":"n1819848858","loc":[-85.6495819,41.9784772]},"n1819848859":{"id":"n1819848859","loc":[-85.6495105,41.9833722]},"n1819848860":{"id":"n1819848860","loc":[-85.6405053,41.9492792]},"n1819848863":{"id":"n1819848863","loc":[-85.6502293,41.9786826]},"n1819848865":{"id":"n1819848865","loc":[-85.6406877,41.9495106]},"n1819848870":{"id":"n1819848870","loc":[-85.6493136,41.9704611]},"n1819848871":{"id":"n1819848871","loc":[-85.6372249,41.9441284]},"n1819848873":{"id":"n1819848873","loc":[-85.6512379,41.9659441]},"n1819848875":{"id":"n1819848875","loc":[-85.6508087,41.9650187]},"n1819848877":{"id":"n1819848877","loc":[-85.6487166,41.9605352]},"n1819848878":{"id":"n1819848878","loc":[-85.6506478,41.9760665]},"n1819848879":{"id":"n1819848879","loc":[-85.651431,41.9758512]},"n1819848886":{"id":"n1819848886","loc":[-85.6477617,41.9563945]},"n1819848889":{"id":"n1819848889","loc":[-85.6497895,41.9832286]},"n1819848892":{"id":"n1819848892","loc":[-85.6504868,41.9791931]},"n1819848893":{"id":"n1819848893","loc":[-85.6498002,41.9615085]},"n1819848894":{"id":"n1819848894","loc":[-85.6404302,41.9502846]},"n1819848901":{"id":"n1819848901","loc":[-85.6354412,41.9439886]},"n1819848903":{"id":"n1819848903","loc":[-85.6472145,41.9698528]},"n1819848904":{"id":"n1819848904","loc":[-85.6401979,41.9486233]},"n1819848905":{"id":"n1819848905","loc":[-85.6475042,41.963503]},"n1819848909":{"id":"n1819848909","loc":[-85.6343405,41.94358]},"n1819848914":{"id":"n1819848914","loc":[-85.6503474,41.9737773]},"n1819848915":{"id":"n1819848915","loc":[-85.6389533,41.9470992]},"n1819848916":{"id":"n1819848916","loc":[-85.6483625,41.9577907]},"n1819848917":{"id":"n1819848917","loc":[-85.6484768,41.9617419]},"n1819848918":{"id":"n1819848918","loc":[-85.644078,41.9545693]},"n1819848919":{"id":"n1819848919","loc":[-85.6437169,41.9543041]},"n1819848920":{"id":"n1819848920","loc":[-85.6478331,41.9627949]},"n1819848922":{"id":"n1819848922","loc":[-85.6499144,41.9785889]},"n1819848924":{"id":"n1819848924","loc":[-85.647633,41.9720066]},"n1819848926":{"id":"n1819848926","loc":[-85.6487987,41.978868]},"n1819848927":{"id":"n1819848927","loc":[-85.6495105,41.9730355]},"n1819848928":{"id":"n1819848928","loc":[-85.648223,41.9829654]},"n1819848929":{"id":"n1819848929","loc":[-85.6514846,41.9659122]},"n1819848931":{"id":"n1819848931","loc":[-85.6498753,41.9731871]},"n1819848932":{"id":"n1819848932","loc":[-85.640906,41.9508575]},"n1819848933":{"id":"n1819848933","loc":[-85.649775,41.9799767]},"n1819848934":{"id":"n1819848934","loc":[-85.6507014,41.9739927]},"n1819848937":{"id":"n1819848937","loc":[-85.6479763,41.9840899]},"n1819848938":{"id":"n1819848938","loc":[-85.6501113,41.9600884]},"n1819848939":{"id":"n1819848939","loc":[-85.6389962,41.9478253]},"n1819848941":{"id":"n1819848941","loc":[-85.637469,41.9445791]},"n1819848942":{"id":"n1819848942","loc":[-85.6494569,41.9601682]},"n1819848943":{"id":"n1819848943","loc":[-85.6368803,41.9439351]},"n1819848945":{"id":"n1819848945","loc":[-85.6474398,41.9724213]},"n1819848946":{"id":"n1819848946","loc":[-85.6382629,41.9463666]},"n1819848948":{"id":"n1819848948","loc":[-85.6489633,41.9830771]},"n1819848952":{"id":"n1819848952","loc":[-85.6488882,41.9600326]},"n1819848953":{"id":"n1819848953","loc":[-85.6488094,41.9774324]},"n1819848954":{"id":"n1819848954","loc":[-85.6491135,41.9600485]},"n1819848955":{"id":"n1819848955","loc":[-85.6501435,41.9734583]},"n1819848956":{"id":"n1819848956","loc":[-85.6495534,41.960958]},"n1819848958":{"id":"n1819848958","loc":[-85.6474683,41.9561491]},"n1819848959":{"id":"n1819848959","loc":[-85.6401083,41.9485451]},"n1819848960":{"id":"n1819848960","loc":[-85.6481764,41.9678686]},"n1819848961":{"id":"n1819848961","loc":[-85.6484017,41.967382]},"n1819848962":{"id":"n1819848962","loc":[-85.6501328,41.959897]},"n1819848964":{"id":"n1819848964","loc":[-85.6403695,41.9504586]},"n1819848966":{"id":"n1819848966","loc":[-85.6398975,41.9491499]},"n1819848967":{"id":"n1819848967","loc":[-85.6412455,41.9510187]},"n1819848968":{"id":"n1819848968","loc":[-85.6482622,41.9619493]},"n1819848969":{"id":"n1819848969","loc":[-85.6405841,41.9501474]},"n1819848970":{"id":"n1819848970","loc":[-85.6478583,41.9703394]},"n1819848971":{"id":"n1819848971","loc":[-85.6493388,41.9832845]},"n1819848972":{"id":"n1819848972","loc":[-85.6485664,41.9829415]},"n1819848974":{"id":"n1819848974","loc":[-85.6491457,41.9779887]},"n1819848975":{"id":"n1819848975","loc":[-85.6468889,41.9697033]},"n1819848976":{"id":"n1819848976","loc":[-85.6452726,41.9546072]},"n1819848977":{"id":"n1819848977","loc":[-85.6448435,41.9546072]},"n1819848979":{"id":"n1819848979","loc":[-85.6485342,41.9763138]},"n1819848980":{"id":"n1819848980","loc":[-85.6495282,41.9664087]},"n1819848986":{"id":"n1819848986","loc":[-85.6486307,41.9603278]},"n1819848987":{"id":"n1819848987","loc":[-85.6492278,41.9791871]},"n1819848990":{"id":"n1819848990","loc":[-85.6501934,41.9800724]},"n1819848992":{"id":"n1819848992","loc":[-85.6482445,41.9819685]},"n1819848993":{"id":"n1819848993","loc":[-85.6481871,41.9704451]},"n1819848994":{"id":"n1819848994","loc":[-85.6371364,41.9457602]},"n1819848996":{"id":"n1819848996","loc":[-85.6500362,41.9801023]},"n1819849000":{"id":"n1819849000","loc":[-85.639007,41.9485914]},"n1819849001":{"id":"n1819849001","loc":[-85.6488882,41.9669253]},"n1819849002":{"id":"n1819849002","loc":[-85.6484698,41.9565062]},"n1819849004":{"id":"n1819849004","loc":[-85.6510769,41.9761064]},"n1819849005":{"id":"n1819849005","loc":[-85.6503581,41.9799029]},"n1819849006":{"id":"n1819849006","loc":[-85.6489381,41.9703893]},"n1819849008":{"id":"n1819849008","loc":[-85.6497457,41.9833588]},"n1819849011":{"id":"n1819849011","loc":[-85.6497358,41.9717593]},"n1819849012":{"id":"n1819849012","loc":[-85.6494676,41.9796796]},"n1819849019":{"id":"n1819849019","loc":[-85.6486093,41.9771034]},"n1819849021":{"id":"n1819849021","loc":[-85.6504546,41.9796556]},"n1819849022":{"id":"n1819849022","loc":[-85.6371294,41.9454154]},"n1819849023":{"id":"n1819849023","loc":[-85.6503436,41.9759249]},"n1819849025":{"id":"n1819849025","loc":[-85.6462382,41.9693822]},"n1819849026":{"id":"n1819849026","loc":[-85.6497573,41.983093]},"n1819849028":{"id":"n1819849028","loc":[-85.6497465,41.9602799]},"n1819849029":{"id":"n1819849029","loc":[-85.6374728,41.9460698]},"n1819849030":{"id":"n1819849030","loc":[-85.6486592,41.9566039]},"n1819849031":{"id":"n1819849031","loc":[-85.6515989,41.9654993]},"n1819849032":{"id":"n1819849032","loc":[-85.6387028,41.9482658]},"n1819849033":{"id":"n1819849033","loc":[-85.6464742,41.9688398]},"n1819849034":{"id":"n1819849034","loc":[-85.6495212,41.9589236]},"n1819849035":{"id":"n1819849035","loc":[-85.6490599,41.9790096]},"n1819849036":{"id":"n1819849036","loc":[-85.6489918,41.9800724]},"n1819849038":{"id":"n1819849038","loc":[-85.6499182,41.9659042]},"n1819849040":{"id":"n1819849040","loc":[-85.639758,41.9490143]},"n1819849041":{"id":"n1819849041","loc":[-85.6514846,41.9755241]},"n1819849042":{"id":"n1819849042","loc":[-85.6436633,41.9540647]},"n1819849045":{"id":"n1819849045","loc":[-85.6475541,41.9726387]},"n1819849046":{"id":"n1819849046","loc":[-85.6488308,41.9718331]},"n1819849047":{"id":"n1819849047","loc":[-85.6377694,41.9460953]},"n1819849048":{"id":"n1819849048","loc":[-85.6490706,41.9804452]},"n1819849049":{"id":"n1819849049","loc":[-85.6485449,41.9766248]},"n1819849051":{"id":"n1819849051","loc":[-85.6483625,41.9790256]},"n1819849052":{"id":"n1819849052","loc":[-85.6490706,41.9585167]},"n1819849053":{"id":"n1819849053","loc":[-85.6425008,41.9522874]},"n1819849054":{"id":"n1819849054","loc":[-85.6475793,41.9632158]},"n1819849055":{"id":"n1819849055","loc":[-85.6408631,41.9499399]},"n1819849056":{"id":"n1819849056","loc":[-85.6483373,41.9814681]},"n1819849057":{"id":"n1819849057","loc":[-85.6313548,41.9442876]},"n1819849058":{"id":"n1819849058","loc":[-85.6432663,41.9529796]},"n1819849059":{"id":"n1819849059","loc":[-85.6487128,41.9582873]},"n1819849060":{"id":"n1819849060","loc":[-85.6482338,41.9817612]},"n1819849062":{"id":"n1819849062","loc":[-85.6485664,41.9788661]},"n1819849063":{"id":"n1819849063","loc":[-85.6373081,41.9448824]},"n1819849064":{"id":"n1819849064","loc":[-85.6472215,41.9557582]},"n1819849065":{"id":"n1819849065","loc":[-85.6348984,41.9440414]},"n1819849066":{"id":"n1819849066","loc":[-85.6501972,41.9647315]},"n1819849067":{"id":"n1819849067","loc":[-85.6489741,41.9808281]},"n1819849068":{"id":"n1819849068","loc":[-85.6420111,41.9515034]},"n1819849069":{"id":"n1819849069","loc":[-85.6397972,41.9488882]},"n1819849070":{"id":"n1819849070","loc":[-85.6499718,41.9593465]},"n1819849071":{"id":"n1819849071","loc":[-85.6486844,41.9811311]},"n1819849072":{"id":"n1819849072","loc":[-85.6390392,41.9474663]},"n1819849074":{"id":"n1819849074","loc":[-85.6495642,41.9616362]},"n1819849075":{"id":"n1819849075","loc":[-85.6483518,41.9791931]},"n1819849076":{"id":"n1819849076","loc":[-85.6478974,41.9833104]},"n1819849077":{"id":"n1819849077","loc":[-85.640155,41.948719]},"n1819849078":{"id":"n1819849078","loc":[-85.6399366,41.9487845]},"n1819849079":{"id":"n1819849079","loc":[-85.6492959,41.9825348]},"n1819849080":{"id":"n1819849080","loc":[-85.6505083,41.9648352]},"n1819849081":{"id":"n1819849081","loc":[-85.6492959,41.9645241]},"n1819849082":{"id":"n1819849082","loc":[-85.6402049,41.9491835]},"n1819849083":{"id":"n1819849083","loc":[-85.6495175,41.9826963]},"n1819849084":{"id":"n1819849084","loc":[-85.6480836,41.9728361]},"n1819849085":{"id":"n1819849085","loc":[-85.6374349,41.9443425]},"n1819849086":{"id":"n1819849086","loc":[-85.6478331,41.9681238]},"n1819849089":{"id":"n1819849089","loc":[-85.639368,41.9486169]},"n1819849092":{"id":"n1819849092","loc":[-85.6503581,41.9788022]},"n1819849093":{"id":"n1819849093","loc":[-85.64862,41.9568014]},"n1819849094":{"id":"n1819849094","loc":[-85.6496999,41.9828877]},"n1819849095":{"id":"n1819849095","loc":[-85.647472,41.972198]},"n1819849096":{"id":"n1819849096","loc":[-85.6485771,41.9644523]},"n1819849097":{"id":"n1819849097","loc":[-85.6388353,41.9480488]},"n1819849099":{"id":"n1819849099","loc":[-85.6472752,41.9683312]},"n1819849104":{"id":"n1819849104","loc":[-85.6479548,41.9836035]},"n1819849105":{"id":"n1819849105","loc":[-85.6462489,41.9691668]},"n1819849107":{"id":"n1819849107","loc":[-85.6511912,41.9746328]},"n1819849108":{"id":"n1819849108","loc":[-85.6498646,41.9714881]},"n1819849111":{"id":"n1819849111","loc":[-85.6488239,41.961684]},"n1819849112":{"id":"n1819849112","loc":[-85.6469356,41.9553812]},"n1819849114":{"id":"n1819849114","loc":[-85.6479548,41.9640853]},"n1819849119":{"id":"n1819849119","loc":[-85.6491565,41.961692]},"n1819849121":{"id":"n1819849121","loc":[-85.651667,41.9656728]},"n1819849124":{"id":"n1819849124","loc":[-85.6388423,41.9484414]},"n1819849126":{"id":"n1819849126","loc":[-85.6371686,41.9450978]},"n1819849127":{"id":"n1819849127","loc":[-85.6502615,41.9656728]},"n1819849129":{"id":"n1819849129","loc":[-85.6498501,41.9613031]},"n1819849131":{"id":"n1819849131","loc":[-85.6513881,41.9653298]},"n1819849133":{"id":"n1819849133","loc":[-85.639883,41.9485291]},"n1819849139":{"id":"n1819849139","loc":[-85.6508693,41.9658264]},"n1819849140":{"id":"n1819849140","loc":[-85.6486806,41.9761642]},"n1819849141":{"id":"n1819849141","loc":[-85.6483159,41.9717613]},"n1819849144":{"id":"n1819849144","loc":[-85.6443714,41.9546232]},"n1819849146":{"id":"n1819849146","loc":[-85.641775,41.9513359]},"n1819849147":{"id":"n1819849147","loc":[-85.6495604,41.9757335]},"n1819849148":{"id":"n1819849148","loc":[-85.6465671,41.9551678]},"n1819849150":{"id":"n1819849150","loc":[-85.6485127,41.9794084]},"n1819849151":{"id":"n1819849151","loc":[-85.6499144,41.9757096]},"n1819849152":{"id":"n1819849152","loc":[-85.6433736,41.9531072]},"n1819849154":{"id":"n1819849154","loc":[-85.6489741,41.9607426]},"n1819849155":{"id":"n1819849155","loc":[-85.640627,41.9507697]},"n1819849156":{"id":"n1819849156","loc":[-85.6509659,41.9743058]},"n1819849157":{"id":"n1819849157","loc":[-85.6486844,41.9704431]},"n1819849158":{"id":"n1819849158","loc":[-85.6498538,41.9711132]},"n1819849159":{"id":"n1819849159","loc":[-85.6358937,41.943719]},"n1819849160":{"id":"n1819849160","loc":[-85.6497358,41.9707702]},"n1819849161":{"id":"n1819849161","loc":[-85.6480476,41.9564842]},"n1819849162":{"id":"n1819849162","loc":[-85.6482982,41.9574556]},"n1819849163":{"id":"n1819849163","loc":[-85.6501757,41.9757794]},"n1819849164":{"id":"n1819849164","loc":[-85.6372973,41.9459916]},"n1819849165":{"id":"n1819849165","loc":[-85.6513773,41.9750775]},"n1819849166":{"id":"n1819849166","loc":[-85.6436418,41.9537455]},"n1819849167":{"id":"n1819849167","loc":[-85.6483625,41.9571524]},"n1819849169":{"id":"n1819849169","loc":[-85.647751,41.9727962]},"n1819849170":{"id":"n1819849170","loc":[-85.6504546,41.9656808]},"n1819849171":{"id":"n1819849171","loc":[-85.6479977,41.971839]},"n1819849172":{"id":"n1819849172","loc":[-85.6482767,41.9642449]},"n1819849174":{"id":"n1819849174","loc":[-85.6414317,41.9512086]},"n1819849176":{"id":"n1819849176","loc":[-85.6469034,41.9685287]},"n1819849179":{"id":"n1819849179","loc":[-85.6408631,41.9497564]},"n1819849182":{"id":"n1819849182","loc":[-85.6476721,41.96384]},"n1819849183":{"id":"n1819849183","loc":[-85.6479725,41.983111]},"n1819849184":{"id":"n1819849184","loc":[-85.640788,41.9500516]},"n1819849185":{"id":"n1819849185","loc":[-85.6427798,41.9528778]},"n1819849186":{"id":"n1819849186","loc":[-85.6435308,41.9534124]},"n1819849187":{"id":"n1819849187","loc":[-85.6483733,41.9821998]},"n1819849189":{"id":"n1819849189","loc":[-85.6351752,41.9440796]},"n1819849191":{"id":"n1819849191","loc":[-85.6487021,41.9601463]},"n1819849192":{"id":"n1819849192","loc":[-85.6363811,41.9437605]},"n1819849193":{"id":"n1819849193","loc":[-85.6490883,41.9759728]},"n1819849194":{"id":"n1819849194","loc":[-85.6423292,41.9520081]},"n1819849195":{"id":"n1819849195","loc":[-85.6500003,41.960242]},"n1819849196":{"id":"n1819849196","loc":[-85.6385778,41.9466443]},"n1819849197":{"id":"n1819849197","loc":[-85.6494032,41.9718789]},"n1819849198":{"id":"n1819849198","loc":[-85.6404339,41.9506501]},"n1819849199":{"id":"n1819849199","loc":[-85.6426226,41.9527083]},"n1819849200":{"id":"n1819849200","loc":[-85.6439101,41.9545035]},"n1819849201":{"id":"n1819849201","loc":[-85.6516563,41.9657845]},"n1819849202":{"id":"n1819849202","loc":[-85.6473395,41.9699585]},"n1819858501":{"id":"n1819858501","loc":[-85.6361263,41.9437126]},"n1819858503":{"id":"n1819858503","loc":[-85.6350068,41.944034]},"n1819858513":{"id":"n1819858513","loc":[-85.6371402,41.9453282]},"n1819858518":{"id":"n1819858518","loc":[-85.6348713,41.9432923]},"n1819858523":{"id":"n1819858523","loc":[-85.6357047,41.943799]},"n1819858526":{"id":"n1819858526","loc":[-85.6349947,41.9435756]},"n1819858531":{"id":"n1819858531","loc":[-85.6350376,41.943827]},"n1820937508":{"id":"n1820937508","loc":[-85.1026013,42.0881722]},"n1820937509":{"id":"n1820937509","loc":[-85.0558088,42.102493]},"n1820937511":{"id":"n1820937511","loc":[-85.3030116,41.9724451]},"n1820937513":{"id":"n1820937513","loc":[-85.0353221,42.1027398]},"n1820937514":{"id":"n1820937514","loc":[-85.0835468,42.1015469]},"n1820937515":{"id":"n1820937515","loc":[-85.2421298,42.0106305]},"n1820937517":{"id":"n1820937517","loc":[-85.0090632,42.0910452]},"n1820937518":{"id":"n1820937518","loc":[-85.086626,42.0948838]},"n1820937520":{"id":"n1820937520","loc":[-85.2552039,42.0015448]},"n1820937521":{"id":"n1820937521","loc":[-85.3739614,41.9969917]},"n1820937522":{"id":"n1820937522","loc":[-85.4831166,41.993898]},"n1820937523":{"id":"n1820937523","loc":[-85.0341084,42.0977657]},"n1820937524":{"id":"n1820937524","loc":[-85.3272802,41.9710333]},"n1820937525":{"id":"n1820937525","loc":[-85.2125568,42.0414521]},"n1820937526":{"id":"n1820937526","loc":[-85.3798022,41.9992458]},"n1820937527":{"id":"n1820937527","loc":[-85.2652021,41.999768]},"n1820937528":{"id":"n1820937528","loc":[-85.3852739,42.0004896]},"n1820937529":{"id":"n1820937529","loc":[-85.3911919,42.0030513]},"n1820937530":{"id":"n1820937530","loc":[-85.5440349,41.9717109]},"n1820937531":{"id":"n1820937531","loc":[-85.2790155,41.9911764]},"n1820937532":{"id":"n1820937532","loc":[-85.4723277,41.9950518]},"n1820937533":{"id":"n1820937533","loc":[-85.5690546,41.9653931]},"n1820937535":{"id":"n1820937535","loc":[-85.5674882,41.9649623]},"n1820937536":{"id":"n1820937536","loc":[-85.6362815,41.9189165]},"n1820937537":{"id":"n1820937537","loc":[-85.5659003,41.963638]},"n1820937539":{"id":"n1820937539","loc":[-85.6391353,41.9122262]},"n1820937540":{"id":"n1820937540","loc":[-85.4834385,41.9894803]},"n1820937541":{"id":"n1820937541","loc":[-85.6399078,41.9160744]},"n1820937542":{"id":"n1820937542","loc":[-85.632874,41.941031]},"n1820937543":{"id":"n1820937543","loc":[-85.1307591,42.0726961]},"n1820937544":{"id":"n1820937544","loc":[-85.6444397,41.9128378]},"n1820937545":{"id":"n1820937545","loc":[-85.6197204,41.9420365]},"n1820937546":{"id":"n1820937546","loc":[-85.1164857,42.0864631]},"n1820937547":{"id":"n1820937547","loc":[-85.6476111,41.9142222]},"n1820937548":{"id":"n1820937548","loc":[-85.2915747,41.9774223]},"n1820937549":{"id":"n1820937549","loc":[-85.6430192,41.9102461]},"n1820937550":{"id":"n1820937550","loc":[-85.1597495,42.0639017]},"n1820937551":{"id":"n1820937551","loc":[-85.5504079,41.9701793]},"n1820937553":{"id":"n1820937553","loc":[-85.2781317,41.9948951]},"n1820937555":{"id":"n1820937555","loc":[-85.3724594,41.997518]},"n1820937556":{"id":"n1820937556","loc":[-85.5629434,41.9665155]},"n1820937557":{"id":"n1820937557","loc":[-85.3791971,41.9990808]},"n1820937558":{"id":"n1820937558","loc":[-85.001891,42.0878843]},"n1820937560":{"id":"n1820937560","loc":[-85.3140838,41.9709056]},"n1820937561":{"id":"n1820937561","loc":[-85.2468032,42.0146987]},"n1820937563":{"id":"n1820937563","loc":[-85.0877378,42.097255]},"n1820937564":{"id":"n1820937564","loc":[-85.2442498,42.0150654]},"n1820937566":{"id":"n1820937566","loc":[-85.3108973,41.9701478]},"n1820937568":{"id":"n1820937568","loc":[-85.0344584,42.1016572]},"n1820937569":{"id":"n1820937569","loc":[-85.2331025,42.0297387]},"n1820937570":{"id":"n1820937570","loc":[-85.5058446,41.9746996]},"n1820937571":{"id":"n1820937571","loc":[-85.5622739,41.9676427]},"n1820937572":{"id":"n1820937572","loc":[-85.2792687,41.9890337]},"n1820937574":{"id":"n1820937574","loc":[-84.9909302,42.08695]},"n1820937575":{"id":"n1820937575","loc":[-85.6218233,41.9418609]},"n1820937576":{"id":"n1820937576","loc":[-85.3577437,41.9931062]},"n1820937577":{"id":"n1820937577","loc":[-85.639028,41.9165853]},"n1820937578":{"id":"n1820937578","loc":[-84.9956576,42.0865348]},"n1820937579":{"id":"n1820937579","loc":[-85.4828376,41.990198]},"n1820937580":{"id":"n1820937580","loc":[-85.3244478,41.9720543]},"n1820937582":{"id":"n1820937582","loc":[-85.0517479,42.1035159]},"n1820937583":{"id":"n1820937583","loc":[-85.225646,42.0338025]},"n1820937584":{"id":"n1820937584","loc":[-84.9941019,42.0862163]},"n1820937586":{"id":"n1820937586","loc":[-85.1051762,42.0879452]},"n1820937587":{"id":"n1820937587","loc":[-85.1245203,42.0753162]},"n1820937588":{"id":"n1820937588","loc":[-85.3250808,41.9719506]},"n1820937589":{"id":"n1820937589","loc":[-85.2720109,41.997933]},"n1820937590":{"id":"n1820937590","loc":[-85.2556653,42.0027248]},"n1820937591":{"id":"n1820937591","loc":[-85.0872483,42.0943544]},"n1820937592":{"id":"n1820937592","loc":[-85.2778353,41.9955023]},"n1820937593":{"id":"n1820937593","loc":[-85.2984733,41.9735538]},"n1820937594":{"id":"n1820937594","loc":[-85.101578,42.0889552]},"n1820937595":{"id":"n1820937595","loc":[-85.3888745,42.0016959]},"n1820937596":{"id":"n1820937596","loc":[-84.9903508,42.0870654]},"n1820937597":{"id":"n1820937597","loc":[-85.6405558,41.9146261]},"n1820937598":{"id":"n1820937598","loc":[-85.6460704,41.9141311]},"n1820937599":{"id":"n1820937599","loc":[-85.0377468,42.1037428]},"n1820937600":{"id":"n1820937600","loc":[-85.2298345,42.0312899]},"n1820937601":{"id":"n1820937601","loc":[-85.1080958,42.0861964]},"n1820937602":{"id":"n1820937602","loc":[-85.6325307,41.9402329]},"n1820937603":{"id":"n1820937603","loc":[-85.1165984,42.0832184]},"n1820937604":{"id":"n1820937604","loc":[-85.6354446,41.9190602]},"n1820937605":{"id":"n1820937605","loc":[-85.1114592,42.0862959]},"n1820937606":{"id":"n1820937606","loc":[-85.0858763,42.1001646]},"n1820937607":{"id":"n1820937607","loc":[-85.0472083,42.1015151]},"n1820937608":{"id":"n1820937608","loc":[-85.0802477,42.1027609]},"n1820937610":{"id":"n1820937610","loc":[-85.0924585,42.0928564]},"n1820937611":{"id":"n1820937611","loc":[-85.0329617,42.09827]},"n1820937612":{"id":"n1820937612","loc":[-85.2814617,41.993465]},"n1820937613":{"id":"n1820937613","loc":[-85.3097708,41.9700282]},"n1820937614":{"id":"n1820937614","loc":[-85.2809427,41.993695]},"n1820937615":{"id":"n1820937615","loc":[-85.0583233,42.1026494]},"n1820937617":{"id":"n1820937617","loc":[-85.2801592,41.9840021]},"n1820937619":{"id":"n1820937619","loc":[-85.1064154,42.0863449]},"n1820937620":{"id":"n1820937620","loc":[-85.0423173,42.1014662]},"n1820937621":{"id":"n1820937621","loc":[-85.2168913,42.0398107]},"n1820937622":{"id":"n1820937622","loc":[-85.2798481,41.9833401]},"n1820937623":{"id":"n1820937623","loc":[-85.0575468,42.1028672]},"n1820937625":{"id":"n1820937625","loc":[-85.0130369,42.0893067]},"n1820937626":{"id":"n1820937626","loc":[-85.0346985,42.1018256]},"n1820937627":{"id":"n1820937627","loc":[-85.2231569,42.0372768]},"n1820937628":{"id":"n1820937628","loc":[-85.2956195,41.9732268]},"n1820937629":{"id":"n1820937629","loc":[-85.1052312,42.086893]},"n1820937630":{"id":"n1820937630","loc":[-85.4813356,41.9958436]},"n1820937631":{"id":"n1820937631","loc":[-85.0961599,42.0914672]},"n1820937632":{"id":"n1820937632","loc":[-85.308419,41.9704749]},"n1820937633":{"id":"n1820937633","loc":[-85.295952,41.9715119]},"n1820937634":{"id":"n1820937634","loc":[-85.3310933,41.9703923]},"n1820937635":{"id":"n1820937635","loc":[-85.2940745,41.9739686]},"n1820937636":{"id":"n1820937636","loc":[-85.3803343,42.000484]},"n1820937637":{"id":"n1820937637","loc":[-85.1174231,42.0845533]},"n1820937638":{"id":"n1820937638","loc":[-85.0095836,42.089839]},"n1820937639":{"id":"n1820937639","loc":[-85.3179354,41.9705866]},"n1820937640":{"id":"n1820937640","loc":[-85.257708,42.0001189]},"n1820937641":{"id":"n1820937641","loc":[-85.2563522,42.0002771]},"n1820937642":{"id":"n1820937642","loc":[-85.3181929,41.970419]},"n1820937643":{"id":"n1820937643","loc":[-85.2911884,41.9757154]},"n1820937644":{"id":"n1820937644","loc":[-85.2714423,41.9975862]},"n1820937645":{"id":"n1820937645","loc":[-85.0193669,42.089888]},"n1820937646":{"id":"n1820937646","loc":[-85.3889818,42.0039921]},"n1820937647":{"id":"n1820937647","loc":[-85.3408093,41.9853965]},"n1820937648":{"id":"n1820937648","loc":[-85.1258091,42.0748332]},"n1820937649":{"id":"n1820937649","loc":[-85.5722561,41.962782]},"n1820937650":{"id":"n1820937650","loc":[-85.3266902,41.9721819]},"n1820937651":{"id":"n1820937651","loc":[-85.1473255,42.065192]},"n1820937652":{"id":"n1820937652","loc":[-85.1462526,42.0655106]},"n1820937653":{"id":"n1820937653","loc":[-85.4641051,42.0013929]},"n1820937654":{"id":"n1820937654","loc":[-85.5620379,41.9700677]},"n1820937655":{"id":"n1820937655","loc":[-85.3226025,41.971121]},"n1820937656":{"id":"n1820937656","loc":[-85.0200965,42.0899516]},"n1820937657":{"id":"n1820937657","loc":[-85.0624714,42.1044711]},"n1820937658":{"id":"n1820937658","loc":[-85.5649562,41.9637178]},"n1820937659":{"id":"n1820937659","loc":[-85.2360315,42.0253315]},"n1820937660":{"id":"n1820937660","loc":[-85.3881449,41.9994475]},"n1820937661":{"id":"n1820937661","loc":[-85.5032911,41.976263]},"n1820937662":{"id":"n1820937662","loc":[-85.481297,41.9871414]},"n1820937663":{"id":"n1820937663","loc":[-85.1167056,42.0841898]},"n1820937664":{"id":"n1820937664","loc":[-85.2891714,41.9787223]},"n1820937665":{"id":"n1820937665","loc":[-85.4393429,42.0058736]},"n1820937666":{"id":"n1820937666","loc":[-85.0077007,42.0895762]},"n1820937667":{"id":"n1820937667","loc":[-85.2736202,41.9979171]},"n1820937668":{"id":"n1820937668","loc":[-84.9935332,42.0859296]},"n1820937669":{"id":"n1820937669","loc":[-85.0622769,42.1046713]},"n1820937670":{"id":"n1820937670","loc":[-85.2309031,42.0311249]},"n1820937671":{"id":"n1820937671","loc":[-85.0343726,42.10069]},"n1820937672":{"id":"n1820937672","loc":[-85.0596551,42.1048612]},"n1820937673":{"id":"n1820937673","loc":[-85.1338597,42.0707449]},"n1820937674":{"id":"n1820937674","loc":[-85.3117663,41.9689194]},"n1820937675":{"id":"n1820937675","loc":[-85.0705649,42.1057499]},"n1820937676":{"id":"n1820937676","loc":[-85.2441425,42.0180944]},"n1820937677":{"id":"n1820937677","loc":[-85.1171174,42.0862692]},"n1820937678":{"id":"n1820937678","loc":[-85.0346824,42.1005519]},"n1820937680":{"id":"n1820937680","loc":[-85.2389927,42.0229245]},"n1820937681":{"id":"n1820937681","loc":[-85.0834892,42.1018642]},"n1820937682":{"id":"n1820937682","loc":[-85.0619443,42.1049459]},"n1820937683":{"id":"n1820937683","loc":[-85.2845366,41.9811868]},"n1820937684":{"id":"n1820937684","loc":[-85.210411,42.0394123]},"n1820937685":{"id":"n1820937685","loc":[-85.4377383,42.0055942]},"n1820937686":{"id":"n1820937686","loc":[-85.2882058,41.9789138]},"n1820937687":{"id":"n1820937687","loc":[-85.2741191,41.9955808]},"n1820937688":{"id":"n1820937688","loc":[-85.3442211,41.9903575]},"n1820937689":{"id":"n1820937689","loc":[-85.2641413,41.9995237]},"n1820937690":{"id":"n1820937690","loc":[-85.2804489,41.9829174]},"n1820937691":{"id":"n1820937691","loc":[-85.5593342,41.9729074]},"n1820937692":{"id":"n1820937692","loc":[-85.3590912,41.9932601]},"n1820937694":{"id":"n1820937694","loc":[-85.4826445,41.9957479]},"n1820937695":{"id":"n1820937695","loc":[-85.4539127,42.0063041]},"n1820937696":{"id":"n1820937696","loc":[-85.2456767,42.0153683]},"n1820937697":{"id":"n1820937697","loc":[-85.5794015,41.9489631]},"n1820937698":{"id":"n1820937698","loc":[-85.4108686,42.0078507]},"n1820937699":{"id":"n1820937699","loc":[-85.0616386,42.1051529]},"n1820937700":{"id":"n1820937700","loc":[-85.4977979,41.978241]},"n1820937701":{"id":"n1820937701","loc":[-85.2488417,42.0086319]},"n1820937702":{"id":"n1820937702","loc":[-85.5588836,41.9728116]},"n1820937703":{"id":"n1820937703","loc":[-85.4557366,42.0051241]},"n1820937705":{"id":"n1820937705","loc":[-85.0723151,42.1056094]},"n1820937706":{"id":"n1820937706","loc":[-85.0057909,42.0887323]},"n1820937707":{"id":"n1820937707","loc":[-85.0756786,42.105677]},"n1820937708":{"id":"n1820937708","loc":[-85.0901504,42.0940001]},"n1820937709":{"id":"n1820937709","loc":[-85.0979999,42.0910213]},"n1820937710":{"id":"n1820937710","loc":[-85.2376301,42.0239686]},"n1820937711":{"id":"n1820937711","loc":[-85.2780671,41.9902299]},"n1820937712":{"id":"n1820937712","loc":[-85.251481,42.0113188]},"n1820937713":{"id":"n1820937713","loc":[-85.3114767,41.9690311]},"n1820937714":{"id":"n1820937714","loc":[-85.2649621,41.9975662]},"n1820937715":{"id":"n1820937715","loc":[-85.283807,41.9813383]},"n1820937716":{"id":"n1820937716","loc":[-85.5515451,41.9703867]},"n1820937717":{"id":"n1820937717","loc":[-85.1176605,42.0850896]},"n1820937718":{"id":"n1820937718","loc":[-85.1069317,42.0862441]},"n1820937719":{"id":"n1820937719","loc":[-85.2739314,41.9976938]},"n1820937720":{"id":"n1820937720","loc":[-85.5550212,41.9702112]},"n1820937721":{"id":"n1820937721","loc":[-85.3076679,41.9719904]},"n1820937722":{"id":"n1820937722","loc":[-85.592319,41.9440316]},"n1820937723":{"id":"n1820937723","loc":[-85.3139979,41.9704031]},"n1820937724":{"id":"n1820937724","loc":[-85.0421134,42.1013149]},"n1820937725":{"id":"n1820937725","loc":[-85.2508373,42.0102741]},"n1820937726":{"id":"n1820937726","loc":[-85.0830922,42.1038821]},"n1820937727":{"id":"n1820937727","loc":[-85.6342473,41.9360031]},"n1820937730":{"id":"n1820937730","loc":[-85.0500192,42.1024942]},"n1820937731":{"id":"n1820937731","loc":[-85.3498644,41.9926221]},"n1820937732":{"id":"n1820937732","loc":[-85.0234117,42.0918903]},"n1820937733":{"id":"n1820937733","loc":[-85.0464425,42.1009408]},"n1820937734":{"id":"n1820937734","loc":[-85.033938,42.099886]},"n1820937736":{"id":"n1820937736","loc":[-85.0152752,42.0886009]},"n1820937737":{"id":"n1820937737","loc":[-85.0441894,42.1012671]},"n1820937738":{"id":"n1820937738","loc":[-85.4668731,41.9979804]},"n1820937739":{"id":"n1820937739","loc":[-85.4407377,42.006033]},"n1820937740":{"id":"n1820937740","loc":[-85.2262253,42.0344878]},"n1820937741":{"id":"n1820937741","loc":[-85.2550001,42.0033706]},"n1820937742":{"id":"n1820937742","loc":[-85.3071422,41.9722617]},"n1820937743":{"id":"n1820937743","loc":[-85.6147852,41.942228]},"n1820937744":{"id":"n1820937744","loc":[-85.0183853,42.0901825]},"n1820937745":{"id":"n1820937745","loc":[-85.6323161,41.9228489]},"n1820937746":{"id":"n1820937746","loc":[-85.0095568,42.0901376]},"n1820937747":{"id":"n1820937747","loc":[-85.2524037,42.0113826]},"n1820937748":{"id":"n1820937748","loc":[-85.3186864,41.9708578]},"n1820937749":{"id":"n1820937749","loc":[-85.2805669,41.9870883]},"n1820937750":{"id":"n1820937750","loc":[-85.0585768,42.1038144]},"n1820937751":{"id":"n1820937751","loc":[-85.2970786,41.9715358]},"n1820937752":{"id":"n1820937752","loc":[-85.1315758,42.0723445]},"n1820937753":{"id":"n1820937753","loc":[-85.2448291,42.0175444]},"n1820937754":{"id":"n1820937754","loc":[-85.2446468,42.0174248]},"n1820937755":{"id":"n1820937755","loc":[-85.229165,42.032129]},"n1820937756":{"id":"n1820937756","loc":[-85.5612654,41.9724926]},"n1820937757":{"id":"n1820937757","loc":[-85.2331776,42.030854]},"n1820937758":{"id":"n1820937758","loc":[-85.2271909,42.0334519]},"n1820937759":{"id":"n1820937759","loc":[-85.1032396,42.0879214]},"n1820937760":{"id":"n1820937760","loc":[-85.0638447,42.1044154]},"n1820937761":{"id":"n1820937761","loc":[-85.1260706,42.0745556]},"n1820937762":{"id":"n1820937762","loc":[-85.3454485,41.99132]},"n1820937763":{"id":"n1820937763","loc":[-85.2639321,41.9980088]},"n1820937764":{"id":"n1820937764","loc":[-85.0837681,42.1013746]},"n1820937765":{"id":"n1820937765","loc":[-85.2808137,41.9869368]},"n1820937766":{"id":"n1820937766","loc":[-85.6338997,41.9309373]},"n1820937767":{"id":"n1820937767","loc":[-85.2267403,42.0332766]},"n1820937768":{"id":"n1820937768","loc":[-85.0605831,42.1052074]},"n1820937769":{"id":"n1820937769","loc":[-85.0259021,42.0930037]},"n1820937770":{"id":"n1820937770","loc":[-85.232963,42.0313162]},"n1820937771":{"id":"n1820937771","loc":[-85.2404947,42.0125381]},"n1820937772":{"id":"n1820937772","loc":[-85.0910892,42.0935742]},"n1820937773":{"id":"n1820937773","loc":[-85.2554829,42.0019435]},"n1820937774":{"id":"n1820937774","loc":[-85.2799339,41.9867773]},"n1820937775":{"id":"n1820937775","loc":[-85.1075432,42.0852767]},"n1820937776":{"id":"n1820937776","loc":[-85.1176927,42.0854001]},"n1820937777":{"id":"n1820937777","loc":[-85.1067064,42.0863357]},"n1820937778":{"id":"n1820937778","loc":[-85.2517492,42.0106333]},"n1820937779":{"id":"n1820937779","loc":[-85.0987174,42.0909031]},"n1820937780":{"id":"n1820937780","loc":[-85.1160083,42.0863994]},"n1820937781":{"id":"n1820937781","loc":[-85.1268645,42.0739703]},"n1820937782":{"id":"n1820937782","loc":[-85.0454702,42.1002852]},"n1820937783":{"id":"n1820937783","loc":[-85.1334145,42.0705418]},"n1820937784":{"id":"n1820937784","loc":[-85.5866542,41.947431]},"n1820937786":{"id":"n1820937786","loc":[-85.2359886,42.0250366]},"n1820937787":{"id":"n1820937787","loc":[-85.3138048,41.9698527]},"n1820937788":{"id":"n1820937788","loc":[-85.1274291,42.0733081]},"n1820937790":{"id":"n1820937790","loc":[-85.6292905,41.9411267]},"n1820937791":{"id":"n1820937791","loc":[-85.5958809,41.9417333]},"n1820937792":{"id":"n1820937792","loc":[-85.1271019,42.0737581]},"n1820937793":{"id":"n1820937793","loc":[-85.2312679,42.0314437]},"n1820937794":{"id":"n1820937794","loc":[-85.1081387,42.0863516]},"n1820937795":{"id":"n1820937795","loc":[-85.2424473,42.0212109]},"n1820937796":{"id":"n1820937796","loc":[-85.2710654,41.9975236]},"n1820937797":{"id":"n1820937797","loc":[-85.4798408,41.9863223]},"n1820937798":{"id":"n1820937798","loc":[-85.035939,42.104296]},"n1820937799":{"id":"n1820937799","loc":[-85.2178139,42.0395398]},"n1820937800":{"id":"n1820937800","loc":[-85.0630709,42.1042614]},"n1820937801":{"id":"n1820937801","loc":[-85.0440124,42.1014861]},"n1820937802":{"id":"n1820937802","loc":[-85.1321874,42.0720458]},"n1820937804":{"id":"n1820937804","loc":[-85.079427,42.1029121]},"n1820937805":{"id":"n1820937805","loc":[-85.2962632,41.9738968]},"n1820937806":{"id":"n1820937806","loc":[-85.6334748,41.9274627]},"n1820937807":{"id":"n1820937807","loc":[-85.1057341,42.0872804]},"n1820937808":{"id":"n1820937808","loc":[-85.4960169,41.9778263]},"n1820937809":{"id":"n1820937809","loc":[-85.2821226,41.9910273]},"n1820937810":{"id":"n1820937810","loc":[-85.0013868,42.0885054]},"n1820937811":{"id":"n1820937811","loc":[-85.2952547,41.9729795]},"n1820937812":{"id":"n1820937812","loc":[-85.1298375,42.0667842]},"n1820937813":{"id":"n1820937813","loc":[-85.1339201,42.0710025]},"n1820937814":{"id":"n1820937814","loc":[-85.0374356,42.103691]},"n1820937815":{"id":"n1820937815","loc":[-85.0061115,42.0880607]},"n1820937817":{"id":"n1820937817","loc":[-85.2398402,42.0226934]},"n1820937818":{"id":"n1820937818","loc":[-85.123501,42.076236]},"n1820937819":{"id":"n1820937819","loc":[-85.1209489,42.0791294]},"n1820937820":{"id":"n1820937820","loc":[-85.0818624,42.1025778]},"n1820937821":{"id":"n1820937821","loc":[-85.4428835,42.0054749]},"n1820937822":{"id":"n1820937822","loc":[-85.4710359,41.9961147]},"n1820937823":{"id":"n1820937823","loc":[-85.4253354,42.006198]},"n1820937824":{"id":"n1820937824","loc":[-85.5486483,41.9709451]},"n1820937825":{"id":"n1820937825","loc":[-85.2303238,42.0310452]},"n1820937826":{"id":"n1820937826","loc":[-85.6450405,41.9136361]},"n1820937828":{"id":"n1820937828","loc":[-85.2606853,41.9964073]},"n1820937830":{"id":"n1820937830","loc":[-85.097383,42.0911447]},"n1820937831":{"id":"n1820937831","loc":[-85.0498207,42.102136]},"n1820937832":{"id":"n1820937832","loc":[-85.1232435,42.0763793]},"n1820937833":{"id":"n1820937833","loc":[-85.394093,42.0055921]},"n1820937834":{"id":"n1820937834","loc":[-85.3566665,41.9928295]},"n1820937835":{"id":"n1820937835","loc":[-85.3543276,41.9920002]},"n1820937837":{"id":"n1820937837","loc":[-85.084668,42.1034932]},"n1820937838":{"id":"n1820937838","loc":[-85.4400296,42.0060649]},"n1820937839":{"id":"n1820937839","loc":[-85.2362246,42.025714]},"n1820937840":{"id":"n1820937840","loc":[-85.0409225,42.1012791]},"n1820937841":{"id":"n1820937841","loc":[-85.2442283,42.019832]},"n1820937842":{"id":"n1820937842","loc":[-85.1123001,42.084824]},"n1820937843":{"id":"n1820937843","loc":[-85.1603074,42.0638061]},"n1820937844":{"id":"n1820937844","loc":[-85.1359744,42.0650646]},"n1820937845":{"id":"n1820937845","loc":[-85.1757569,42.053849]},"n1820937846":{"id":"n1820937846","loc":[-85.5200925,41.9716686]},"n1820937848":{"id":"n1820937848","loc":[-85.5525322,41.9701315]},"n1820937849":{"id":"n1820937849","loc":[-85.0406489,42.10149]},"n1820937850":{"id":"n1820937850","loc":[-85.0142547,42.088825]},"n1820937851":{"id":"n1820937851","loc":[-85.343749,41.9881884]},"n1820937852":{"id":"n1820937852","loc":[-85.074996,42.1060205]},"n1820937853":{"id":"n1820937853","loc":[-85.2436275,42.0136864]},"n1820937854":{"id":"n1820937854","loc":[-85.2641453,41.9980897]},"n1820937856":{"id":"n1820937856","loc":[-85.2802343,41.9870086]},"n1820937857":{"id":"n1820937857","loc":[-85.0099256,42.0909946]},"n1820937858":{"id":"n1820937858","loc":[-85.493957,41.9786079]},"n1820937859":{"id":"n1820937859","loc":[-85.0739405,42.1059795]},"n1820937860":{"id":"n1820937860","loc":[-85.2331605,42.0301423]},"n1820937862":{"id":"n1820937862","loc":[-85.2035231,42.0438425]},"n1820937863":{"id":"n1820937863","loc":[-85.0884928,42.0986971]},"n1820937864":{"id":"n1820937864","loc":[-85.131597,42.0690142]},"n1820937865":{"id":"n1820937865","loc":[-85.3937454,42.0052677]},"n1820937866":{"id":"n1820937866","loc":[-85.2212729,42.0378561]},"n1820937867":{"id":"n1820937867","loc":[-85.0886068,42.0982421]},"n1820937868":{"id":"n1820937868","loc":[-85.0875004,42.0968064]},"n1820937869":{"id":"n1820937869","loc":[-85.0771323,42.1042642]},"n1820937870":{"id":"n1820937870","loc":[-85.0164554,42.0894887]},"n1820937871":{"id":"n1820937871","loc":[-85.6069102,41.9415577]},"n1820937872":{"id":"n1820937872","loc":[-85.3273875,41.9704908]},"n1820937873":{"id":"n1820937873","loc":[-85.3890891,41.9997983]},"n1820937875":{"id":"n1820937875","loc":[-85.5091276,41.9723705]},"n1820937876":{"id":"n1820937876","loc":[-85.0770626,42.1047696]},"n1820937877":{"id":"n1820937877","loc":[-85.612575,41.9419567]},"n1820937878":{"id":"n1820937878","loc":[-85.3868146,42.0036094]},"n1820937879":{"id":"n1820937879","loc":[-85.2722738,41.9981204]},"n1820937880":{"id":"n1820937880","loc":[-85.3064878,41.9723733]},"n1820937882":{"id":"n1820937882","loc":[-85.1270845,42.0727678]},"n1820937884":{"id":"n1820937884","loc":[-85.3316512,41.97923]},"n1820937885":{"id":"n1820937885","loc":[-85.3932519,42.0042472]},"n1820937886":{"id":"n1820937886","loc":[-85.2457411,42.0175444]},"n1820937887":{"id":"n1820937887","loc":[-85.1397509,42.0648415]},"n1820937891":{"id":"n1820937891","loc":[-85.3196735,41.9719665]},"n1820937892":{"id":"n1820937892","loc":[-85.3372473,41.9845033]},"n1820937894":{"id":"n1820937894","loc":[-85.3254778,41.9719745]},"n1820937897":{"id":"n1820937897","loc":[-85.3185148,41.9691268]},"n1820937899":{"id":"n1820937899","loc":[-85.5419106,41.9714556]},"n1820937901":{"id":"n1820937901","loc":[-85.3293509,41.9748368]},"n1820937903":{"id":"n1820937903","loc":[-85.0798078,42.1028365]},"n1820937905":{"id":"n1820937905","loc":[-85.3954191,42.0056025]},"n1820937909":{"id":"n1820937909","loc":[-85.3417534,41.9857155]},"n1820937913":{"id":"n1820937913","loc":[-84.9927822,42.0857107]},"n1820937915":{"id":"n1820937915","loc":[-85.5444212,41.9712801]},"n1820937917":{"id":"n1820937917","loc":[-85.259088,41.9981682]},"n1820937921":{"id":"n1820937921","loc":[-85.2784576,41.9876358]},"n1820937922":{"id":"n1820937922","loc":[-84.9971918,42.087753]},"n1820937924":{"id":"n1820937924","loc":[-85.5310688,41.966899]},"n1820937928":{"id":"n1820937928","loc":[-85.3766436,41.9979326]},"n1820937930":{"id":"n1820937930","loc":[-85.5494852,41.9704346]},"n1820937933":{"id":"n1820937933","loc":[-85.5548281,41.9695412]},"n1820937935":{"id":"n1820937935","loc":[-85.0768588,42.105088]},"n1820937937":{"id":"n1820937937","loc":[-85.2646885,41.9978054]},"n1820937939":{"id":"n1820937939","loc":[-85.2441532,42.0176082]},"n1820937941":{"id":"n1820937941","loc":[-85.105553,42.0877928]},"n1820937943":{"id":"n1820937943","loc":[-85.0879457,42.0958909]},"n1820937944":{"id":"n1820937944","loc":[-85.3187015,41.9704402]},"n1820937945":{"id":"n1820937945","loc":[-85.5624456,41.970626]},"n1820937946":{"id":"n1820937946","loc":[-85.0580176,42.1028644]},"n1820937948":{"id":"n1820937948","loc":[-85.3016061,41.9726286]},"n1820937949":{"id":"n1820937949","loc":[-85.4310388,42.0069418]},"n1820937950":{"id":"n1820937950","loc":[-85.2945144,41.9740723]},"n1820937951":{"id":"n1820937951","loc":[-85.1170222,42.082657]},"n1820937952":{"id":"n1820937952","loc":[-85.0864503,42.0947632]},"n1820937953":{"id":"n1820937953","loc":[-85.4285926,42.0059533]},"n1820937970":{"id":"n1820937970","loc":[-85.3629965,41.9938023]},"n1820937972":{"id":"n1820937972","loc":[-85.2438099,42.0199755]},"n1820937974":{"id":"n1820937974","loc":[-85.1327654,42.0699285]},"n1820937977":{"id":"n1820937977","loc":[-85.1515956,42.0611935]},"n1820937978":{"id":"n1820937978","loc":[-85.0107369,42.0896638]},"n1820937979":{"id":"n1820937979","loc":[-85.1152626,42.0862083]},"n1820937980":{"id":"n1820937980","loc":[-85.4531831,42.0062881]},"n1820937981":{"id":"n1820937981","loc":[-85.0341473,42.0985924]},"n1820937982":{"id":"n1820937982","loc":[-85.0877485,42.0960171]},"n1820937983":{"id":"n1820937983","loc":[-85.2756373,41.9951742]},"n1820937984":{"id":"n1820937984","loc":[-85.2965421,41.9714401]},"n1820937985":{"id":"n1820937985","loc":[-85.2409775,42.0226934]},"n1820937986":{"id":"n1820937986","loc":[-85.0170723,42.0900579]},"n1820937987":{"id":"n1820937987","loc":[-85.1034663,42.0880555]},"n1820937988":{"id":"n1820937988","loc":[-85.0585071,42.1031577]},"n1820937990":{"id":"n1820937990","loc":[-85.0819174,42.1032373]},"n1820937992":{"id":"n1820937992","loc":[-85.0546608,42.1030542]},"n1820937993":{"id":"n1820937993","loc":[-85.0100811,42.0906125]},"n1820937995":{"id":"n1820937995","loc":[-85.6304278,41.9432655]},"n1820937997":{"id":"n1820937997","loc":[-85.0255628,42.092778]},"n1820938011":{"id":"n1820938011","loc":[-85.2316756,42.0317146]},"n1820938012":{"id":"n1820938012","loc":[-85.4067917,42.008042]},"n1820938013":{"id":"n1820938013","loc":[-85.390398,42.0028759]},"n1820938014":{"id":"n1820938014","loc":[-85.0161604,42.0886527]},"n1820938015":{"id":"n1820938015","loc":[-85.125337,42.0744589]},"n1820938016":{"id":"n1820938016","loc":[-85.2151317,42.0404801]},"n1820938017":{"id":"n1820938017","loc":[-85.3165085,41.9706025]},"n1820938018":{"id":"n1820938018","loc":[-85.5641193,41.9640688]},"n1820938019":{"id":"n1820938019","loc":[-85.147583,42.0642203]},"n1820938022":{"id":"n1820938022","loc":[-85.2803781,41.9947886]},"n1820938024":{"id":"n1820938024","loc":[-85.2692469,41.9982053]},"n1820938026":{"id":"n1820938026","loc":[-85.4321975,42.0067505]},"n1820938028":{"id":"n1820938028","loc":[-85.572535,41.9633405]},"n1820938030":{"id":"n1820938030","loc":[-85.3237505,41.9716475]},"n1820938032":{"id":"n1820938032","loc":[-85.6487698,41.9141583]},"n1820938033":{"id":"n1820938033","loc":[-85.0526371,42.1038315]},"n1820938034":{"id":"n1820938034","loc":[-85.088069,42.0978731]},"n1820938035":{"id":"n1820938035","loc":[-85.2516312,42.0102267]},"n1820938039":{"id":"n1820938039","loc":[-85.2731374,41.9982958]},"n1820938040":{"id":"n1820938040","loc":[-85.5453224,41.9713439]},"n1820938041":{"id":"n1820938041","loc":[-85.4480548,42.0049647]},"n1820938043":{"id":"n1820938043","loc":[-85.2504081,42.010322]},"n1820938045":{"id":"n1820938045","loc":[-85.2663447,41.99919]},"n1820938046":{"id":"n1820938046","loc":[-85.0507287,42.102907]},"n1820938047":{"id":"n1820938047","loc":[-85.0408246,42.1024743]},"n1820938048":{"id":"n1820938048","loc":[-85.2796335,41.9866099]},"n1820938050":{"id":"n1820938050","loc":[-85.452475,42.0061127]},"n1820938051":{"id":"n1820938051","loc":[-85.2410569,42.0128147]},"n1820938052":{"id":"n1820938052","loc":[-85.0413302,42.1011477]},"n1820938053":{"id":"n1820938053","loc":[-85.6327409,41.9197627]},"n1820938056":{"id":"n1820938056","loc":[-85.1072039,42.0857994]},"n1820938057":{"id":"n1820938057","loc":[-85.2001114,42.0448145]},"n1820938058":{"id":"n1820938058","loc":[-85.2655347,41.9978186]},"n1820938059":{"id":"n1820938059","loc":[-85.2330918,42.0304874]},"n1820938060":{"id":"n1820938060","loc":[-85.2601113,41.9966545]},"n1820938061":{"id":"n1820938061","loc":[-85.5397863,41.9708494]},"n1820938062":{"id":"n1820938062","loc":[-85.2702085,41.9977217]},"n1820938063":{"id":"n1820938063","loc":[-85.2219982,42.03699]},"n1820938064":{"id":"n1820938064","loc":[-85.0668957,42.105121]},"n1820938065":{"id":"n1820938065","loc":[-85.2328665,42.0270769]},"n1820938066":{"id":"n1820938066","loc":[-85.3189654,41.9694778]},"n1820938067":{"id":"n1820938067","loc":[-85.3814115,42.0022915]},"n1820938068":{"id":"n1820938068","loc":[-85.2759108,41.9956008]},"n1820938069":{"id":"n1820938069","loc":[-85.0391938,42.1034853]},"n1820938070":{"id":"n1820938070","loc":[-85.2850623,41.9810353]},"n1820938071":{"id":"n1820938071","loc":[-85.538074,41.970855]},"n1820938073":{"id":"n1820938073","loc":[-85.1319661,42.0670932]},"n1820938074":{"id":"n1820938074","loc":[-85.2816763,41.9913678]},"n1820938075":{"id":"n1820938075","loc":[-85.3182144,41.9700282]},"n1820938076":{"id":"n1820938076","loc":[-85.5909028,41.9458989]},"n1820938077":{"id":"n1820938077","loc":[-85.4057617,42.0074361]},"n1820938078":{"id":"n1820938078","loc":[-85.2620438,41.9967729]},"n1820938079":{"id":"n1820938079","loc":[-85.1122143,42.0851107]},"n1820938080":{"id":"n1820938080","loc":[-85.2443785,42.0174567]},"n1820938081":{"id":"n1820938081","loc":[-85.0319733,42.0953853]},"n1820938082":{"id":"n1820938082","loc":[-85.0878276,42.09443]},"n1820938083":{"id":"n1820938083","loc":[-85.0271789,42.0935809]},"n1820938084":{"id":"n1820938084","loc":[-85.0326399,42.0974222]},"n1820938085":{"id":"n1820938085","loc":[-85.3989167,42.0065592]},"n1820938086":{"id":"n1820938086","loc":[-85.3263361,41.9721261]},"n1820938087":{"id":"n1820938087","loc":[-85.2547855,42.0037134]},"n1820938088":{"id":"n1820938088","loc":[-85.4373259,42.005746]},"n1820938089":{"id":"n1820938089","loc":[-85.3094275,41.9699245]},"n1820938090":{"id":"n1820938090","loc":[-85.2783246,41.9872793]},"n1820938092":{"id":"n1820938092","loc":[-85.0815633,42.1025169]},"n1820938093":{"id":"n1820938093","loc":[-85.1788511,42.0522134]},"n1820938095":{"id":"n1820938095","loc":[-85.2830345,41.9816733]},"n1820938096":{"id":"n1820938096","loc":[-85.0744984,42.1059835]},"n1820938097":{"id":"n1820938097","loc":[-85.2788396,41.9879333]},"n1820938098":{"id":"n1820938098","loc":[-85.3640093,41.9946531]},"n1820938099":{"id":"n1820938099","loc":[-85.291167,41.9787463]},"n1820938100":{"id":"n1820938100","loc":[-85.0772436,42.1038156]},"n1820938101":{"id":"n1820938101","loc":[-85.00563,42.0887482]},"n1820938102":{"id":"n1820938102","loc":[-85.0326881,42.0961245]},"n1820938104":{"id":"n1820938104","loc":[-85.0530448,42.1038634]},"n1820938105":{"id":"n1820938105","loc":[-85.2625266,41.9970639]},"n1820938106":{"id":"n1820938106","loc":[-85.2827556,41.9823512]},"n1820938107":{"id":"n1820938107","loc":[-85.2784319,41.9910752]},"n1820938108":{"id":"n1820938108","loc":[-85.0882099,42.094393]},"n1820938109":{"id":"n1820938109","loc":[-85.5718484,41.9645371]},"n1820938110":{"id":"n1820938110","loc":[-85.2559764,42.0099317]},"n1820938111":{"id":"n1820938111","loc":[-85.2969284,41.973179]},"n1820938113":{"id":"n1820938113","loc":[-85.3875055,42.0019726]},"n1820938114":{"id":"n1820938114","loc":[-85.4250779,42.0068199]},"n1820938115":{"id":"n1820938115","loc":[-85.0645367,42.104889]},"n1820938116":{"id":"n1820938116","loc":[-85.1636762,42.0623724]},"n1820938117":{"id":"n1820938117","loc":[-85.0757322,42.1055935]},"n1820938118":{"id":"n1820938118","loc":[-85.3695197,41.9981559]},"n1820938120":{"id":"n1820938120","loc":[-85.1297516,42.0671027]},"n1820938121":{"id":"n1820938121","loc":[-85.1057448,42.0875551]},"n1820938122":{"id":"n1820938122","loc":[-85.2805175,41.9943182]},"n1820938123":{"id":"n1820938123","loc":[-85.2545173,42.0040722]},"n1820938124":{"id":"n1820938124","loc":[-84.9966607,42.0871319]},"n1820938125":{"id":"n1820938125","loc":[-85.0099899,42.0904612]},"n1820938126":{"id":"n1820938126","loc":[-85.2489919,42.0091102]},"n1820938127":{"id":"n1820938127","loc":[-85.0342706,42.0979476]},"n1820938128":{"id":"n1820938128","loc":[-85.1080891,42.0855884]},"n1820938129":{"id":"n1820938129","loc":[-85.0128183,42.0905356]},"n1820938130":{"id":"n1820938130","loc":[-85.631608,41.9434251]},"n1820938131":{"id":"n1820938131","loc":[-85.2551975,42.0008524]},"n1820938132":{"id":"n1820938132","loc":[-85.6421823,41.9096233]},"n1820938133":{"id":"n1820938133","loc":[-85.0125059,42.0906284]},"n1820938134":{"id":"n1820938134","loc":[-85.5499358,41.9701793]},"n1820938135":{"id":"n1820938135","loc":[-85.5472107,41.9712323]},"n1820938136":{"id":"n1820938136","loc":[-85.2760758,41.9958691]},"n1820938137":{"id":"n1820938137","loc":[-85.276678,41.9960433]},"n1820938138":{"id":"n1820938138","loc":[-85.0570319,42.1024731]},"n1820938140":{"id":"n1820938140","loc":[-85.2394325,42.0227492]},"n1820938142":{"id":"n1820938142","loc":[-85.5666341,41.9638829]},"n1820938144":{"id":"n1820938144","loc":[-85.258101,41.9996353]},"n1820938147":{"id":"n1820938147","loc":[-85.2129645,42.0413565]},"n1820938149":{"id":"n1820938149","loc":[-84.9962369,42.0868373]},"n1820938151":{"id":"n1820938151","loc":[-85.2570386,42.0084968]},"n1820938153":{"id":"n1820938153","loc":[-85.3971142,42.0050285]},"n1820938155":{"id":"n1820938155","loc":[-85.1072093,42.0855566]},"n1820938157":{"id":"n1820938157","loc":[-85.2840323,41.9920959]},"n1820938159":{"id":"n1820938159","loc":[-85.1187924,42.0816458]},"n1820938161":{"id":"n1820938161","loc":[-85.2681324,41.9985788]},"n1820938163":{"id":"n1820938163","loc":[-85.0887034,42.0984969]},"n1820938165":{"id":"n1820938165","loc":[-85.4133405,42.0073141]},"n1820938166":{"id":"n1820938166","loc":[-85.0097445,42.0902888]},"n1820938167":{"id":"n1820938167","loc":[-85.0828133,42.1037388]},"n1820938168":{"id":"n1820938168","loc":[-85.0549599,42.1030833]},"n1820938169":{"id":"n1820938169","loc":[-85.4571528,42.0010421]},"n1820938178":{"id":"n1820938178","loc":[-85.2706644,41.9975941]},"n1820938180":{"id":"n1820938180","loc":[-85.2258606,42.0335794]},"n1820938182":{"id":"n1820938182","loc":[-85.2832276,41.9814659]},"n1820938184":{"id":"n1820938184","loc":[-85.1082299,42.0860928]},"n1820938185":{"id":"n1820938185","loc":[-85.3839392,42.0022381]},"n1820938186":{"id":"n1820938186","loc":[-85.2772131,41.995905]},"n1820938187":{"id":"n1820938187","loc":[-85.1044895,42.0879214]},"n1820938188":{"id":"n1820938188","loc":[-85.2135267,42.0407087]},"n1820938189":{"id":"n1820938189","loc":[-85.2543993,42.0044628]},"n1820938190":{"id":"n1820938190","loc":[-85.1501793,42.0617351]},"n1820938191":{"id":"n1820938191","loc":[-85.3350587,41.9820469]},"n1820938192":{"id":"n1820938192","loc":[-85.1350731,42.0655735]},"n1820938193":{"id":"n1820938193","loc":[-85.0404008,42.1028843]},"n1820938194":{"id":"n1820938194","loc":[-85.6323161,41.943042]},"n1820938195":{"id":"n1820938195","loc":[-85.1259593,42.0742837]},"n1820938196":{"id":"n1820938196","loc":[-85.4562988,42.0033758]},"n1820938197":{"id":"n1820938197","loc":[-85.256824,42.0056826]},"n1820938198":{"id":"n1820938198","loc":[-85.2742103,41.9963862]},"n1820938199":{"id":"n1820938199","loc":[-85.0380888,42.1037877]},"n1820938200":{"id":"n1820938200","loc":[-85.47404,41.9944721]},"n1820938201":{"id":"n1820938201","loc":[-85.103021,42.087948]},"n1820938202":{"id":"n1820938202","loc":[-85.4030151,42.0065113]},"n1820938203":{"id":"n1820938203","loc":[-85.2113981,42.040735]},"n1820938204":{"id":"n1820938204","loc":[-85.2603433,41.9965137]},"n1820938206":{"id":"n1820938206","loc":[-85.1669378,42.0607634]},"n1820938207":{"id":"n1820938207","loc":[-85.0642027,42.1046076]},"n1820938208":{"id":"n1820938208","loc":[-85.2812428,41.9915696]},"n1820938209":{"id":"n1820938209","loc":[-85.0839559,42.1038343]},"n1820938210":{"id":"n1820938210","loc":[-85.1239946,42.0769368]},"n1820938211":{"id":"n1820938211","loc":[-85.2311177,42.0283042]},"n1820938212":{"id":"n1820938212","loc":[-85.2791614,41.9882682]},"n1820938213":{"id":"n1820938213","loc":[-85.2674941,41.9987582]},"n1820938214":{"id":"n1820938214","loc":[-85.352787,41.9919579]},"n1820938215":{"id":"n1820938215","loc":[-85.0874146,42.0952182]},"n1820938216":{"id":"n1820938216","loc":[-85.0069711,42.0877092]},"n1820938217":{"id":"n1820938217","loc":[-85.2059049,42.0404004]},"n1820938218":{"id":"n1820938218","loc":[-85.2403552,42.0227332]},"n1820938219":{"id":"n1820938219","loc":[-85.2492923,42.0098915]},"n1820938220":{"id":"n1820938220","loc":[-85.269778,41.9979541]},"n1820938221":{"id":"n1820938221","loc":[-85.2097673,42.0389024]},"n1820938222":{"id":"n1820938222","loc":[-85.0845942,42.1032015]},"n1820938223":{"id":"n1820938223","loc":[-84.993206,42.0858142]},"n1820938224":{"id":"n1820938224","loc":[-85.2108187,42.0402729]},"n1820938225":{"id":"n1820938225","loc":[-84.9893959,42.0873043]},"n1820938226":{"id":"n1820938226","loc":[-85.2952332,41.9719984]},"n1820938227":{"id":"n1820938227","loc":[-85.4100961,42.0081536]},"n1820938228":{"id":"n1820938228","loc":[-85.3299088,41.9785696]},"n1820938229":{"id":"n1820938229","loc":[-85.2258176,42.0340097]},"n1820938230":{"id":"n1820938230","loc":[-85.3146739,41.9711449]},"n1820938231":{"id":"n1820938231","loc":[-85.5447645,41.9712801]},"n1820938232":{"id":"n1820938232","loc":[-85.5510087,41.9705941]},"n1820938233":{"id":"n1820938233","loc":[-85.5122389,41.9703445]},"n1820938234":{"id":"n1820938234","loc":[-85.2792687,41.9865381]},"n1820938235":{"id":"n1820938235","loc":[-85.1475229,42.0630151]},"n1820938237":{"id":"n1820938237","loc":[-85.0332889,42.0996034]},"n1820938238":{"id":"n1820938238","loc":[-85.2588882,41.9986877]},"n1820938239":{"id":"n1820938239","loc":[-85.0656458,42.1050892]},"n1820938240":{"id":"n1820938240","loc":[-84.9913915,42.086098]},"n1820938241":{"id":"n1820938241","loc":[-85.4752416,41.9944402]},"n1820938242":{"id":"n1820938242","loc":[-85.1214304,42.0791147]},"n1820938243":{"id":"n1820938243","loc":[-85.0075183,42.0886925]},"n1820938244":{"id":"n1820938244","loc":[-85.1052888,42.0872087]},"n1820938245":{"id":"n1820938245","loc":[-85.3104252,41.9703393]},"n1820938246":{"id":"n1820938246","loc":[-85.232109,42.0318158]},"n1820938247":{"id":"n1820938247","loc":[-85.0756075,42.1059528]},"n1820938248":{"id":"n1820938248","loc":[-85.0075612,42.0890866]},"n1820938249":{"id":"n1820938249","loc":[-85.1013312,42.0897474]},"n1820938250":{"id":"n1820938250","loc":[-85.1168076,42.0828919]},"n1820938251":{"id":"n1820938251","loc":[-85.2951367,41.9723334]},"n1820938252":{"id":"n1820938252","loc":[-85.0879363,42.0976053]},"n1820938253":{"id":"n1820938253","loc":[-85.0354763,42.1021838]},"n1820938254":{"id":"n1820938254","loc":[-85.2379627,42.0236339]},"n1820938255":{"id":"n1820938255","loc":[-85.1308245,42.0685364]},"n1820938256":{"id":"n1820938256","loc":[-85.0914446,42.0934774]},"n1820938257":{"id":"n1820938257","loc":[-85.2436812,42.014069]},"n1820938258":{"id":"n1820938258","loc":[-85.0682529,42.1056106]},"n1820938259":{"id":"n1820938259","loc":[-85.290652,41.9766805]},"n1820938260":{"id":"n1820938260","loc":[-85.0133494,42.0897434]},"n1820938261":{"id":"n1820938261","loc":[-85.2753047,41.9949429]},"n1820938262":{"id":"n1820938262","loc":[-85.0314691,42.0950788]},"n1820938263":{"id":"n1820938263","loc":[-85.3444786,41.9908359]},"n1820938264":{"id":"n1820938264","loc":[-85.0443115,42.1009061]},"n1820938265":{"id":"n1820938265","loc":[-85.0634853,42.1043159]},"n1820938267":{"id":"n1820938267","loc":[-85.3978223,42.0053952]},"n1820938268":{"id":"n1820938268","loc":[-85.0228659,42.0911885]},"n1820938269":{"id":"n1820938269","loc":[-85.0220237,42.0906272]},"n1820938270":{"id":"n1820938270","loc":[-85.1061525,42.0863369]},"n1820938271":{"id":"n1820938271","loc":[-85.2382309,42.0233708]},"n1820938272":{"id":"n1820938272","loc":[-85.310672,41.9702755]},"n1820938273":{"id":"n1820938273","loc":[-85.1448192,42.0652613]},"n1820938274":{"id":"n1820938274","loc":[-85.6036057,41.9403766]},"n1820938275":{"id":"n1820938275","loc":[-85.0778941,42.1032413]},"n1820938276":{"id":"n1820938276","loc":[-85.1279374,42.0723974]},"n1820938277":{"id":"n1820938277","loc":[-85.2806635,41.9847836]},"n1820938278":{"id":"n1820938278","loc":[-85.2653201,41.9976352]},"n1820938279":{"id":"n1820938279","loc":[-85.0351665,42.1001805]},"n1820938280":{"id":"n1820938280","loc":[-85.0718269,42.1056253]},"n1820938281":{"id":"n1820938281","loc":[-85.2574248,42.0075322]},"n1820938282":{"id":"n1820938282","loc":[-85.126666,42.0740778]},"n1820938283":{"id":"n1820938283","loc":[-85.077705,42.1034733]},"n1820938284":{"id":"n1820938284","loc":[-85.3535552,41.9919045]},"n1820938286":{"id":"n1820938286","loc":[-85.2810711,41.9866657]},"n1820938287":{"id":"n1820938287","loc":[-85.4567494,42.0019885]},"n1820938288":{"id":"n1820938288","loc":[-85.2642419,41.9992936]},"n1820938289":{"id":"n1820938289","loc":[-85.2643344,41.9980925]},"n1820938290":{"id":"n1820938290","loc":[-85.3270335,41.9776125]},"n1820938291":{"id":"n1820938291","loc":[-85.1200584,42.0795077]},"n1820938292":{"id":"n1820938292","loc":[-85.2290792,42.0340256]},"n1820938293":{"id":"n1820938293","loc":[-85.6015887,41.9401372]},"n1820938294":{"id":"n1820938294","loc":[-85.5370869,41.970488]},"n1820938295":{"id":"n1820938295","loc":[-85.3108866,41.9698048]},"n1820938297":{"id":"n1820938297","loc":[-85.1556511,42.0628184]},"n1820938298":{"id":"n1820938298","loc":[-85.0027922,42.0875221]},"n1820938300":{"id":"n1820938300","loc":[-85.3873338,42.0040614]},"n1820938301":{"id":"n1820938301","loc":[-85.0350753,42.1004034]},"n1820938302":{"id":"n1820938302","loc":[-85.6239476,41.9411906]},"n1820938304":{"id":"n1820938304","loc":[-85.0118246,42.0897964]},"n1820938306":{"id":"n1820938306","loc":[-85.4796877,41.995275]},"n1820938307":{"id":"n1820938307","loc":[-85.5388636,41.9707856]},"n1820938309":{"id":"n1820938309","loc":[-85.2971902,41.9727773]},"n1820938310":{"id":"n1820938310","loc":[-85.5426831,41.9715513]},"n1820938311":{"id":"n1820938311","loc":[-85.2798373,41.9836671]},"n1820938312":{"id":"n1820938312","loc":[-85.2432198,42.0104017]},"n1820938313":{"id":"n1820938313","loc":[-85.2650412,41.9987554]},"n1820938317":{"id":"n1820938317","loc":[-85.0015423,42.0882386]},"n1820938318":{"id":"n1820938318","loc":[-85.1409783,42.064879]},"n1820938319":{"id":"n1820938319","loc":[-85.1691908,42.058995]},"n1820938320":{"id":"n1820938320","loc":[-85.1059165,42.0864882]},"n1820938321":{"id":"n1820938321","loc":[-85.3664941,41.9965771]},"n1820938323":{"id":"n1820938323","loc":[-85.3143198,41.9710971]},"n1820938324":{"id":"n1820938324","loc":[-85.0016067,42.0880675]},"n1820938325":{"id":"n1820938325","loc":[-85.0148139,42.0887164]},"n1820938326":{"id":"n1820938326","loc":[-85.0324682,42.0959056]},"n1820938327":{"id":"n1820938327","loc":[-85.0898661,42.0939921]},"n1820938328":{"id":"n1820938328","loc":[-85.2556427,42.0004936]},"n1820938329":{"id":"n1820938329","loc":[-85.6287112,41.9407437]},"n1820938330":{"id":"n1820938330","loc":[-84.9913392,42.0866701]},"n1820938331":{"id":"n1820938331","loc":[-85.2685777,41.9984632]},"n1820938332":{"id":"n1820938332","loc":[-85.0078884,42.0901614]},"n1820938333":{"id":"n1820938333","loc":[-84.999642,42.0878616]},"n1820938334":{"id":"n1820938334","loc":[-85.0188909,42.0899186]},"n1820938335":{"id":"n1820938335","loc":[-85.2830238,41.9819843]},"n1820938336":{"id":"n1820938336","loc":[-85.2491421,42.0096204]},"n1820938337":{"id":"n1820938337","loc":[-85.0585701,42.1034295]},"n1820938338":{"id":"n1820938338","loc":[-85.0651965,42.1051636]},"n1820938339":{"id":"n1820938339","loc":[-85.0583944,42.104292]},"n1820938340":{"id":"n1820938340","loc":[-85.119876,42.0801567]},"n1820938341":{"id":"n1820938341","loc":[-85.0943937,42.0931323]},"n1820938342":{"id":"n1820938342","loc":[-85.1504583,42.0613209]},"n1820938343":{"id":"n1820938343","loc":[-85.0425426,42.1019836]},"n1820938345":{"id":"n1820938345","loc":[-84.9991391,42.0878206]},"n1820938346":{"id":"n1820938346","loc":[-85.2563841,42.0094614]},"n1820938347":{"id":"n1820938347","loc":[-85.0515387,42.103297]},"n1820938348":{"id":"n1820938348","loc":[-85.0857261,42.1003636]},"n1820938349":{"id":"n1820938349","loc":[-85.078971,42.1029241]},"n1820938350":{"id":"n1820938350","loc":[-85.5699558,41.958931]},"n1820938351":{"id":"n1820938351","loc":[-85.3181285,41.9696533]},"n1820938352":{"id":"n1820938352","loc":[-85.5998506,41.9402329]},"n1820938353":{"id":"n1820938353","loc":[-85.2567277,42.000317]},"n1820938354":{"id":"n1820938354","loc":[-85.3082795,41.9708338]},"n1820938355":{"id":"n1820938355","loc":[-85.3127856,41.9692784]},"n1820938356":{"id":"n1820938356","loc":[-85.0340775,42.1010721]},"n1820938357":{"id":"n1820938357","loc":[-85.3158111,41.9706583]},"n1820938359":{"id":"n1820938359","loc":[-85.2312035,42.0280412]},"n1820938360":{"id":"n1820938360","loc":[-85.2448613,42.018477]},"n1820938361":{"id":"n1820938361","loc":[-85.29077,41.9759068]},"n1820938364":{"id":"n1820938364","loc":[-85.3677387,41.9976615]},"n1820938365":{"id":"n1820938365","loc":[-85.0785204,42.1030355]},"n1820938366":{"id":"n1820938366","loc":[-85.2262039,42.0333722]},"n1820938367":{"id":"n1820938367","loc":[-85.1226011,42.0780902]},"n1820938368":{"id":"n1820938368","loc":[-85.3229673,41.971129]},"n1820938369":{"id":"n1820938369","loc":[-85.385334,42.0000056]},"n1820938370":{"id":"n1820938370","loc":[-85.000098,42.0879094]},"n1820938372":{"id":"n1820938372","loc":[-85.3852481,42.0025091]},"n1820938373":{"id":"n1820938373","loc":[-85.3770513,41.9982515]},"n1820938374":{"id":"n1820938374","loc":[-85.6278314,41.9405362]},"n1820938375":{"id":"n1820938375","loc":[-85.6355133,41.9344068]},"n1820938376":{"id":"n1820938376","loc":[-85.635642,41.9324753]},"n1820938377":{"id":"n1820938377","loc":[-85.3154463,41.970778]},"n1820938378":{"id":"n1820938378","loc":[-85.0920334,42.093411]},"n1820938379":{"id":"n1820938379","loc":[-85.3269155,41.9722297]},"n1820938381":{"id":"n1820938381","loc":[-85.1134334,42.0849184]},"n1820938382":{"id":"n1820938382","loc":[-85.005968,42.088585]},"n1820938384":{"id":"n1820938384","loc":[-85.1245203,42.0757183]},"n1820938385":{"id":"n1820938385","loc":[-85.020704,42.0905396]},"n1820938386":{"id":"n1820938386","loc":[-85.119585,42.0808984]},"n1820938387":{"id":"n1820938387","loc":[-85.0072447,42.0880117]},"n1820938388":{"id":"n1820938388","loc":[-85.2742908,41.9960273]},"n1820938389":{"id":"n1820938389","loc":[-85.3275807,41.9696852]},"n1820938390":{"id":"n1820938390","loc":[-85.2385635,42.0231556]},"n1820938392":{"id":"n1820938392","loc":[-85.0202856,42.0900778]},"n1820938393":{"id":"n1820938393","loc":[-85.2067847,42.0395398]},"n1820938394":{"id":"n1820938394","loc":[-85.5183544,41.9713495]},"n1820938396":{"id":"n1820938396","loc":[-85.5073037,41.9736787]},"n1820938397":{"id":"n1820938397","loc":[-85.2519638,42.0114225]},"n1820938398":{"id":"n1820938398","loc":[-85.287487,41.9793285]},"n1820938399":{"id":"n1820938399","loc":[-85.2298088,42.0336431]},"n1820938400":{"id":"n1820938400","loc":[-85.229444,42.0339141]},"n1820938401":{"id":"n1820938401","loc":[-85.2421791,42.0220239]},"n1820938402":{"id":"n1820938402","loc":[-85.2976687,41.9737612]},"n1820938403":{"id":"n1820938403","loc":[-85.3622069,41.993473]},"n1820938404":{"id":"n1820938404","loc":[-85.2465458,42.014906]},"n1820938405":{"id":"n1820938405","loc":[-85.5724663,41.9639412]},"n1820938406":{"id":"n1820938406","loc":[-85.3708501,41.9982037]},"n1820938408":{"id":"n1820938408","loc":[-85.2564592,42.0055311]},"n1820938409":{"id":"n1820938409","loc":[-85.1192846,42.0810856]},"n1820938410":{"id":"n1820938410","loc":[-85.5623812,41.971663]},"n1820938411":{"id":"n1820938411","loc":[-85.3221948,41.9719665]},"n1820938412":{"id":"n1820938412","loc":[-85.5168738,41.9710305]},"n1820938413":{"id":"n1820938413","loc":[-85.4546852,42.0061127]},"n1820938414":{"id":"n1820938414","loc":[-85.5896153,41.9463617]},"n1820938415":{"id":"n1820938415","loc":[-85.2978189,41.9722138]},"n1820938416":{"id":"n1820938416","loc":[-85.1021681,42.0883581]},"n1820938417":{"id":"n1820938417","loc":[-85.2797193,41.9912984]},"n1820938419":{"id":"n1820938419","loc":[-85.2362461,42.0248533]},"n1820938420":{"id":"n1820938420","loc":[-85.4833639,41.9846252]},"n1820938422":{"id":"n1820938422","loc":[-85.3281064,41.9689433]},"n1820938424":{"id":"n1820938424","loc":[-85.2416963,42.0130088]},"n1820938425":{"id":"n1820938425","loc":[-85.5718655,41.9564577]},"n1820938426":{"id":"n1820938426","loc":[-85.0512812,42.1030701]},"n1820938427":{"id":"n1820938427","loc":[-85.1273527,42.0723616]},"n1820938428":{"id":"n1820938428","loc":[-85.0215033,42.0904083]},"n1820938429":{"id":"n1820938429","loc":[-85.6169953,41.942228]},"n1820938430":{"id":"n1820938430","loc":[-85.2829165,41.9907243]},"n1820938431":{"id":"n1820938431","loc":[-85.2240796,42.0374203]},"n1820938432":{"id":"n1820938432","loc":[-85.0167598,42.0898442]},"n1820938433":{"id":"n1820938433","loc":[-85.2132649,42.0411334]},"n1820938434":{"id":"n1820938434","loc":[-85.2293839,42.031513]},"n1820938435":{"id":"n1820938435","loc":[-85.1203374,42.0792608]},"n1820938436":{"id":"n1820938436","loc":[-85.109571,42.086268]},"n1820938437":{"id":"n1820938437","loc":[-85.1079026,42.0853842]},"n1820938438":{"id":"n1820938438","loc":[-85.109237,42.0862413]},"n1820938439":{"id":"n1820938439","loc":[-85.2259936,42.0350831]},"n1820938440":{"id":"n1820938440","loc":[-85.3669705,41.99679]},"n1820938441":{"id":"n1820938441","loc":[-85.2418143,42.0223507]},"n1820938442":{"id":"n1820938442","loc":[-85.3101248,41.9702515]},"n1820938443":{"id":"n1820938443","loc":[-85.069315,42.1059688]},"n1820938444":{"id":"n1820938444","loc":[-85.205862,42.0410378]},"n1820938445":{"id":"n1820938445","loc":[-85.0388076,42.1036604]},"n1820938446":{"id":"n1820938446","loc":[-85.2225389,42.0370115]},"n1820938447":{"id":"n1820938447","loc":[-85.3241474,41.9719346]},"n1820938448":{"id":"n1820938448","loc":[-85.3125496,41.9690789]},"n1820938449":{"id":"n1820938449","loc":[-85.1146497,42.0857039]},"n1820938450":{"id":"n1820938450","loc":[-85.1333944,42.0714963]},"n1820938451":{"id":"n1820938451","loc":[-85.5619306,41.9720937]},"n1820938452":{"id":"n1820938452","loc":[-85.2553651,42.0006479]},"n1820938453":{"id":"n1820938453","loc":[-85.3151137,41.9710093]},"n1820938454":{"id":"n1820938454","loc":[-85.2592315,41.9977947]},"n1820938455":{"id":"n1820938455","loc":[-85.2655723,41.9995966]},"n1820938456":{"id":"n1820938456","loc":[-85.4820652,41.9959233]},"n1820938459":{"id":"n1820938459","loc":[-85.450737,42.0055068]},"n1820938460":{"id":"n1820938460","loc":[-85.2428658,42.0205573]},"n1820938461":{"id":"n1820938461","loc":[-85.0835576,42.1021559]},"n1820938462":{"id":"n1820938462","loc":[-85.244636,42.0194733]},"n1820938463":{"id":"n1820938463","loc":[-85.5702562,41.9581332]},"n1820938465":{"id":"n1820938465","loc":[-85.5680031,41.9659515]},"n1820938467":{"id":"n1820938467","loc":[-85.2798752,41.9948353]},"n1820938468":{"id":"n1820938468","loc":[-85.0477407,42.1015537]},"n1820938469":{"id":"n1820938469","loc":[-85.6403842,41.913732]},"n1820938470":{"id":"n1820938470","loc":[-85.0396029,42.103289]},"n1820938471":{"id":"n1820938471","loc":[-85.2824702,41.9907777]},"n1820938472":{"id":"n1820938472","loc":[-85.2969284,41.9735538]},"n1820938474":{"id":"n1820938474","loc":[-85.401041,42.0070853]},"n1820938475":{"id":"n1820938475","loc":[-85.4116625,42.0073883]},"n1820938476":{"id":"n1820938476","loc":[-85.0437764,42.1016214]},"n1820938477":{"id":"n1820938477","loc":[-85.3643269,41.9958436]},"n1820938478":{"id":"n1820938478","loc":[-85.3895182,42.0009465]},"n1820938479":{"id":"n1820938479","loc":[-85.636157,41.9333373]},"n1820938480":{"id":"n1820938480","loc":[-85.2811355,41.9858044]},"n1820938481":{"id":"n1820938481","loc":[-85.0239052,42.092153]},"n1820938482":{"id":"n1820938482","loc":[-85.2558798,42.0053557]},"n1820938483":{"id":"n1820938483","loc":[-85.2544422,42.0047339]},"n1820938484":{"id":"n1820938484","loc":[-85.4864683,41.9843183]},"n1820938485":{"id":"n1820938485","loc":[-85.2554185,42.0031075]},"n1820938486":{"id":"n1820938486","loc":[-85.3082795,41.9712486]},"n1820938487":{"id":"n1820938487","loc":[-85.2433378,42.0133436]},"n1820938488":{"id":"n1820938488","loc":[-85.0216696,42.0904162]},"n1820938489":{"id":"n1820938489","loc":[-85.2546138,42.0050289]},"n1820938490":{"id":"n1820938490","loc":[-85.2717521,41.9977349]},"n1820938491":{"id":"n1820938491","loc":[-85.0100489,42.0908195]},"n1820938492":{"id":"n1820938492","loc":[-85.207879,42.0392211]},"n1820938493":{"id":"n1820938493","loc":[-85.0007363,42.0882836]},"n1820938494":{"id":"n1820938494","loc":[-85.5775303,41.9504097]},"n1820938495":{"id":"n1820938495","loc":[-85.1131584,42.0847683]},"n1820938496":{"id":"n1820938496","loc":[-85.0887825,42.0941633]},"n1820938497":{"id":"n1820938497","loc":[-85.1185926,42.0818938]},"n1820938498":{"id":"n1820938498","loc":[-85.2748487,41.9948712]},"n1820938499":{"id":"n1820938499","loc":[-85.2566952,42.0090788]},"n1820938500":{"id":"n1820938500","loc":[-85.0774757,42.1036234]},"n1820938501":{"id":"n1820938501","loc":[-85.4190869,42.008903]},"n1820938502":{"id":"n1820938502","loc":[-85.1140395,42.0850577]},"n1820938503":{"id":"n1820938503","loc":[-85.1136104,42.0848627]},"n1820938504":{"id":"n1820938504","loc":[-85.5828089,41.9480638]},"n1820938505":{"id":"n1820938505","loc":[-85.625514,41.9405202]},"n1820938506":{"id":"n1820938506","loc":[-85.2063384,42.0398322]},"n1820938507":{"id":"n1820938507","loc":[-85.3395476,41.9851636]},"n1820938508":{"id":"n1820938508","loc":[-85.0328853,42.0963606]},"n1820938510":{"id":"n1820938510","loc":[-85.1170369,42.0843702]},"n1820938511":{"id":"n1820938511","loc":[-85.2784748,41.9868487]},"n1820938512":{"id":"n1820938512","loc":[-85.6310501,41.9435528]},"n1820938514":{"id":"n1820938514","loc":[-85.0334284,42.0981028]},"n1820938515":{"id":"n1820938515","loc":[-84.9912091,42.0868226]},"n1820938516":{"id":"n1820938516","loc":[-85.2806141,41.9940351]},"n1820938517":{"id":"n1820938517","loc":[-85.1233025,42.0776734]},"n1820938518":{"id":"n1820938518","loc":[-85.2047891,42.0429023]},"n1820938519":{"id":"n1820938519","loc":[-85.1475443,42.0648312]},"n1820938520":{"id":"n1820938520","loc":[-85.2644685,41.9990891]},"n1820938521":{"id":"n1820938521","loc":[-85.1056281,42.0872553]},"n1820938522":{"id":"n1820938522","loc":[-85.4813184,41.9930105]},"n1820938523":{"id":"n1820938523","loc":[-85.321551,41.9722936]},"n1820938524":{"id":"n1820938524","loc":[-85.1564664,42.0631211]},"n1820938525":{"id":"n1820938525","loc":[-85.4149885,42.0079144]},"n1820938527":{"id":"n1820938527","loc":[-85.2861888,41.9803653]},"n1820938528":{"id":"n1820938528","loc":[-85.1301379,42.0682178]},"n1820938529":{"id":"n1820938529","loc":[-85.4156537,42.0084247]},"n1820938530":{"id":"n1820938530","loc":[-85.245151,42.0176082]},"n1820938531":{"id":"n1820938531","loc":[-85.457818,42.0001651]},"n1820938532":{"id":"n1820938532","loc":[-85.310951,41.9694538]},"n1820938533":{"id":"n1820938533","loc":[-85.1509089,42.0611298]},"n1820938534":{"id":"n1820938534","loc":[-85.1108249,42.086321]},"n1820938535":{"id":"n1820938535","loc":[-85.1260344,42.0740687]},"n1820938536":{"id":"n1820938536","loc":[-85.4561228,42.0042791]},"n1820938537":{"id":"n1820938537","loc":[-85.2805082,41.9945761]},"n1820938538":{"id":"n1820938538","loc":[-85.273352,41.9981921]},"n1820938539":{"id":"n1820938539","loc":[-85.1084216,42.0864364]},"n1820938540":{"id":"n1820938540","loc":[-85.5009737,41.9773637]},"n1820938541":{"id":"n1820938541","loc":[-85.3960843,42.0051879]},"n1820938542":{"id":"n1820938542","loc":[-85.3425088,41.9865034]},"n1820938545":{"id":"n1820938545","loc":[-84.9937907,42.0860849]},"n1820938546":{"id":"n1820938546","loc":[-85.1084176,42.086065]},"n1820938547":{"id":"n1820938547","loc":[-85.3492851,41.9924786]},"n1820938548":{"id":"n1820938548","loc":[-85.2512235,42.0101147]},"n1820938549":{"id":"n1820938549","loc":[-85.3717298,41.9979326]},"n1820938551":{"id":"n1820938551","loc":[-85.2573712,42.0064081]},"n1820938552":{"id":"n1820938552","loc":[-85.2514596,42.010139]},"n1820938553":{"id":"n1820938553","loc":[-85.416512,42.0088073]},"n1820938554":{"id":"n1820938554","loc":[-85.4365964,42.0061606]},"n1820938555":{"id":"n1820938555","loc":[-85.4552431,42.0057301]},"n1820938556":{"id":"n1820938556","loc":[-85.2916283,41.9778769]},"n1820938557":{"id":"n1820938557","loc":[-85.100709,42.0902968]},"n1820938558":{"id":"n1820938558","loc":[-85.4703064,41.9965771]},"n1820938559":{"id":"n1820938559","loc":[-85.3134722,41.9696134]},"n1820938560":{"id":"n1820938560","loc":[-85.4834213,41.9885768]},"n1820938561":{"id":"n1820938561","loc":[-85.2740641,41.9975236]},"n1820938562":{"id":"n1820938562","loc":[-85.148334,42.0623405]},"n1820938563":{"id":"n1820938563","loc":[-85.2358598,42.0263675]},"n1820938565":{"id":"n1820938565","loc":[-85.2902979,41.9790892]},"n1820938566":{"id":"n1820938566","loc":[-85.2528865,42.0112869]},"n1820938567":{"id":"n1820938567","loc":[-85.2595319,41.9973003]},"n1820938568":{"id":"n1820938568","loc":[-85.071151,42.105689]},"n1820938570":{"id":"n1820938570","loc":[-85.299278,41.9732188]},"n1820938571":{"id":"n1820938571","loc":[-85.0354669,42.1024771]},"n1820938583":{"id":"n1820938583","loc":[-85.3313937,41.972562]},"n1820938585":{"id":"n1820938585","loc":[-85.0756933,42.1058334]},"n1820938587":{"id":"n1820938587","loc":[-85.3130324,41.9694219]},"n1820938590":{"id":"n1820938590","loc":[-85.0934227,42.0931681]},"n1820938592":{"id":"n1820938592","loc":[-85.3517956,41.9922553]},"n1820938593":{"id":"n1820938593","loc":[-85.4023971,42.0065169]},"n1820938594":{"id":"n1820938594","loc":[-85.3506798,41.9925583]},"n1820938595":{"id":"n1820938595","loc":[-85.3673524,41.9971193]},"n1820938596":{"id":"n1820938596","loc":[-85.1073608,42.0853523]},"n1820938597":{"id":"n1820938597","loc":[-85.2976579,41.972477]},"n1820938598":{"id":"n1820938598","loc":[-85.5616517,41.9694295]},"n1820938599":{"id":"n1820938599","loc":[-85.3552074,41.9921915]},"n1820938600":{"id":"n1820938600","loc":[-85.4665126,41.9999953]},"n1820938601":{"id":"n1820938601","loc":[-85.2740695,41.9966226]},"n1820938602":{"id":"n1820938602","loc":[-85.279376,41.9886669]},"n1820938603":{"id":"n1820938603","loc":[-85.0771109,42.1040413]},"n1820938604":{"id":"n1820938604","loc":[-85.2636049,41.9977895]},"n1820938605":{"id":"n1820938605","loc":[-85.3762145,41.9976456]},"n1820938606":{"id":"n1820938606","loc":[-85.2321369,42.0289577]},"n1820938620":{"id":"n1820938620","loc":[-85.4947724,41.9776189]},"n1820938622":{"id":"n1820938622","loc":[-85.1547069,42.0622768]},"n1820938624":{"id":"n1820938624","loc":[-85.0005056,42.0880249]},"n1820938626":{"id":"n1820938626","loc":[-85.0735596,42.1059357]},"n1820938628":{"id":"n1820938628","loc":[-85.4665298,41.99932]},"n1820938629":{"id":"n1820938629","loc":[-85.434515,42.0065273]},"n1820938630":{"id":"n1820938630","loc":[-85.117462,42.0823823]},"n1820938631":{"id":"n1820938631","loc":[-85.0131777,42.0890707]},"n1820938632":{"id":"n1820938632","loc":[-85.0875326,42.0961934]},"n1820938634":{"id":"n1820938634","loc":[-85.6433839,41.9112042]},"n1820938635":{"id":"n1820938635","loc":[-85.1366181,42.064969]},"n1820938636":{"id":"n1820938636","loc":[-85.073109,42.1057925]},"n1820938638":{"id":"n1820938638","loc":[-85.161406,42.0632541]},"n1820938640":{"id":"n1820938640","loc":[-85.6343932,41.9188845]},"n1820938642":{"id":"n1820938642","loc":[-85.2500004,42.010306]},"n1820938644":{"id":"n1820938644","loc":[-85.291918,41.9753166]},"n1820938663":{"id":"n1820938663","loc":[-85.2841611,41.9916812]},"n1820938664":{"id":"n1820938664","loc":[-85.1052955,42.0868134]},"n1820938665":{"id":"n1820938665","loc":[-85.4606118,42.0005534]},"n1820938666":{"id":"n1820938666","loc":[-85.5672736,41.9642922]},"n1820938667":{"id":"n1820938667","loc":[-85.6348481,41.9316932]},"n1820938668":{"id":"n1820938668","loc":[-85.0224904,42.0909576]},"n1820938669":{"id":"n1820938669","loc":[-85.0133856,42.0899755]},"n1820938670":{"id":"n1820938670","loc":[-85.344779,41.991139]},"n1820938671":{"id":"n1820938671","loc":[-85.632874,41.9425313]},"n1820938673":{"id":"n1820938673","loc":[-85.4941501,41.9779698]},"n1820938675":{"id":"n1820938675","loc":[-85.0862559,42.0997519]},"n1820938676":{"id":"n1820938676","loc":[-85.0097874,42.0898032]},"n1820938678":{"id":"n1820938678","loc":[-84.9913553,42.0863675]},"n1820938680":{"id":"n1820938680","loc":[-85.0533666,42.1038315]},"n1820938682":{"id":"n1820938682","loc":[-85.2950294,41.9743914]},"n1820938684":{"id":"n1820938684","loc":[-85.2517385,42.0104499]},"n1820938686":{"id":"n1820938686","loc":[-85.0247971,42.0922514]},"n1820938688":{"id":"n1820938688","loc":[-85.0807037,42.1026017]},"n1820938690":{"id":"n1820938690","loc":[-85.52462,41.9722748]},"n1820938694":{"id":"n1820938694","loc":[-85.2586535,41.9988818]},"n1820938695":{"id":"n1820938695","loc":[-85.0931612,42.092948]},"n1820938697":{"id":"n1820938697","loc":[-85.2470822,42.016564]},"n1820938698":{"id":"n1820938698","loc":[-85.4143018,42.0075158]},"n1820938699":{"id":"n1820938699","loc":[-85.0771484,42.104487]},"n1820938700":{"id":"n1820938700","loc":[-85.0291208,42.0942775]},"n1820938701":{"id":"n1820938701","loc":[-85.6367964,41.9185971]},"n1820938702":{"id":"n1820938702","loc":[-85.085419,42.1010693]},"n1820938703":{"id":"n1820938703","loc":[-85.0583877,42.1040584]},"n1820938705":{"id":"n1820938705","loc":[-85.2573379,42.0003182]},"n1820938706":{"id":"n1820938706","loc":[-85.2655937,41.9981575]},"n1820938707":{"id":"n1820938707","loc":[-85.023181,42.0915758]},"n1820938708":{"id":"n1820938708","loc":[-85.2318687,42.0274674]},"n1820938709":{"id":"n1820938709","loc":[-85.1056389,42.0866184]},"n1820938710":{"id":"n1820938710","loc":[-85.5276265,41.9700978]},"n1820938711":{"id":"n1820938711","loc":[-85.0864128,42.0945761]},"n1820938712":{"id":"n1820938712","loc":[-84.9897071,42.0871888]},"n1820938714":{"id":"n1820938714","loc":[-85.1328845,42.0665611]},"n1820938715":{"id":"n1820938715","loc":[-85.0336537,42.0991377]},"n1820938716":{"id":"n1820938716","loc":[-85.087597,42.0986692]},"n1820938717":{"id":"n1820938717","loc":[-85.1241394,42.0761882]},"n1820938718":{"id":"n1820938718","loc":[-85.1176002,42.0847723]},"n1820938719":{"id":"n1820938719","loc":[-85.2423615,42.0216572]},"n1820938721":{"id":"n1820938721","loc":[-85.2196378,42.0387908]},"n1820938722":{"id":"n1820938722","loc":[-85.0164272,42.0890082]},"n1820938723":{"id":"n1820938723","loc":[-85.5917182,41.9451807]},"n1820938724":{"id":"n1820938724","loc":[-85.2458806,42.0086638]},"n1820938725":{"id":"n1820938725","loc":[-85.1264474,42.0740527]},"n1820938726":{"id":"n1820938726","loc":[-85.1961631,42.04738]},"n1820938727":{"id":"n1820938727","loc":[-85.2784643,41.9943648]},"n1820938728":{"id":"n1820938728","loc":[-85.2905554,41.9763216]},"n1820938729":{"id":"n1820938729","loc":[-85.2913386,41.9771511]},"n1820938730":{"id":"n1820938730","loc":[-85.0112519,42.0895683]},"n1820938732":{"id":"n1820938732","loc":[-85.4290261,42.0064531]},"n1820938733":{"id":"n1820938733","loc":[-85.3867073,42.0031629]},"n1820938734":{"id":"n1820938734","loc":[-85.4943647,41.9836005]},"n1820938735":{"id":"n1820938735","loc":[-85.4900303,41.9860728]},"n1820938736":{"id":"n1820938736","loc":[-85.0866153,42.0944539]},"n1820938737":{"id":"n1820938737","loc":[-85.0869532,42.0990911]},"n1820938738":{"id":"n1820938738","loc":[-85.6321659,41.9208851]},"n1820938739":{"id":"n1820938739","loc":[-85.5930485,41.9433453]},"n1820938740":{"id":"n1820938740","loc":[-85.0406851,42.102733]},"n1820938741":{"id":"n1820938741","loc":[-85.1051131,42.0869846]},"n1820938742":{"id":"n1820938742","loc":[-85.1377554,42.0648893]},"n1820938743":{"id":"n1820938743","loc":[-85.2795694,41.994604]},"n1820938745":{"id":"n1820938745","loc":[-85.4948153,41.9826594]},"n1820938746":{"id":"n1820938746","loc":[-85.4488916,42.0050923]},"n1820938747":{"id":"n1820938747","loc":[-85.1052526,42.0866144]},"n1820938748":{"id":"n1820938748","loc":[-85.1468749,42.0653991]},"n1820938749":{"id":"n1820938749","loc":[-85.0856886,42.1006104]},"n1820938750":{"id":"n1820938750","loc":[-85.2144022,42.0404004]},"n1820938751":{"id":"n1820938751","loc":[-85.277771,41.9907458]},"n1820938752":{"id":"n1820938752","loc":[-85.1474542,42.0636149]},"n1820938753":{"id":"n1820938753","loc":[-85.0820515,42.1028075]},"n1820938754":{"id":"n1820938754","loc":[-85.1122948,42.08525]},"n1820938756":{"id":"n1820938756","loc":[-85.0173352,42.0901933]},"n1820938757":{"id":"n1820938757","loc":[-85.2259721,42.0354018]},"n1820938758":{"id":"n1820938758","loc":[-85.0872389,42.0987795]},"n1820938759":{"id":"n1820938759","loc":[-85.2291436,42.031874]},"n1820938760":{"id":"n1820938760","loc":[-85.3802485,42.0016002]},"n1820938761":{"id":"n1820938761","loc":[-85.3945822,42.0057938]},"n1820938762":{"id":"n1820938762","loc":[-85.5273237,41.9713017]},"n1820938763":{"id":"n1820938763","loc":[-85.2868862,41.9798629]},"n1820938764":{"id":"n1820938764","loc":[-85.2516677,42.0107899]},"n1820938766":{"id":"n1820938766","loc":[-85.3183002,41.9693103]},"n1820938768":{"id":"n1820938768","loc":[-85.2159042,42.0401932]},"n1820938770":{"id":"n1820938770","loc":[-85.0094481,42.0911141]},"n1820938771":{"id":"n1820938771","loc":[-85.0244538,42.0922155]},"n1820938772":{"id":"n1820938772","loc":[-85.231697,42.028862]},"n1820938773":{"id":"n1820938773","loc":[-85.2102394,42.0390617]},"n1820938774":{"id":"n1820938774","loc":[-85.2463419,42.0151212]},"n1820938775":{"id":"n1820938775","loc":[-85.0726195,42.1056424]},"n1820938776":{"id":"n1820938776","loc":[-85.0060431,42.0883262]},"n1820938778":{"id":"n1820938778","loc":[-85.425889,42.0056982]},"n1820938779":{"id":"n1820938779","loc":[-85.1183042,42.0820638]},"n1820938780":{"id":"n1820938780","loc":[-85.441596,42.0058257]},"n1820938781":{"id":"n1820938781","loc":[-85.1124879,42.0847086]},"n1820938782":{"id":"n1820938782","loc":[-85.2452733,42.0153894]},"n1820938783":{"id":"n1820938783","loc":[-85.2741191,41.9969244]},"n1820938784":{"id":"n1820938784","loc":[-85.2829487,41.9822236]},"n1820938785":{"id":"n1820938785","loc":[-85.3202743,41.972142]},"n1820938786":{"id":"n1820938786","loc":[-85.2345402,42.0266465]},"n1820938787":{"id":"n1820938787","loc":[-85.3037626,41.9724611]},"n1820938789":{"id":"n1820938789","loc":[-85.2474792,42.0161973]},"n1820938790":{"id":"n1820938790","loc":[-85.2951045,41.9727323]},"n1820938791":{"id":"n1820938791","loc":[-85.322345,41.9712726]},"n1820938792":{"id":"n1820938792","loc":[-85.2402372,42.0110394]},"n1820938793":{"id":"n1820938793","loc":[-85.5135693,41.9698659]},"n1820938794":{"id":"n1820938794","loc":[-85.4695339,41.9967366]},"n1820938796":{"id":"n1820938796","loc":[-85.0418492,42.1011131]},"n1820938797":{"id":"n1820938797","loc":[-85.3334107,41.9806337]},"n1820938798":{"id":"n1820938798","loc":[-85.5625314,41.9711685]},"n1820938799":{"id":"n1820938799","loc":[-85.3755707,41.9973585]},"n1820938800":{"id":"n1820938800","loc":[-85.5227532,41.9722429]},"n1820938801":{"id":"n1820938801","loc":[-85.4267687,42.0052836]},"n1820938803":{"id":"n1820938803","loc":[-85.0284704,42.0940837]},"n1820938804":{"id":"n1820938804","loc":[-85.015585,42.0885305]},"n1820938805":{"id":"n1820938805","loc":[-85.0765905,42.1053865]},"n1820938806":{"id":"n1820938806","loc":[-85.2614953,41.9964551]},"n1820938808":{"id":"n1820938808","loc":[-85.0307355,42.0947313]},"n1820938810":{"id":"n1820938810","loc":[-85.3894753,42.0003565]},"n1820938812":{"id":"n1820938812","loc":[-85.0868848,42.095006]},"n1820938813":{"id":"n1820938813","loc":[-85.3854198,42.0009465]},"n1820938814":{"id":"n1820938814","loc":[-85.2659692,41.9993534]},"n1820938815":{"id":"n1820938815","loc":[-85.1234259,42.0765266]},"n1820938816":{"id":"n1820938816","loc":[-85.1426906,42.0648893]},"n1820938818":{"id":"n1820938818","loc":[-85.1014533,42.0893067]},"n1820938819":{"id":"n1820938819","loc":[-85.0883064,42.098067]},"n1820938820":{"id":"n1820938820","loc":[-85.0503156,42.102704]},"n1820938821":{"id":"n1820938821","loc":[-85.1179649,42.0821884]},"n1820938822":{"id":"n1820938822","loc":[-85.3484697,41.9921596]},"n1820938823":{"id":"n1820938823","loc":[-85.3732962,41.9970874]},"n1820938824":{"id":"n1820938824","loc":[-85.2784104,41.9898312]},"n1820938825":{"id":"n1820938825","loc":[-85.4441709,42.0052198]},"n1820938826":{"id":"n1820938826","loc":[-85.3925438,42.0038326]},"n1820938829":{"id":"n1820938829","loc":[-85.5717582,41.9621861]},"n1820938830":{"id":"n1820938830","loc":[-85.0866314,42.0995051]},"n1820938831":{"id":"n1820938831","loc":[-85.576672,41.9522769]},"n1820938832":{"id":"n1820938832","loc":[-85.1587238,42.0636205]},"n1820938833":{"id":"n1820938833","loc":[-85.3804245,41.9999155]},"n1820938834":{"id":"n1820938834","loc":[-85.280083,41.9948843]},"n1820938836":{"id":"n1820938836","loc":[-85.561892,41.9686693]},"n1820938837":{"id":"n1820938837","loc":[-85.0158975,42.0885253]},"n1820938838":{"id":"n1820938838","loc":[-85.4248204,42.007633]},"n1820938839":{"id":"n1820938839","loc":[-85.0352738,42.1039657]},"n1820938840":{"id":"n1820938840","loc":[-85.211956,42.0411812]},"n1820938841":{"id":"n1820938841","loc":[-85.4816575,41.9908997]},"n1820938842":{"id":"n1820938842","loc":[-85.3807635,42.0020308]},"n1820938843":{"id":"n1820938843","loc":[-85.0100865,42.0898521]},"n1820938844":{"id":"n1820938844","loc":[-85.0103936,42.0897434]},"n1820938848":{"id":"n1820938848","loc":[-85.2430052,42.0131363]},"n1820938849":{"id":"n1820938849","loc":[-85.112559,42.0853723]},"n1820938851":{"id":"n1820938851","loc":[-85.3641553,41.9952535]},"n1820938852":{"id":"n1820938852","loc":[-85.2087373,42.0390777]},"n1820938853":{"id":"n1820938853","loc":[-85.2473933,42.0148263]},"n1820938854":{"id":"n1820938854","loc":[-85.0213464,42.090509]},"n1820938855":{"id":"n1820938855","loc":[-85.0673208,42.1052353]},"n1820938856":{"id":"n1820938856","loc":[-85.1003053,42.0905528]},"n1820938857":{"id":"n1820938857","loc":[-85.2617367,41.9965389]},"n1820938858":{"id":"n1820938858","loc":[-85.280363,41.9916015]},"n1820938859":{"id":"n1820938859","loc":[-85.0038866,42.0873469]},"n1820938860":{"id":"n1820938860","loc":[-85.2476401,42.0151451]},"n1820938861":{"id":"n1820938861","loc":[-85.193717,42.0499294]},"n1820938862":{"id":"n1820938862","loc":[-85.3478689,41.9917609]},"n1820938863":{"id":"n1820938863","loc":[-85.5638017,41.9648881]},"n1820938864":{"id":"n1820938864","loc":[-85.4356308,42.0064476]},"n1820938865":{"id":"n1820938865","loc":[-85.0561722,42.1023509]},"n1820938867":{"id":"n1820938867","loc":[-85.2256031,42.0356034]},"n1820938868":{"id":"n1820938868","loc":[-85.6102576,41.9420844]},"n1820938869":{"id":"n1820938869","loc":[-85.2285213,42.0339938]},"n1820938870":{"id":"n1820938870","loc":[-85.0326238,42.0978003]},"n1820938871":{"id":"n1820938871","loc":[-85.0131389,42.0903736]},"n1820938872":{"id":"n1820938872","loc":[-85.2550859,42.0012259]},"n1820938873":{"id":"n1820938873","loc":[-85.1130029,42.0846966]},"n1820938874":{"id":"n1820938874","loc":[-85.1579041,42.06336]},"n1820938875":{"id":"n1820938875","loc":[-85.0430522,42.1020234]},"n1820938876":{"id":"n1820938876","loc":[-85.2786679,41.9865935]},"n1820938877":{"id":"n1820938877","loc":[-85.1221666,42.0788706]},"n1820938878":{"id":"n1820938878","loc":[-85.2554614,42.0103303]},"n1820938879":{"id":"n1820938879","loc":[-85.2349801,42.0265748]},"n1820938880":{"id":"n1820938880","loc":[-85.0997434,42.0907864]},"n1820938881":{"id":"n1820938881","loc":[-85.0045464,42.0878167]},"n1820938882":{"id":"n1820938882","loc":[-85.2728048,41.9982519]},"n1820938883":{"id":"n1820938883","loc":[-85.3111333,41.9691587]},"n1820938884":{"id":"n1820938884","loc":[-85.3219802,41.9721899]},"n1820938885":{"id":"n1820938885","loc":[-85.3091378,41.9699325]},"n1820938887":{"id":"n1820938887","loc":[-85.4242367,42.0085203]},"n1820938888":{"id":"n1820938888","loc":[-84.9968377,42.0874504]},"n1820938890":{"id":"n1820938890","loc":[-85.5443139,41.9714078]},"n1820938891":{"id":"n1820938891","loc":[-85.6404013,41.9154676]},"n1820938892":{"id":"n1820938892","loc":[-85.3644986,41.9962582]},"n1820938893":{"id":"n1820938893","loc":[-85.0496772,42.1018323]},"n1820938894":{"id":"n1820938894","loc":[-85.297261,41.9737373]},"n1820938895":{"id":"n1820938895","loc":[-85.0327096,42.098071]},"n1820938896":{"id":"n1820938896","loc":[-85.3856773,41.9996867]},"n1820938897":{"id":"n1820938897","loc":[-85.0493862,42.1015509]},"n1820938898":{"id":"n1820938898","loc":[-84.9969879,42.0876614]},"n1820938899":{"id":"n1820938899","loc":[-85.0848625,42.1013587]},"n1820938900":{"id":"n1820938900","loc":[-85.5853195,41.9479201]},"n1820938901":{"id":"n1820938901","loc":[-85.6329169,41.9387964]},"n1820938902":{"id":"n1820938902","loc":[-85.0843046,42.1029468]},"n1820938903":{"id":"n1820938903","loc":[-85.1228747,42.0778474]},"n1820938904":{"id":"n1820938904","loc":[-85.4855456,41.984095]},"n1820938905":{"id":"n1820938905","loc":[-85.0573269,42.1026801]},"n1820938906":{"id":"n1820938906","loc":[-85.2425868,42.0131523]},"n1820938907":{"id":"n1820938907","loc":[-85.1149622,42.0860053]},"n1820938908":{"id":"n1820938908","loc":[-85.4833097,41.9951578]},"n1820938909":{"id":"n1820938909","loc":[-85.075979,42.1056372]},"n1820938910":{"id":"n1820938910","loc":[-85.0338509,42.0977139]},"n1820938911":{"id":"n1820938911","loc":[-85.6384272,41.9115715]},"n1820938912":{"id":"n1820938912","loc":[-85.0458363,42.1004074]},"n1820938913":{"id":"n1820938913","loc":[-85.0592138,42.1048305]},"n1820938914":{"id":"n1820938914","loc":[-85.2807493,41.9916653]},"n1820938915":{"id":"n1820938915","loc":[-85.1103274,42.0864193]},"n1820938916":{"id":"n1820938916","loc":[-85.6267156,41.9404404]},"n1820938918":{"id":"n1820938918","loc":[-85.0331374,42.0982911]},"n1820938919":{"id":"n1820938919","loc":[-85.5637331,41.965409]},"n1820938920":{"id":"n1820938920","loc":[-85.5457515,41.9714237]},"n1820938922":{"id":"n1820938922","loc":[-85.082073,42.1030104]},"n1820938923":{"id":"n1820938923","loc":[-85.0780765,42.103102]},"n1820938924":{"id":"n1820938924","loc":[-85.4208035,42.0089508]},"n1820938925":{"id":"n1820938925","loc":[-85.3469934,41.9914795]},"n1820938926":{"id":"n1820938926","loc":[-85.0322,42.095619]},"n1820938927":{"id":"n1820938927","loc":[-85.4784431,41.9949401]},"n1820938928":{"id":"n1820938928","loc":[-85.1303095,42.0667523]},"n1820938929":{"id":"n1820938929","loc":[-85.2463784,42.0084781]},"n1820938930":{"id":"n1820938930","loc":[-85.6299986,41.9427707]},"n1820938931":{"id":"n1820938931","loc":[-85.6325907,41.9238499]},"n1820938932":{"id":"n1820938932","loc":[-85.4808464,41.9914476]},"n1820938934":{"id":"n1820938934","loc":[-85.2411599,42.0105292]},"n1820938935":{"id":"n1820938935","loc":[-85.0163213,42.0892379]},"n1820938936":{"id":"n1820938936","loc":[-85.3290934,41.9682322]},"n1820938937":{"id":"n1820938937","loc":[-85.4925623,41.9853231]},"n1820938938":{"id":"n1820938938","loc":[-85.0338294,42.09892]},"n1820938940":{"id":"n1820938940","loc":[-85.4174561,42.008903]},"n1820938941":{"id":"n1820938941","loc":[-85.1165595,42.0838845]},"n1820938942":{"id":"n1820938942","loc":[-85.2954585,41.9717192]},"n1820938943":{"id":"n1820938943","loc":[-85.6330199,41.9257338]},"n1820938944":{"id":"n1820938944","loc":[-85.2294654,42.0324478]},"n1820938945":{"id":"n1820938945","loc":[-85.5601282,41.9728914]},"n1820938946":{"id":"n1820938946","loc":[-85.1176324,42.08568]},"n1820938947":{"id":"n1820938947","loc":[-85.0210245,42.0906005]},"n1820938948":{"id":"n1820938948","loc":[-85.0251887,42.09253]},"n1820938949":{"id":"n1820938949","loc":[-85.0895832,42.0939551]},"n1820938950":{"id":"n1820938950","loc":[-84.9915109,42.085842]},"n1820938951":{"id":"n1820938951","loc":[-85.2187366,42.0393486]},"n1820938952":{"id":"n1820938952","loc":[-85.006605,42.087579]},"n1820938953":{"id":"n1820938953","loc":[-85.046641,42.1012393]},"n1820938954":{"id":"n1820938954","loc":[-85.052102,42.103695]},"n1820938955":{"id":"n1820938955","loc":[-85.283925,41.9912825]},"n1820938956":{"id":"n1820938956","loc":[-85.2326626,42.0316349]},"n1820938957":{"id":"n1820938957","loc":[-85.1174298,42.0859694]},"n1820938958":{"id":"n1820938958","loc":[-85.3802056,41.9994794]},"n1820938959":{"id":"n1820938959","loc":[-85.4586334,41.9999737]},"n1820938960":{"id":"n1820938960","loc":[-85.4302234,42.0069418]},"n1820938961":{"id":"n1820938961","loc":[-85.092201,42.0930674]},"n1820938962":{"id":"n1820938962","loc":[-85.3684511,41.9979382]},"n1820938963":{"id":"n1820938963","loc":[-85.4618735,42.0011856]},"n1820938964":{"id":"n1820938964","loc":[-85.4828205,41.9877793]},"n1820938965":{"id":"n1820938965","loc":[-85.0837789,42.1025726]},"n1820938966":{"id":"n1820938966","loc":[-85.0176195,42.090253]},"n1820938967":{"id":"n1820938967","loc":[-85.3801627,42.001074]},"n1820938968":{"id":"n1820938968","loc":[-85.4767007,41.994488]},"n1820938969":{"id":"n1820938969","loc":[-85.274268,41.9957495]},"n1820938970":{"id":"n1820938970","loc":[-85.2977438,41.9719506]},"n1820938971":{"id":"n1820938971","loc":[-85.2425546,42.0208682]},"n1820938972":{"id":"n1820938972","loc":[-85.2557082,42.002382]},"n1820938973":{"id":"n1820938973","loc":[-85.3187937,41.9691986]},"n1820938975":{"id":"n1820938975","loc":[-85.2448077,42.0153045]},"n1820938977":{"id":"n1820938977","loc":[-85.0343015,42.0997718]},"n1820938978":{"id":"n1820938978","loc":[-85.2449364,42.01874]},"n1820938979":{"id":"n1820938979","loc":[-85.2598391,41.9969602]},"n1820938980":{"id":"n1820938980","loc":[-85.4294724,42.0067665]},"n1820938981":{"id":"n1820938981","loc":[-85.428082,42.0055124]},"n1820938983":{"id":"n1820938983","loc":[-85.5436315,41.9717484]},"n1820938985":{"id":"n1820938985","loc":[-85.5978336,41.9407437]},"n1820938986":{"id":"n1820938986","loc":[-85.491661,41.9860249]},"n1820938987":{"id":"n1820938987","loc":[-85.4942789,41.9801392]},"n1820938988":{"id":"n1820938988","loc":[-85.0416306,42.1010841]},"n1820938989":{"id":"n1820938989","loc":[-85.2653644,41.9984433]},"n1820938990":{"id":"n1820938990","loc":[-85.1028266,42.0881124]},"n1820938991":{"id":"n1820938991","loc":[-85.0163146,42.0887932]},"n1820938992":{"id":"n1820938992","loc":[-85.5282209,41.9678112]},"n1820938993":{"id":"n1820938993","loc":[-85.5442752,41.9715888]},"n1820938994":{"id":"n1820938994","loc":[-85.5634327,41.9658558]},"n1820938995":{"id":"n1820938995","loc":[-85.0384227,42.1037627]},"n1820938996":{"id":"n1820938996","loc":[-85.1144258,42.0854439]},"n1820938997":{"id":"n1820938997","loc":[-85.1870651,42.0506305]},"n1820938998":{"id":"n1820938998","loc":[-85.1256159,42.0747376]},"n1820938999":{"id":"n1820938999","loc":[-85.3272695,41.9715836]},"n1820939000":{"id":"n1820939000","loc":[-85.0543067,42.103098]},"n1820939001":{"id":"n1820939001","loc":[-85.4678173,41.9973585]},"n1820939003":{"id":"n1820939003","loc":[-85.0266626,42.0933154]},"n1820939004":{"id":"n1820939004","loc":[-85.0353046,42.1019728]},"n1820939005":{"id":"n1820939005","loc":[-85.1237961,42.0762798]},"n1820939006":{"id":"n1820939006","loc":[-85.2812214,41.9826702]},"n1820939007":{"id":"n1820939007","loc":[-85.2927763,41.9747343]},"n1820939008":{"id":"n1820939008","loc":[-85.3270979,41.9720862]},"n1820939009":{"id":"n1820939009","loc":[-85.488657,41.9856581]},"n1820939010":{"id":"n1820939010","loc":[-85.3087301,41.9701399]},"n1820939011":{"id":"n1820939011","loc":[-85.0276939,42.093768]},"n1820939012":{"id":"n1820939012","loc":[-85.2956516,41.9748779]},"n1820939013":{"id":"n1820939013","loc":[-85.1298579,42.0726443]},"n1820939014":{"id":"n1820939014","loc":[-85.105144,42.0870893]},"n1820939015":{"id":"n1820939015","loc":[-85.0677486,42.1053917]},"n1820939016":{"id":"n1820939016","loc":[-85.0333681,42.0993459]},"n1820939017":{"id":"n1820939017","loc":[-85.6384272,41.910805]},"n1820939018":{"id":"n1820939018","loc":[-85.399496,42.006894]},"n1820939019":{"id":"n1820939019","loc":[-85.2648427,41.9998318]},"n1820939020":{"id":"n1820939020","loc":[-85.1237424,42.0766779]},"n1820939021":{"id":"n1820939021","loc":[-85.2515025,42.0109442]},"n1820939022":{"id":"n1820939022","loc":[-85.5566306,41.9718385]},"n1820939023":{"id":"n1820939023","loc":[-85.090644,42.0938369]},"n1820939024":{"id":"n1820939024","loc":[-85.1245525,42.074914]},"n1820939025":{"id":"n1820939025","loc":[-85.1099934,42.0863926]},"n1820939026":{"id":"n1820939026","loc":[-85.1251653,42.0744589]},"n1820939027":{"id":"n1820939027","loc":[-85.401792,42.0068143]},"n1820939028":{"id":"n1820939028","loc":[-85.0094763,42.0899584]},"n1820939029":{"id":"n1820939029","loc":[-85.1330779,42.0705605]},"n1820939030":{"id":"n1820939030","loc":[-85.4935064,41.984398]},"n1820939031":{"id":"n1820939031","loc":[-85.5713334,41.9613939]},"n1820939032":{"id":"n1820939032","loc":[-85.0873945,42.0964669]},"n1820939033":{"id":"n1820939033","loc":[-85.0886497,42.0986481]},"n1820939034":{"id":"n1820939034","loc":[-85.3276343,41.9758897]},"n1820939035":{"id":"n1820939035","loc":[-85.1304386,42.0727387]},"n1820939036":{"id":"n1820939036","loc":[-85.2551932,42.0052999]},"n1820939037":{"id":"n1820939037","loc":[-85.2206936,42.0384458]},"n1820939038":{"id":"n1820939038","loc":[-85.2313645,42.0286389]},"n1820939039":{"id":"n1820939039","loc":[-85.0754586,42.1059835]},"n1820939040":{"id":"n1820939040","loc":[-85.0663324,42.1050812]},"n1820939041":{"id":"n1820939041","loc":[-85.2406234,42.0106887]},"n1820939042":{"id":"n1820939042","loc":[-85.0685962,42.1058175]},"n1820939043":{"id":"n1820939043","loc":[-85.0689462,42.1059437]},"n1820939044":{"id":"n1820939044","loc":[-85.0586144,42.1046144]},"n1820939045":{"id":"n1820939045","loc":[-85.3650565,41.9965452]},"n1820939047":{"id":"n1820939047","loc":[-85.5752558,41.9536014]},"n1820939048":{"id":"n1820939048","loc":[-85.5110159,41.9710624]},"n1820939050":{"id":"n1820939050","loc":[-85.2832641,41.9926477]},"n1820939051":{"id":"n1820939051","loc":[-85.0078402,42.0898947]},"n1820939052":{"id":"n1820939052","loc":[-85.3882737,42.0017916]},"n1820939053":{"id":"n1820939053","loc":[-85.1718945,42.0564937]},"n1820939054":{"id":"n1820939054","loc":[-85.0947048,42.0929293]},"n1820939055":{"id":"n1820939055","loc":[-85.4456944,42.0051082]},"n1820939056":{"id":"n1820939056","loc":[-85.3139872,41.9706903]},"n1820939057":{"id":"n1820939057","loc":[-85.3893895,42.0034021]},"n1820939058":{"id":"n1820939058","loc":[-85.2425332,42.0106089]},"n1820939059":{"id":"n1820939059","loc":[-85.6085624,41.9420844]},"n1820939060":{"id":"n1820939060","loc":[-85.210411,42.0397789]},"n1820939061":{"id":"n1820939061","loc":[-85.2762542,41.9960473]},"n1820939062":{"id":"n1820939062","loc":[-85.4686584,41.9969973]},"n1820939063":{"id":"n1820939063","loc":[-85.3860421,42.0018394]},"n1820939064":{"id":"n1820939064","loc":[-85.5636944,41.9644414]},"n1820939065":{"id":"n1820939065","loc":[-85.3267331,41.9766554]},"n1820939066":{"id":"n1820939066","loc":[-85.0868996,42.0943822]},"n1820939067":{"id":"n1820939067","loc":[-85.104861,42.0880038]},"n1820939068":{"id":"n1820939068","loc":[-85.5537123,41.9695093]},"n1820939069":{"id":"n1820939069","loc":[-85.6325092,41.9396743]},"n1820939070":{"id":"n1820939070","loc":[-85.3869648,42.0024454]},"n1820939071":{"id":"n1820939071","loc":[-85.2775349,41.9957335]},"n1820939072":{"id":"n1820939072","loc":[-85.2055616,42.0421533]},"n1820939073":{"id":"n1820939073","loc":[-85.4731431,41.9946531]},"n1820939074":{"id":"n1820939074","loc":[-85.0399609,42.1030833]},"n1820939075":{"id":"n1820939075","loc":[-85.3055758,41.9725169]},"n1820939076":{"id":"n1820939076","loc":[-85.4834599,41.994488]},"n1820939077":{"id":"n1820939077","loc":[-85.3819866,42.0023018]},"n1820939078":{"id":"n1820939078","loc":[-85.1218756,42.0789992]},"n1820939079":{"id":"n1820939079","loc":[-85.2793159,41.9944458]},"n1820939080":{"id":"n1820939080","loc":[-85.2495498,42.0101466]},"n1820939081":{"id":"n1820939081","loc":[-85.0035969,42.0872434]},"n1820939082":{"id":"n1820939082","loc":[-85.1054243,42.0865626]},"n1820939083":{"id":"n1820939083","loc":[-85.0917665,42.0934774]},"n1820939084":{"id":"n1820939084","loc":[-85.3442211,41.988938]},"n1820939086":{"id":"n1820939086","loc":[-85.273989,41.9953588]},"n1820939087":{"id":"n1820939087","loc":[-85.1142541,42.0852488]},"n1820939089":{"id":"n1820939089","loc":[-85.1526684,42.0615758]},"n1820939090":{"id":"n1820939090","loc":[-85.2538843,42.0110159]},"n1820939091":{"id":"n1820939091","loc":[-85.28341,41.9909635]},"n1820939092":{"id":"n1820939092","loc":[-85.3963178,42.0050217]},"n1820939093":{"id":"n1820939093","loc":[-85.0851682,42.1012472]},"n1820939095":{"id":"n1820939095","loc":[-85.2811784,41.986243]},"n1820939096":{"id":"n1820939096","loc":[-85.4274125,42.0052995]},"n1820939097":{"id":"n1820939097","loc":[-85.0871262,42.0951652]},"n1820939099":{"id":"n1820939099","loc":[-85.1314253,42.0671665]},"n1820939100":{"id":"n1820939100","loc":[-85.2778997,41.991001]},"n1820939101":{"id":"n1820939101","loc":[-85.112107,42.0862812]},"n1820939102":{"id":"n1820939102","loc":[-85.299911,41.9729955]},"n1820939103":{"id":"n1820939103","loc":[-85.639822,41.9094796]},"n1820939104":{"id":"n1820939104","loc":[-85.122294,42.0785334]},"n1820939105":{"id":"n1820939105","loc":[-85.2476294,42.015719]},"n1820939106":{"id":"n1820939106","loc":[-85.4946007,41.9814631]},"n1820939107":{"id":"n1820939107","loc":[-85.0879524,42.0986919]},"n1820939108":{"id":"n1820939108","loc":[-85.0342814,42.098274]},"n1820939109":{"id":"n1820939109","loc":[-85.2450695,42.0095463]},"n1820939110":{"id":"n1820939110","loc":[-85.3847546,42.0024135]},"n1820939111":{"id":"n1820939111","loc":[-85.2961344,41.9742558]},"n1820939112":{"id":"n1820939112","loc":[-85.27899,41.994317]},"n1820939114":{"id":"n1820939114","loc":[-85.1017644,42.0886618]},"n1820939115":{"id":"n1820939115","loc":[-85.076215,42.1056333]},"n1820939116":{"id":"n1820939116","loc":[-85.1198009,42.0805349]},"n1820939117":{"id":"n1820939117","loc":[-85.11988,42.0798513]},"n1820939118":{"id":"n1820939118","loc":[-85.147819,42.0625476]},"n1820939119":{"id":"n1820939119","loc":[-85.0585969,42.1029042]},"n1820939120":{"id":"n1820939120","loc":[-85.1248596,42.0745744]},"n1820939121":{"id":"n1820939121","loc":[-85.3023786,41.9725249]},"n1820939123":{"id":"n1820939123","loc":[-85.0119332,42.0900699]},"n1820939124":{"id":"n1820939124","loc":[-85.2466852,42.0170343]},"n1820939125":{"id":"n1820939125","loc":[-85.0033019,42.0872792]},"n1820939126":{"id":"n1820939126","loc":[-85.0042084,42.0875778]},"n1820939128":{"id":"n1820939128","loc":[-85.0052961,42.0885424]},"n1820939130":{"id":"n1820939130","loc":[-85.0647942,42.10508]},"n1820939131":{"id":"n1820939131","loc":[-85.2824123,41.9825107]},"n1820939132":{"id":"n1820939132","loc":[-85.3210039,41.9723255]},"n1820939133":{"id":"n1820939133","loc":[-85.0491033,42.1014184]},"n1820939134":{"id":"n1820939134","loc":[-85.1127776,42.0855168]},"n1820939135":{"id":"n1820939135","loc":[-85.1243768,42.0759322]},"n1820939137":{"id":"n1820939137","loc":[-85.125974,42.0747547]},"n1820939138":{"id":"n1820939138","loc":[-85.1071248,42.0859973]},"n1820939139":{"id":"n1820939139","loc":[-85.5326175,41.9674833]},"n1820939140":{"id":"n1820939140","loc":[-85.1338715,42.0660833]},"n1820939142":{"id":"n1820939142","loc":[-85.649671,41.9135675]},"n1820939144":{"id":"n1820939144","loc":[-85.0236545,42.0920444]},"n1820939145":{"id":"n1820939145","loc":[-85.1084391,42.0859376]},"n1820939146":{"id":"n1820939146","loc":[-85.1539988,42.0618626]},"n1820939147":{"id":"n1820939147","loc":[-85.2354521,42.026511]},"n1820939148":{"id":"n1820939148","loc":[-85.2362246,42.0260408]},"n1820939149":{"id":"n1820939149","loc":[-85.2401342,42.0115233]},"n1820939150":{"id":"n1820939150","loc":[-85.295319,41.9747423]},"n1820939151":{"id":"n1820939151","loc":[-85.1164696,42.0835409]},"n1820939152":{"id":"n1820939152","loc":[-85.3232891,41.9712885]},"n1820939153":{"id":"n1820939153","loc":[-85.2574463,42.0068944]},"n1820939155":{"id":"n1820939155","loc":[-85.5704064,41.9598246]},"n1820939156":{"id":"n1820939156","loc":[-85.0349077,42.0998116]},"n1820939157":{"id":"n1820939157","loc":[-85.0949529,42.0925619]},"n1820939159":{"id":"n1820939159","loc":[-85.0179829,42.0902343]},"n1820939160":{"id":"n1820939160","loc":[-85.0405832,42.1016942]},"n1820939161":{"id":"n1820939161","loc":[-85.2534015,42.0111833]},"n1820939162":{"id":"n1820939162","loc":[-85.0839881,42.102708]},"n1820939163":{"id":"n1820939163","loc":[-85.0341996,42.1008385]},"n1820939164":{"id":"n1820939164","loc":[-85.1037761,42.0879731]},"n1820939173":{"id":"n1820939173","loc":[-85.0460616,42.1005786]},"n1820939177":{"id":"n1820939177","loc":[-85.0061651,42.0878059]},"n1820939181":{"id":"n1820939181","loc":[-85.1456775,42.0654684]},"n1820939183":{"id":"n1820939183","loc":[-85.1325508,42.0718439]},"n1820939185":{"id":"n1820939185","loc":[-85.2485842,42.008329]},"n1820939187":{"id":"n1820939187","loc":[-85.2744128,41.9949322]},"n1820939189":{"id":"n1820939189","loc":[-85.2579025,41.9999542]},"n1820939191":{"id":"n1820939191","loc":[-85.3358998,41.9828987]},"n1820939193":{"id":"n1820939193","loc":[-85.3192658,41.9716714]},"n1820939194":{"id":"n1820939194","loc":[-85.6400795,41.9130725]},"n1820939195":{"id":"n1820939195","loc":[-85.3278489,41.9780591]},"n1820939196":{"id":"n1820939196","loc":[-85.2800197,41.983061]},"n1820939197":{"id":"n1820939197","loc":[-85.3278167,41.9692943]},"n1820939198":{"id":"n1820939198","loc":[-85.3366894,41.9838653]},"n1820939199":{"id":"n1820939199","loc":[-85.0328383,42.0969923]},"n1820939201":{"id":"n1820939201","loc":[-85.3259284,41.9720383]},"n1820939217":{"id":"n1820939217","loc":[-85.1840181,42.0503277]},"n1820939220":{"id":"n1820939220","loc":[-85.422563,42.0089986]},"n1820939222":{"id":"n1820939222","loc":[-85.555386,41.9707856]},"n1820939224":{"id":"n1820939224","loc":[-85.3830809,42.002254]},"n1820939226":{"id":"n1820939226","loc":[-84.9917938,42.0857517]},"n1820939227":{"id":"n1820939227","loc":[-85.2936775,41.9740484]},"n1820939228":{"id":"n1820939228","loc":[-85.2632133,41.9975024]},"n1820939229":{"id":"n1820939229","loc":[-85.2809424,41.9853259]},"n1820939230":{"id":"n1820939230","loc":[-85.242104,42.0131204]},"n1820939232":{"id":"n1820939232","loc":[-85.2610246,41.9963901]},"n1820939233":{"id":"n1820939233","loc":[-85.2335531,42.0268378]},"n1820939234":{"id":"n1820939234","loc":[-85.3188839,41.9713575]},"n1820939235":{"id":"n1820939235","loc":[-85.2413637,42.0225658]},"n1820939237":{"id":"n1820939237","loc":[-85.0010796,42.0887215]},"n1820939239":{"id":"n1820939239","loc":[-85.2241697,42.0362624]},"n1820939243":{"id":"n1820939243","loc":[-85.0368456,42.1040134]},"n1820939244":{"id":"n1820939244","loc":[-85.1327986,42.069524]},"n1820939260":{"id":"n1820939260","loc":[-85.5408163,41.9711206]},"n1820939261":{"id":"n1820939261","loc":[-85.2959199,41.9746546]},"n1820939262":{"id":"n1820939262","loc":[-85.3298659,41.9683598]},"n1820939263":{"id":"n1820939263","loc":[-85.2240581,42.0358425]},"n1820939264":{"id":"n1820939264","loc":[-85.2438206,42.0101944]},"n1820939265":{"id":"n1820939265","loc":[-85.3984489,42.0059589]},"n1820939266":{"id":"n1820939266","loc":[-85.2330811,42.0294279]},"n1820939268":{"id":"n1820939268","loc":[-85.1126877,42.0857704]},"n1820939271":{"id":"n1820939271","loc":[-85.254925,42.0106253]},"n1820939273":{"id":"n1820939273","loc":[-85.4328046,42.0064662]},"n1820939277":{"id":"n1820939277","loc":[-85.289622,41.9789616]},"n1820939279":{"id":"n1820939279","loc":[-85.4574532,42.0004043]},"n1820939281":{"id":"n1820939281","loc":[-85.4803486,41.9867211]},"n1820939283":{"id":"n1820939283","loc":[-85.157475,42.0631848]},"n1820939285":{"id":"n1820939285","loc":[-85.2571458,42.0059935]},"n1820939287":{"id":"n1820939287","loc":[-85.2818544,41.9825984]},"n1820939289":{"id":"n1820939289","loc":[-85.2298302,42.0328781]},"n1820939291":{"id":"n1820939291","loc":[-85.4819523,41.984821]},"n1820939301":{"id":"n1820939301","loc":[-85.3139765,41.9701159]},"n1820939304":{"id":"n1820939304","loc":[-85.0424447,42.101742]},"n1820939306":{"id":"n1820939306","loc":[-85.6360283,41.9338482]},"n1820939310":{"id":"n1820939310","loc":[-85.3463025,41.9913622]},"n1820939312":{"id":"n1820939312","loc":[-85.4664869,41.9988097]},"n1820939314":{"id":"n1820939314","loc":[-85.149364,42.0622449]},"n1820939316":{"id":"n1820939316","loc":[-85.2460415,42.0153125]},"n1820939318":{"id":"n1820939318","loc":[-85.4806103,41.9924523]},"n1820939320":{"id":"n1820939320","loc":[-85.2449042,42.0190987]},"n1820939322":{"id":"n1820939322","loc":[-85.5280165,41.9689263]},"n1820939324":{"id":"n1820939324","loc":[-85.0051204,42.0882625]},"n1820939326":{"id":"n1820939326","loc":[-85.1240925,42.0771546]},"n1820939329":{"id":"n1820939329","loc":[-85.2261653,42.0342225]},"n1820939331":{"id":"n1820939331","loc":[-85.5259933,41.972211]},"n1820939333":{"id":"n1820939333","loc":[-85.0074754,42.0883183]},"n1820939335":{"id":"n1820939335","loc":[-85.0764014,42.1055549]},"n1820939336":{"id":"n1820939336","loc":[-85.2908773,41.9769597]},"n1820939337":{"id":"n1820939337","loc":[-85.4095382,42.0083449]},"n1820939346":{"id":"n1820939346","loc":[-85.2514166,42.0111753]},"n1820939348":{"id":"n1820939348","loc":[-85.0030377,42.0873799]},"n1820939350":{"id":"n1820939350","loc":[-85.3659362,41.9964974]},"n1820939352":{"id":"n1820939352","loc":[-85.226058,42.0348281]},"n1820939355":{"id":"n1820939355","loc":[-85.1902408,42.0507101]},"n1820939357":{"id":"n1820939357","loc":[-85.2781854,41.9946001]},"n1820939359":{"id":"n1820939359","loc":[-85.2139988,42.0405175]},"n1820939361":{"id":"n1820939361","loc":[-85.0086609,42.0908262]},"n1820939363":{"id":"n1820939363","loc":[-85.0627128,42.1043398]},"n1820939365":{"id":"n1820939365","loc":[-85.1311346,42.072501]},"n1820939369":{"id":"n1820939369","loc":[-85.248198,42.0082652]},"n1820939370":{"id":"n1820939370","loc":[-84.99792,42.087794]},"n1820939371":{"id":"n1820939371","loc":[-85.2786775,41.9942783]},"n1820939372":{"id":"n1820939372","loc":[-85.0342103,42.1013957]},"n1820939373":{"id":"n1820939373","loc":[-85.2022357,42.0444799]},"n1820939374":{"id":"n1820939374","loc":[-85.2279205,42.0337388]},"n1820939375":{"id":"n1820939375","loc":[-85.1337699,42.0712614]},"n1820939376":{"id":"n1820939376","loc":[-85.317517,41.9707062]},"n1820939377":{"id":"n1820939377","loc":[-85.1326326,42.070218]},"n1820939394":{"id":"n1820939394","loc":[-85.0197746,42.0899118]},"n1820939397":{"id":"n1820939397","loc":[-85.2590076,41.9984632]},"n1820939399":{"id":"n1820939399","loc":[-85.2469964,42.0083449]},"n1820939400":{"id":"n1820939400","loc":[-85.2470929,42.0146668]},"n1820939401":{"id":"n1820939401","loc":[-84.9984095,42.0878087]},"n1820939402":{"id":"n1820939402","loc":[-85.2372653,42.0243273]},"n1820939403":{"id":"n1820939403","loc":[-85.2454986,42.0091955]},"n1820939404":{"id":"n1820939404","loc":[-85.0539205,42.1035995]},"n1820939405":{"id":"n1820939405","loc":[-85.550601,41.9706101]},"n1820939406":{"id":"n1820939406","loc":[-85.0351343,42.0999656]},"n1820939407":{"id":"n1820939407","loc":[-85.0082908,42.0905755]},"n1820939408":{"id":"n1820939408","loc":[-85.0132904,42.0902251]},"n1820939410":{"id":"n1820939410","loc":[-85.0892546,42.094012]},"n1820939412":{"id":"n1820939412","loc":[-85.0350793,42.1030315]},"n1820939416":{"id":"n1820939416","loc":[-85.0012406,42.0886777]},"n1820939418":{"id":"n1820939418","loc":[-85.0577453,42.1029229]},"n1820939420":{"id":"n1820939420","loc":[-85.1230786,42.0776722]},"n1820939422":{"id":"n1820939422","loc":[-85.571136,41.9649304]},"n1820939436":{"id":"n1820939436","loc":[-85.1137968,42.0848997]},"n1820939437":{"id":"n1820939437","loc":[-85.3559584,41.9925105]},"n1820939438":{"id":"n1820939438","loc":[-85.0080172,42.0903565]},"n1820939439":{"id":"n1820939439","loc":[-85.0048897,42.0880913]},"n1820939441":{"id":"n1820939441","loc":[-85.0406959,42.1018574]},"n1820939443":{"id":"n1820939443","loc":[-85.3897328,42.0029078]},"n1820939445":{"id":"n1820939445","loc":[-85.122349,42.0782814]},"n1820939448":{"id":"n1820939448","loc":[-85.4872193,41.985036]},"n1820939450":{"id":"n1820939450","loc":[-85.0120459,42.0904919]},"n1820939452":{"id":"n1820939452","loc":[-85.6320543,41.921982]},"n1820939456":{"id":"n1820939456","loc":[-85.0844749,42.1036843]},"n1820939458":{"id":"n1820939458","loc":[-85.0968037,42.091296]},"n1820939463":{"id":"n1820939463","loc":[-85.5339747,41.9681841]},"n1820939465":{"id":"n1820939465","loc":[-85.4125423,42.0072129]},"n1820939467":{"id":"n1820939467","loc":[-85.6335563,41.9303626]},"n1820939469":{"id":"n1820939469","loc":[-85.2821014,41.9932126]},"n1820939471":{"id":"n1820939471","loc":[-85.374691,41.9969917]},"n1820939485":{"id":"n1820939485","loc":[-85.4471321,42.0049806]},"n1820939487":{"id":"n1820939487","loc":[-85.3752532,41.9972206]},"n1820939489":{"id":"n1820939489","loc":[-85.4517283,42.005927]},"n1820939492":{"id":"n1820939492","loc":[-85.4662552,42.0005693]},"n1820939494":{"id":"n1820939494","loc":[-85.0120083,42.0902928]},"n1820939496":{"id":"n1820939496","loc":[-85.044463,42.1004631]},"n1820939498":{"id":"n1820939498","loc":[-85.418293,42.0089667]},"n1820939500":{"id":"n1820939500","loc":[-85.0554762,42.1027358]},"n1820939504":{"id":"n1820939504","loc":[-85.1246289,42.0746858]},"n1820939507":{"id":"n1820939507","loc":[-85.0408139,42.1021838]},"n1820939508":{"id":"n1820939508","loc":[-85.1236204,42.0775169]},"n1820939509":{"id":"n1820939509","loc":[-85.0350109,42.1037428]},"n1820939510":{"id":"n1820939510","loc":[-85.0551583,42.1029878]},"n1820939511":{"id":"n1820939511","loc":[-85.0956771,42.0916662]},"n1820939512":{"id":"n1820939512","loc":[-85.2323408,42.0273638]},"n1820939513":{"id":"n1820939513","loc":[-85.1232771,42.0762388]},"n1820939531":{"id":"n1820939531","loc":[-85.264608,41.9997828]},"n1820939533":{"id":"n1820939533","loc":[-85.4198808,42.0087914]},"n1820939535":{"id":"n1820939535","loc":[-85.3080864,41.9715677]},"n1820939536":{"id":"n1820939536","loc":[-85.1189426,42.0812596]},"n1820939537":{"id":"n1820939537","loc":[-85.2642741,41.9996764]},"n1820939538":{"id":"n1820939538","loc":[-85.2572531,42.0079627]},"n1820939539":{"id":"n1820939539","loc":[-85.2907807,41.9790174]},"n1820939540":{"id":"n1820939540","loc":[-85.3171415,41.9707301]},"n1820939541":{"id":"n1820939541","loc":[-85.08777,42.0953841]},"n1820939542":{"id":"n1820939542","loc":[-85.1239262,42.0773218]},"n1820939543":{"id":"n1820939543","loc":[-84.9973956,42.0877968]},"n1820939544":{"id":"n1820939544","loc":[-85.011606,42.0896161]},"n1820939545":{"id":"n1820939545","loc":[-85.4077358,42.0082971]},"n1820939546":{"id":"n1820939546","loc":[-85.3614945,41.9933717]},"n1820939547":{"id":"n1820939547","loc":[-85.3189118,41.9697649]},"n1820939550":{"id":"n1820939550","loc":[-85.1262691,42.0740221]},"n1820939551":{"id":"n1820939551","loc":[-85.3863639,41.9994635]},"n1820939552":{"id":"n1820939552","loc":[-85.2836034,41.9923953]},"n1820939554":{"id":"n1820939554","loc":[-85.3222377,41.9715916]},"n1820939555":{"id":"n1820939555","loc":[-85.0122658,42.0906312]},"n1820939556":{"id":"n1820939556","loc":[-85.0022652,42.0877581]},"n1820939557":{"id":"n1820939557","loc":[-85.1011314,42.0899954]},"n1820939559":{"id":"n1820939559","loc":[-85.0008181,42.0885293]},"n1820939561":{"id":"n1820939561","loc":[-85.3637046,41.9942488]},"n1820939562":{"id":"n1820939562","loc":[-85.4500117,42.0052892]},"n1820939563":{"id":"n1820939563","loc":[-85.0537636,42.1036365]},"n1820939565":{"id":"n1820939565","loc":[-85.2367503,42.0246939]},"n1820939566":{"id":"n1820939566","loc":[-85.0448479,42.1002653]},"n1820939567":{"id":"n1820939567","loc":[-85.6337065,41.9295006]},"n1820939568":{"id":"n1820939568","loc":[-85.0879792,42.095623]},"n1820939569":{"id":"n1820939569","loc":[-85.6347623,41.9352369]},"n1820939570":{"id":"n1820939570","loc":[-85.1497931,42.0620378]},"n1820939571":{"id":"n1820939571","loc":[-85.5676169,41.9656324]},"n1820939572":{"id":"n1820939572","loc":[-85.638041,41.9166971]},"n1820939573":{"id":"n1820939573","loc":[-85.4993429,41.9781293]},"n1820939574":{"id":"n1820939574","loc":[-85.5352831,41.9692127]},"n1820939575":{"id":"n1820939575","loc":[-84.9924429,42.0857118]},"n1820939577":{"id":"n1820939577","loc":[-85.0581101,42.1026721]},"n1820939578":{"id":"n1820939578","loc":[-85.641088,41.9094477]},"n1820939579":{"id":"n1820939579","loc":[-85.2548821,42.0052282]},"n1820939580":{"id":"n1820939580","loc":[-85.1124463,42.0859734]},"n1820939581":{"id":"n1820939581","loc":[-85.1083479,42.0857624]},"n1820939583":{"id":"n1820939583","loc":[-85.1387424,42.0648893]},"n1820939584":{"id":"n1820939584","loc":[-85.5152645,41.9700892]},"n1820939585":{"id":"n1820939585","loc":[-85.5463738,41.9713439]},"n1820939586":{"id":"n1820939586","loc":[-85.360207,41.9933717]},"n1820939587":{"id":"n1820939587","loc":[-85.2402372,42.0120917]},"n1820939588":{"id":"n1820939588","loc":[-85.3936381,42.0047255]},"n1820939589":{"id":"n1820939589","loc":[-85.3310246,41.973784]},"n1820939590":{"id":"n1820939590","loc":[-85.0329403,42.096642]},"n1820939591":{"id":"n1820939591","loc":[-85.0097271,42.0910981]},"n1820939593":{"id":"n1820939593","loc":[-85.0446562,42.1003437]},"n1820939595":{"id":"n1820939595","loc":[-85.0856671,42.1008452]},"n1820939596":{"id":"n1820939596","loc":[-85.4087228,42.0083449]},"n1820939597":{"id":"n1820939597","loc":[-85.0609519,42.1052564]},"n1820939598":{"id":"n1820939598","loc":[-85.3432126,41.9874548]},"n1820939599":{"id":"n1820939599","loc":[-85.4041738,42.0067027]},"n1820939600":{"id":"n1820939600","loc":[-85.0825437,42.1035768]},"n1820939601":{"id":"n1820939601","loc":[-85.048422,42.101498]},"n1820939602":{"id":"n1820939602","loc":[-85.0336256,42.0999031]},"n1820939603":{"id":"n1820939603","loc":[-85.046818,42.1014104]},"n1820939605":{"id":"n1820939605","loc":[-85.2856524,41.98078]},"n1820939607":{"id":"n1820939607","loc":[-85.1118173,42.0864245]},"n1820939609":{"id":"n1820939609","loc":[-85.0443397,42.1006263]},"n1820939610":{"id":"n1820939610","loc":[-85.0336698,42.0978361]},"n1820939611":{"id":"n1820939611","loc":[-85.4630322,42.0014248]},"n1820939612":{"id":"n1820939612","loc":[-85.0613127,42.1052353]},"n1820939613":{"id":"n1820939613","loc":[-85.0137571,42.0887801]},"n1820939614":{"id":"n1820939614","loc":[-85.272487,41.9982013]},"n1820939616":{"id":"n1820939616","loc":[-85.4665727,41.9983791]},"n1820939617":{"id":"n1820939617","loc":[-85.1288078,42.0725476]},"n1820939618":{"id":"n1820939618","loc":[-85.4653282,42.00109]},"n1820939619":{"id":"n1820939619","loc":[-85.2314717,42.0276746]},"n1820939620":{"id":"n1820939620","loc":[-85.255982,42.0003569]},"n1820939621":{"id":"n1820939621","loc":[-85.2886779,41.9787223]},"n1820939622":{"id":"n1820939622","loc":[-85.22438,42.0367509]},"n1820939623":{"id":"n1820939623","loc":[-85.0334713,42.0998382]},"n1820939624":{"id":"n1820939624","loc":[-85.2236504,42.037484]},"n1820939625":{"id":"n1820939625","loc":[-85.636908,41.9175162]},"n1820939627":{"id":"n1820939627","loc":[-85.2669187,41.9989707]},"n1820939628":{"id":"n1820939628","loc":[-85.3247268,41.9720702]},"n1820939629":{"id":"n1820939629","loc":[-85.3785104,41.9987299]},"n1820939630":{"id":"n1820939630","loc":[-85.5267658,41.9720515]},"n1820939631":{"id":"n1820939631","loc":[-85.2445116,42.0098811]},"n1820939632":{"id":"n1820939632","loc":[-85.1271448,42.0725077]},"n1820939633":{"id":"n1820939633","loc":[-85.0345751,42.099724]},"n1820939634":{"id":"n1820939634","loc":[-85.4217476,42.0089986]},"n1820939635":{"id":"n1820939635","loc":[-85.3121848,41.9689433]},"n1820939636":{"id":"n1820939636","loc":[-85.2826419,41.9929985]},"n1820939637":{"id":"n1820939637","loc":[-85.3160257,41.9706344]},"n1820939638":{"id":"n1820939638","loc":[-85.5684967,41.9657919]},"n1820939640":{"id":"n1820939640","loc":[-85.225131,42.0356194]},"n1820939642":{"id":"n1820939642","loc":[-85.1324124,42.0693328]},"n1820939644":{"id":"n1820939644","loc":[-84.9994073,42.0878843]},"n1820939645":{"id":"n1820939645","loc":[-85.1087596,42.0863329]},"n1820939646":{"id":"n1820939646","loc":[-85.2915532,41.9782996]},"n1820939647":{"id":"n1820939647","loc":[-84.9988708,42.0877808]},"n1820939648":{"id":"n1820939648","loc":[-85.2243628,42.0356728]},"n1820939649":{"id":"n1820939649","loc":[-85.0427397,42.1020524]},"n1820939650":{"id":"n1820939650","loc":[-85.6388392,41.9100752]},"n1820939651":{"id":"n1820939651","loc":[-85.0133709,42.0888557]},"n1820939652":{"id":"n1820939652","loc":[-85.318798,41.9701211]},"n1820939653":{"id":"n1820939653","loc":[-85.6335778,41.9190602]},"n1820939654":{"id":"n1820939654","loc":[-85.6338396,41.9370247]},"n1820939655":{"id":"n1820939655","loc":[-85.0939069,42.0931988]},"n1820939656":{"id":"n1820939656","loc":[-85.5702347,41.9651378]},"n1820939657":{"id":"n1820939657","loc":[-85.4235286,42.0088392]},"n1820939658":{"id":"n1820939658","loc":[-85.2740856,41.9972206]},"n1820939659":{"id":"n1820939659","loc":[-85.4824299,41.9934195]},"n1820939660":{"id":"n1820939660","loc":[-85.3857846,42.0014408]},"n1820939661":{"id":"n1820939661","loc":[-85.0451658,42.10028]},"n1820939662":{"id":"n1820939662","loc":[-85.3893036,42.001377]},"n1820939664":{"id":"n1820939664","loc":[-85.2455845,42.0088607]},"n1820939665":{"id":"n1820939665","loc":[-85.2741071,41.9951116]},"n1820939666":{"id":"n1820939666","loc":[-85.1298375,42.0677718]},"n1820939667":{"id":"n1820939667","loc":[-85.5491848,41.9707377]},"n1820939669":{"id":"n1820939669","loc":[-85.2780298,41.995238]},"n1820939670":{"id":"n1820939670","loc":[-85.1330068,42.0716926]},"n1820939671":{"id":"n1820939671","loc":[-85.0811342,42.1025129]},"n1820939672":{"id":"n1820939672","loc":[-85.2325124,42.0290135]},"n1820939673":{"id":"n1820939673","loc":[-85.2975077,41.9716953]},"n1820939674":{"id":"n1820939674","loc":[-85.0951729,42.0922394]},"n1820939676":{"id":"n1820939676","loc":[-85.0363252,42.1043119]},"n1820939677":{"id":"n1820939677","loc":[-85.2960057,41.97349]},"n1820939678":{"id":"n1820939678","loc":[-85.3701849,41.9982515]},"n1820939679":{"id":"n1820939679","loc":[-85.3381486,41.9848861]},"n1820939680":{"id":"n1820939680","loc":[-85.2058448,42.0417286]},"n1820939682":{"id":"n1820939682","loc":[-85.0819335,42.1034443]},"n1820939683":{"id":"n1820939683","loc":[-85.3872223,41.9993359]},"n1820939684":{"id":"n1820939684","loc":[-85.095366,42.091909]},"n1820939685":{"id":"n1820939685","loc":[-85.2327914,42.0291888]},"n1820939686":{"id":"n1820939686","loc":[-85.0433459,42.1018773]},"n1820939687":{"id":"n1820939687","loc":[-85.0585339,42.1027318]},"n1820939688":{"id":"n1820939688","loc":[-85.0062885,42.0876347]},"n1820939689":{"id":"n1820939689","loc":[-85.246299,42.017377]},"n1820939690":{"id":"n1820939690","loc":[-85.2932376,41.9742877]},"n1820939691":{"id":"n1820939691","loc":[-85.2962846,41.9736815]},"n1820939692":{"id":"n1820939692","loc":[-85.6052365,41.9409193]},"n1820939693":{"id":"n1820939693","loc":[-85.2570536,42.0003341]},"n1820939694":{"id":"n1820939694","loc":[-85.0488458,42.1014064]},"n1820939695":{"id":"n1820939695","loc":[-85.4050321,42.0069578]},"n1820939696":{"id":"n1820939696","loc":[-85.4847517,41.9845894]},"n1820939697":{"id":"n1820939697","loc":[-85.0844655,42.1013826]},"n1820939698":{"id":"n1820939698","loc":[-85.1437206,42.0650008]},"n1820939699":{"id":"n1820939699","loc":[-85.1168183,42.0864034]},"n1820939700":{"id":"n1820939700","loc":[-85.5479831,41.9711366]},"n1820939701":{"id":"n1820939701","loc":[-85.0349948,42.1034124]},"n1820939702":{"id":"n1820939702","loc":[-85.0835589,42.1038821]},"n1820939703":{"id":"n1820939703","loc":[-85.0203875,42.0902649]},"n1820939704":{"id":"n1820939704","loc":[-85.0371191,42.1038184]},"n1820939705":{"id":"n1820939705","loc":[-85.1273312,42.0735681]},"n1820939707":{"id":"n1820939707","loc":[-85.1272239,42.0730226]},"n1820939710":{"id":"n1820939710","loc":[-85.0349881,42.1019012]},"n1820939712":{"id":"n1820939712","loc":[-85.2440459,42.0178313]},"n1820939713":{"id":"n1820939713","loc":[-85.2444751,42.0182618]},"n1820939714":{"id":"n1820939714","loc":[-85.0539996,42.1032863]},"n1820939715":{"id":"n1820939715","loc":[-85.2215905,42.0373246]},"n1820939716":{"id":"n1820939716","loc":[-85.0649712,42.1051994]},"n1820939717":{"id":"n1820939717","loc":[-85.0927146,42.0927581]},"n1820939718":{"id":"n1820939718","loc":[-85.3884668,42.0042312]},"n1820939719":{"id":"n1820939719","loc":[-85.0840672,42.1013241]},"n1820939720":{"id":"n1820939720","loc":[-85.304739,41.9725408]},"n1820939721":{"id":"n1820939721","loc":[-85.2243585,42.0371334]},"n1820939722":{"id":"n1820939722","loc":[-85.0599823,42.1049686]},"n1820939723":{"id":"n1820939723","loc":[-85.0298825,42.0944288]},"n1820939724":{"id":"n1820939724","loc":[-85.0366095,42.1042443]},"n1820939725":{"id":"n1820939725","loc":[-85.0698783,42.1058135]},"n1820939726":{"id":"n1820939726","loc":[-85.1054551,42.0873361]},"n1820939727":{"id":"n1820939727","loc":[-84.9952324,42.0864285]},"n1820939728":{"id":"n1820939728","loc":[-85.3442211,41.9897993]},"n1820939729":{"id":"n1820939729","loc":[-85.4386134,42.0056822]},"n1820939730":{"id":"n1820939730","loc":[-85.2438528,42.0146589]},"n1820939731":{"id":"n1820939731","loc":[-85.0355581,42.1041846]},"n1820939732":{"id":"n1820939732","loc":[-85.557682,41.9724447]},"n1820939734":{"id":"n1820939734","loc":[-85.2299418,42.033314]},"n1820939735":{"id":"n1820939735","loc":[-85.6297412,41.9419088]},"n1820939736":{"id":"n1820939736","loc":[-85.2645101,41.9980259]},"n1820939738":{"id":"n1820939738","loc":[-85.082195,42.1035649]},"n1820939739":{"id":"n1820939739","loc":[-85.234272,42.0267102]},"n1820939740":{"id":"n1820939740","loc":[-85.0130758,42.0895006]},"n1820939741":{"id":"n1820939741","loc":[-85.4594702,42.0000375]},"n1820939742":{"id":"n1820939742","loc":[-84.9946745,42.0863687]},"n1820939743":{"id":"n1820939743","loc":[-85.6438775,41.9120186]},"n1820939744":{"id":"n1820939744","loc":[-85.6372685,41.9168089]},"n1820939745":{"id":"n1820939745","loc":[-85.2789468,41.9893208]},"n1820939747":{"id":"n1820939747","loc":[-85.3775019,41.998427]},"n1820939749":{"id":"n1820939749","loc":[-85.0993571,42.0909178]},"n1820939750":{"id":"n1820939750","loc":[-85.1308503,42.0669339]},"n1820939751":{"id":"n1820939751","loc":[-85.4802566,41.9856659]},"n1820939752":{"id":"n1820939752","loc":[-85.2543563,42.0108804]},"n1820939753":{"id":"n1820939753","loc":[-85.1041033,42.0878815]},"n1820939755":{"id":"n1820939755","loc":[-85.4000969,42.0071651]},"n1820939757":{"id":"n1820939757","loc":[-85.3858275,42.0022381]},"n1820939758":{"id":"n1820939758","loc":[-85.3653998,41.996609]},"n1820939759":{"id":"n1820939759","loc":[-85.2432949,42.0202305]},"n1820939760":{"id":"n1820939760","loc":[-85.3878874,42.0042472]},"n1820939761":{"id":"n1820939761","loc":[-85.2516741,42.0114145]},"n1820939762":{"id":"n1820939762","loc":[-85.2788825,41.9865142]},"n1820939763":{"id":"n1820939763","loc":[-85.0009147,42.0886686]},"n1820939764":{"id":"n1820939764","loc":[-85.3918142,42.003434]},"n1820939765":{"id":"n1820939765","loc":[-85.5532832,41.9696848]},"n1820939766":{"id":"n1820939766","loc":[-85.5545063,41.969254]},"n1820939768":{"id":"n1820939768","loc":[-85.1327989,42.0704769]},"n1820939770":{"id":"n1820939770","loc":[-85.0588558,42.1047696]},"n1820939772":{"id":"n1820939772","loc":[-85.555798,41.9713017]},"n1820939773":{"id":"n1820939773","loc":[-85.0565853,42.1023589]},"n1820939774":{"id":"n1820939774","loc":[-85.2582941,41.9992765]},"n1820939775":{"id":"n1820939775","loc":[-85.3007264,41.9727642]},"n1820939776":{"id":"n1820939776","loc":[-85.2477045,42.0082652]},"n1820939777":{"id":"n1820939777","loc":[-85.2415247,42.0104973]},"n1821006698":{"id":"n1821006698","loc":[-85.6345227,41.9382009]},"n1821006700":{"id":"n1821006700","loc":[-85.6344894,41.938975]},"n1821006704":{"id":"n1821006704","loc":[-85.6351181,41.9370157]},"n1821006706":{"id":"n1821006706","loc":[-85.6357554,41.9361657]},"n1821006708":{"id":"n1821006708","loc":[-85.6351235,41.9368481]},"n1821006710":{"id":"n1821006710","loc":[-85.6352844,41.9364211]},"n1821006712":{"id":"n1821006712","loc":[-85.6351503,41.937307]},"n1821006716":{"id":"n1821006716","loc":[-85.6350366,41.9379774]},"n1821006725":{"id":"n1821006725","loc":[-85.6352147,41.9375903]},"n1821137607":{"id":"n1821137607","loc":[-85.5297057,41.9669915]},"n1821137608":{"id":"n1821137608","loc":[-85.5288598,41.9673094]},"n1821139530":{"id":"n1821139530","loc":[-85.4832228,41.9881686]},"n1821139531":{"id":"n1821139531","loc":[-85.4812101,41.9851258]},"n1821139532":{"id":"n1821139532","loc":[-85.4799127,41.9860244]},"n1821139533":{"id":"n1821139533","loc":[-85.4800313,41.9865555]},"n1841425201":{"id":"n1841425201","loc":[-85.4334577,42.0063713]},"n1841425222":{"id":"n1841425222","loc":[-85.4382449,42.0055785]},"n1914861007":{"id":"n1914861007","loc":[-85.394959,42.0057472]},"n1914861057":{"id":"n1914861057","loc":[-85.3967185,42.0049695]},"n1914861112":{"id":"n1914861112","loc":[-85.394179,42.0056906]},"n1914861306":{"id":"n1914861306","loc":[-85.3900226,42.0028488]},"n2114807565":{"id":"n2114807565","loc":[-85.6385979,41.9577824]},"n2114807568":{"id":"n2114807568","loc":[-85.6325097,41.9775713]},"n2114807572":{"id":"n2114807572","loc":[-85.6328996,41.9980965]},"n2114807578":{"id":"n2114807578","loc":[-85.6344818,41.9696956]},"n2114807583":{"id":"n2114807583","loc":[-85.6326289,41.9757853]},"n2114807593":{"id":"n2114807593","loc":[-85.6360828,41.9650674]},"n2130304159":{"id":"n2130304159","loc":[-85.6352537,41.9450015],"tags":{"railway":"level_crossing"}},"n2139795852":{"id":"n2139795852","loc":[-85.6374708,41.9311633]},"n2139858882":{"id":"n2139858882","loc":[-85.635178,41.9356158]},"n2139858883":{"id":"n2139858883","loc":[-85.63533,41.9355886]},"n2139858884":{"id":"n2139858884","loc":[-85.6353819,41.93556]},"n2139858885":{"id":"n2139858885","loc":[-85.6353665,41.9355157]},"n2139858886":{"id":"n2139858886","loc":[-85.6353165,41.9354971]},"n2139858887":{"id":"n2139858887","loc":[-85.6352454,41.9355328]},"n2139858888":{"id":"n2139858888","loc":[-85.6350184,41.9357846]},"n2139858889":{"id":"n2139858889","loc":[-85.634978,41.9359448]},"n2139858890":{"id":"n2139858890","loc":[-85.6347723,41.9361523]},"n2139858891":{"id":"n2139858891","loc":[-85.6347165,41.9362667]},"n2139858892":{"id":"n2139858892","loc":[-85.6346992,41.9364312]},"n2139858893":{"id":"n2139858893","loc":[-85.634603,41.9366329]},"n2139858894":{"id":"n2139858894","loc":[-85.6345973,41.9367488]},"n2139858895":{"id":"n2139858895","loc":[-85.6345127,41.9369734]},"n2139858896":{"id":"n2139858896","loc":[-85.634478,41.9371923]},"n2139858897":{"id":"n2139858897","loc":[-85.6344838,41.9373768]},"n2139858898":{"id":"n2139858898","loc":[-85.6346242,41.9375299]},"n2139858899":{"id":"n2139858899","loc":[-85.6347723,41.9376357]},"n2139858900":{"id":"n2139858900","loc":[-85.6347607,41.9377788]},"n2139858901":{"id":"n2139858901","loc":[-85.6346204,41.9379533]},"n2139858902":{"id":"n2139858902","loc":[-85.6344184,41.9380105]},"n2139858903":{"id":"n2139858903","loc":[-85.6341627,41.9380406]},"n2139858904":{"id":"n2139858904","loc":[-85.634005,41.9381679]},"n2139858905":{"id":"n2139858905","loc":[-85.63393,41.9383353]},"n2139858906":{"id":"n2139858906","loc":[-85.6338588,41.9384597]},"n2139858907":{"id":"n2139858907","loc":[-85.6336627,41.9387759]},"n2139858908":{"id":"n2139858908","loc":[-85.6335127,41.9389361]},"n2139858933":{"id":"n2139858933","loc":[-85.6353118,41.9432646]},"n2139858934":{"id":"n2139858934","loc":[-85.6353952,41.9433002]},"n2139858935":{"id":"n2139858935","loc":[-85.6356496,41.9433255]},"n2139858936":{"id":"n2139858936","loc":[-85.6363128,41.9433373]},"n2139858937":{"id":"n2139858937","loc":[-85.6365467,41.9433779]},"n2139858938":{"id":"n2139858938","loc":[-85.6368692,41.9435265]},"n2139858939":{"id":"n2139858939","loc":[-85.6370986,41.9437039]},"n2139858940":{"id":"n2139858940","loc":[-85.6372371,41.9437732]},"n2139858941":{"id":"n2139858941","loc":[-85.6374756,41.9438171]},"n2139858942":{"id":"n2139858942","loc":[-85.6376164,41.9439286]},"n2139858943":{"id":"n2139858943","loc":[-85.6377504,41.944138]},"n2139858944":{"id":"n2139858944","loc":[-85.6384204,41.9443137]},"n2139858945":{"id":"n2139858945","loc":[-85.6385726,41.9444506]},"n2139858946":{"id":"n2139858946","loc":[-85.638702,41.9445739]},"n2139858947":{"id":"n2139858947","loc":[-85.6387179,41.9446516]},"n2139858948":{"id":"n2139858948","loc":[-85.6387088,41.9447985]},"n2139858949":{"id":"n2139858949","loc":[-85.6387656,41.9449877]},"n2139858950":{"id":"n2139858950","loc":[-85.638777,41.9451448]},"n2139858951":{"id":"n2139858951","loc":[-85.6387088,41.9452631]},"n2139858964":{"id":"n2139858964","loc":[-85.6383346,41.9442912]},"n2139858966":{"id":"n2139858966","loc":[-85.6384724,41.9443605]},"n2139858967":{"id":"n2139858967","loc":[-85.6354078,41.9434285]},"n2139858968":{"id":"n2139858968","loc":[-85.635271,41.943654]},"n2139858969":{"id":"n2139858969","loc":[-85.6352657,41.9437437]},"n2139858970":{"id":"n2139858970","loc":[-85.635271,41.9438195]},"n2139858971":{"id":"n2139858971","loc":[-85.6351563,41.9438906]},"n2139858972":{"id":"n2139858972","loc":[-85.6351384,41.9438882]},"n2139858973":{"id":"n2139858973","loc":[-85.6351514,41.9438034]},"n2139858974":{"id":"n2139858974","loc":[-85.6351237,41.9436641]},"n2139858975":{"id":"n2139858975","loc":[-85.6351498,41.9436108]},"n2139858976":{"id":"n2139858976","loc":[-85.6351058,41.9435345]},"n2139858977":{"id":"n2139858977","loc":[-85.6349641,41.9432051]},"n2139858986":{"id":"n2139858986","loc":[-85.6341205,41.9380746]},"n2139858990":{"id":"n2139858990","loc":[-85.6345671,41.9381816]},"n2139858995":{"id":"n2139858995","loc":[-85.6339783,41.9382273]},"n2139859003":{"id":"n2139859003","loc":[-85.6340477,41.9373489]},"n2139859004":{"id":"n2139859004","loc":[-85.6339784,41.9374752]},"n2139870406":{"id":"n2139870406","loc":[-85.6342265,41.9432605]},"n2139877106":{"id":"n2139877106","loc":[-85.6346323,41.9438746]},"n2139982399":{"id":"n2139982399","loc":[-85.6324055,41.9408537]},"n2139982400":{"id":"n2139982400","loc":[-85.632488,41.941063],"tags":{"leisure":"slipway"}},"n2139982401":{"id":"n2139982401","loc":[-85.6327261,41.9415366]},"n2139982402":{"id":"n2139982402","loc":[-85.6326391,41.9413598]},"n2139982403":{"id":"n2139982403","loc":[-85.6327041,41.9414391]},"n2139982405":{"id":"n2139982405","loc":[-85.6322891,41.9406009]},"n2139982406":{"id":"n2139982406","loc":[-85.6325412,41.9425257]},"n2139989333":{"id":"n2139989333","loc":[-85.6340584,41.9431731]},"n2140006331":{"id":"n2140006331","loc":[-85.6361751,41.9459744]},"n2140006334":{"id":"n2140006334","loc":[-85.636528,41.9459751]},"n2140006336":{"id":"n2140006336","loc":[-85.6370918,41.9458926]},"n2140006338":{"id":"n2140006338","loc":[-85.6378806,41.9456474]},"n2140006340":{"id":"n2140006340","loc":[-85.6385831,41.9454343]},"n2140006342":{"id":"n2140006342","loc":[-85.639341,41.945157]},"n2140006344":{"id":"n2140006344","loc":[-85.6393497,41.9450232]},"n2140006346":{"id":"n2140006346","loc":[-85.6388245,41.9450145]},"n2140006348":{"id":"n2140006348","loc":[-85.6388167,41.9441739]},"n2140006351":{"id":"n2140006351","loc":[-85.6382915,41.9441797]},"n2140006353":{"id":"n2140006353","loc":[-85.63828,41.9438109]},"n2140006355":{"id":"n2140006355","loc":[-85.6381949,41.9436009]},"n2140006357":{"id":"n2140006357","loc":[-85.6371904,41.9435918]},"n2140006359":{"id":"n2140006359","loc":[-85.6366966,41.9432727]},"n2140006361":{"id":"n2140006361","loc":[-85.6353755,41.9432744]},"n2140006365":{"id":"n2140006365","loc":[-85.6350906,41.9435472]},"n2140006366":{"id":"n2140006366","loc":[-85.6343461,41.9441573]},"n2140006395":{"id":"n2140006395","loc":[-85.6351171,41.9437175]},"n2140006397":{"id":"n2140006397","loc":[-85.635352,41.9450206]},"n2140006399":{"id":"n2140006399","loc":[-85.6358194,41.9454937]},"n2140006401":{"id":"n2140006401","loc":[-85.6348693,41.9445739]},"n2140006431":{"id":"n2140006431","loc":[-85.6376737,41.9438023]},"n2140006437":{"id":"n2140006437","loc":[-85.6382631,41.9442724]},"n2189123379":{"id":"n2189123379","loc":[-85.6342671,41.9352665]},"w203974076":{"id":"w203974076","tags":{"highway":"footway"},"nodes":["n2139870442","n2139870457","n2139870458","n2139870459","n2139870460","n2139870452"]},"w170989131":{"id":"w170989131","tags":{"name":"St Joseph River","waterway":"river"},"nodes":["n1820938225","n1820938712","n1820937596","n1820937574","n1820938515","n1820938330","n1820938678","n1820938240","n1820938950","n1820939226","n1820939575","n1820937913","n1820938223","n1820937668","n1820938545","n1820937584","n1820939742","n1820939727","n1820937578","n1820938149","n1820938124","n1820938888","n1820938898","n1820937922","n1820939543","n1820939370","n1820939401","n1820939647","n1820938345","n1820939644","n1820938333","n1820938370","n1820938624","n1820938493","n1820939559","n1820939763","n1820939237","n1820939416","n1820937810","n1820938317","n1820938324","n1820937558","n1820939556","n1820938298","n1820939348","n1820939125","n1820939081","n1820938859","n1820939126","n1820938881","n1820939439","n1820939324","n1820939128","n1820938101","n1820937706","n1820938382","n1820938776","n1820937815","n1820939177","n1820939688","n1820938952","n1820938216","n1820938387","n1820939333","n1820938243","n1820938248","n1820937666","n1820939051","n1820938332","n1820939438","n1820939407","n1820939361","n1820937517","n1820938770","n1820939591","n1820937857","n1820938491","n1820937993","n1820938125","n1820938166","n1820937746","n1820939028","n1820937638","n1820938676","n1820938843","n1820938844","n1820937978","n1820938730","n1820939544","n1820938304","n1820939123","n1820939494","n1820939450","n1820939555","n1820938133","n1820938129","n1820938871","n1820939408","n1820938669","n1820938260","n1820939740","n1820937625","n1820938631","n1820939651","n1820939613","n1820937850","n1820938325","n1820937736","n1820938804","n1820938837","n1820938014","n1820938991","n1820938722","n1820938935","n1820937870","n1820938432","n1820937986","n1820938756","n1820938966","n1820939159","n1820937744","n1820938334","n1820937645","n1820939394","n1820937656","n1820938392","n1820939703","n1820938385","n1820938947","n1820938854","n1820938428","n1820938488","n1820938269","n1820938668","n1820938268","n1820938707","n1820937732","n1820939144","n1820938481","n1820938771","n1820938686","n1820938948","n1820937997","n1820937769","n1820939003","n1820938083","n1820939011","n1820938803","n1820938700","n1820939723","n1820938808","n1820938262","n1820938081","n1820938926","n1820938326","n1820938102","n1820938508","n1820939590","n1820939199","n1820938084","n1820938870","n1820938895","n1820937611","n1820938918","n1820938514","n1820939610","n1820938910","n1820937523","n1820938127","n1820939108","n1820937981","n1820938938","n1820938715","n1820939016","n1820938237","n1820939623","n1820939602","n1820937734","n1820938977","n1820939633","n1820939156","n1820939406","n1820938279","n1820938301","n1820937678","n1820937671","n1820939163","n1820938356","n1820939372","n1820937568","n1820937626","n1820939710","n1820939004","n1820938253","n1820938571","n1820937513","n1820939412","n1820939701","n1820939509","n1820938839","n1820939731","n1820937798","n1820939676","n1820939724","n1820939243","n1820939704","n1820937814","n1820937599","n1820938199","n1820938995","n1820938445","n1820938069","n1820938470","n1820939074","n1820938193","n1820938740","n1820938047","n1820939507","n1820939441","n1820939160","n1820937849","n1820937840","n1820938052","n1820938988","n1820938796","n1820937724","n1820937620","n1820939304","n1820938343","n1820939649","n1820938875","n1820939686","n1820938476","n1820937801","n1820937737","n1820938264","n1820939609","n1820939496","n1820939593","n1820939566","n1820939661","n1820937782","n1820938912","n1820939173","n1820937733","n1820938953","n1820939603","n1820937607","n1820938468","n1820939601","n1820939694","n1820939133","n1820938897","n1820938893","n1820937831","n1820937730","n1820938820","n1820938046","n1820938426","n1820938347","n1820937582","n1820938954","n1820938033","n1820938104","n1820938680","n1820939563","n1820939404","n1820939714","n1820939000","n1820937992","n1820938168","n1820939510","n1820939500","n1820937509","n1820938865","n1820939773","n1820938138","n1820938905","n1820937623","n1820939418","n1820937946","n1820939577","n1820937615","n1820939687","n1820939119","n1820937988","n1820938337","n1820937750","n1820938703","n1820938339","n1820939044","n1820939770","n1820938913","n1820937672","n1820939722","n1820937768","n1820939597","n1820939612","n1820937699","n1820937682","n1820937669","n1820937657","n1820939363","n1820937800","n1820938265","n1820937760","n1820938207","n1820938115","n1820939130","n1820939716","n1820938338","n1820938239","n1820939040","n1820938064","n1820938855","n1820939015","n1820938258","n1820939042","n1820939043","n1820938443","n1820939725","n1820937675","n1820938568","n1820938280","n1820937705","n1820938775","n1820938636","n1820938626","n1820937859","n1820938096","n1820937852","n1820939039","n1820938247","n1820938585","n1820937707","n1820938117","n1820938909","n1820939115","n1820939335","n1820938805","n1820937935","n1820937876","n1820938699","n1820937869","n1820938603","n1820938100","n1820938500","n1820938283","n1820938275","n1820938923","n1820938365","n1820938349","n1820937804","n1820937903","n1820937608","n1820938688","n1820939671","n1820938092","n1820937820","n1820938753","n1820938922","n1820937990","n1820939682","n1820939738","n1820939600","n1820938167","n1820937726","n1820939702","n1820938209","n1820939456","n1820937837","n1820938222","n1820938902","n1820939162","n1820938965","n1820938461","n1820937681","n1820937514","n1820937764","n1820939719","n1820939697","n1820938899","n1820939093","n1820938702","n1820939595","n1820938749","n1820938348","n1820937606","n1820938675","n1820938830","n1820938737","n1820938758","n1820938716","n1820939107","n1820937863","n1820939033","n1820938163","n1820937867","n1820938819","n1820938034","n1820938252","n1820937563","n1820937868","n1820939032","n1820938632","n1820937982","n1820937943","n1820939568","n1820939541","n1820938215","n1820939097","n1820938812","n1820937518","n1820937952","n1820938711","n1820938736","n1820939066","n1820937591","n1820938082","n1820938108","n1820938496","n1820939410","n1820938949","n1820938327","n1820937708","n1820939023","n1820937772","n1820938256","n1820939083","n1820938378","n1820938961","n1820937610","n1820939717","n1820938695","n1820938590","n1820939655","n1820938341","n1820939054","n1820939157","n1820939674","n1820939684","n1820939511","n1820937631","n1820939458","n1820937830","n1820937709","n1820937779","n1820939749","n1820938880","n1820938856","n1820938557","n1820939557","n1820938249","n1820938818","n1820937594","n1820939114","n1820938416","n1820937508","n1820938990","n1820938201","n1820937759","n1820937987","n1820939164","n1820939753","n1820938187","n1820939067","n1820937586","n1820937941","n1820938121","n1820937807","n1820938521","n1820939726","n1820938244","n1820939014","n1820938741","n1820937629","n1820938664","n1820938747","n1820939082","n1820938709","n1820938320","n1820938270","n1820937619","n1820937777","n1820937718","n1820939138","n1820938056","n1820938155","n1820938596","n1820937775","n1820938437","n1820938128","n1820939581","n1820939145","n1820938546","n1820938184","n1820937601","n1820937794","n1820938539","n1820939645","n1820938438","n1820938436","n1820939025","n1820938915","n1820938534","n1820937605","n1820939607","n1820939101","n1820939580","n1820939268","n1820939134","n1820938849","n1820938754","n1820938079","n1820937842","n1820938781","n1820938873","n1820938495","n1820938381","n1820938503","n1820939436","n1820938502","n1820939087","n1820938996","n1820938449","n1820938907","n1820937979","n1820937780","n1820937546","n1820939699","n1820937677","n1820938957","n1820938946","n1820937776","n1820937717","n1820938718","n1820937637","n1820938510","n1820937663","n1820938941","n1820939151","n1820937603","n1820938250","n1820937951","n1820938630","n1820938821","n1820938779","n1820938497","n1820938159","n1820939536","n1820938409","n1820938386","n1820939116","n1820938340","n1820939117","n1820938291","n1820938435","n1820937819","n1820938242","n1820939078","n1820938877","n1820939104","n1820939445","n1820938367","n1820938903","n1820939420","n1820938517","n1820939508","n1820939542","n1820939326","n1820938210","n1820939020","n1820938815","n1820937832","n1820939513","n1820937818","n1820939005","n1820938717","n1820939135","n1820938384","n1820937587","n1820939024","n1820939504","n1820939120","n1820939026","n1820938015","n1820938998","n1820937648","n1820939137","n1820937761","n1820938195","n1820938535","n1820939550","n1820938725","n1820938282","n1820937781","n1820937792","n1820939705","n1820937788","n1820939707","n1820937882","n1820939632","n1820938427","n1820938276","n1820939617","n1820939013","n1820939035","n1820937543","n1820939365","n1820937752","n1820937802","n1820939183","n1820939670","n1820938450","n1820939375","n1820937813","n1820937673","n1820937783","n1820939029","n1820939768","n1820939377","n1820937974","n1820939244","n1820939642","n1820937864","n1820938255","n1820938528","n1820939666","n1820938120","n1820937812","n1820938928","n1820939750","n1820939099","n1820938073","n1820938714","n1820939140","n1820938192","n1820937844","n1820938635","n1820938742","n1820939583","n1820937887","n1820938318","n1820938816","n1820939698","n1820938273","n1820939181","n1820937652","n1820938748","n1820937651","n1820938519","n1820938019","n1820938752","n1820938235","n1820939118","n1820938562","n1820939314","n1820939570","n1820938190","n1820938342","n1820938533","n1820937977","n1820939089","n1820939146","n1820938622","n1820938297","n1820938524","n1820939283","n1820938874","n1820938832","n1820937550","n1820937843","n1820938638","n1820938116","n1820938206","n1820938319","n1820939053","n1820937845","n1820938093","n1820939217","n1820938997","n1820939355","n1820938861","n1820938726","n1820938057","n1820939373","n1820937862","n1820938518","n1820939072","n1820939680","n1820938444","n1820938217","n1820938506","n1820938393","n1820938492","n1820938852","n1820938221","n1820938773","n1820937684","n1820939060","n1820938224","n1820938203","n1820938840","n1820937525","n1820938147","n1820938433","n1820938188","n1820939359","n1820938750","n1820938016","n1820938768","n1820937621","n1820937799","n1820938951","n1820938721","n1820939037","n1820937866","n1820939715","n1820938063","n1820938446","n1820937627","n1820939624","n1820938431","n1820939721","n1820939622","n1820939239","n1820939263","n1820939648","n1820939640","n1820938867","n1820938757","n1820938439","n1820939352","n1820937740","n1820939329","n1820938229","n1820937583","n1820938180","n1820938366","n1820937767","n1820937758","n1820939374","n1820938869","n1820938292","n1820938400","n1820938399","n1820939734","n1820939289","n1820938944","n1820937755","n1820938759","n1820938434","n1820937600","n1820937825","n1820937670","n1820937793","n1820938011","n1820938246","n1820938956","n1820937770","n1820937757","n1820938059","n1820937860","n1820937569","n1820939266","n1820939685","n1820939672","n1820938606","n1820938772","n1820939038","n1820938211","n1820938359","n1820939619","n1820938708","n1820939512","n1820938065","n1820939233","n1820939739","n1820938786","n1820938879","n1820939147","n1820938563","n1820939148","n1820937839","n1820937659","n1820937786","n1820938419","n1820939565","n1820939402","n1820937710","n1820938254","n1820938271","n1820938390","n1820937680","n1820938140","n1820937817","n1820938218","n1820937985","n1820939235","n1820938441","n1820938401","n1820938719","n1820937795","n1820938971","n1820938460","n1820939759","n1820937972","n1820937841","n1820938462","n1820939320","n1820938978","n1820938360","n1820939713","n1820937676","n1820939712","n1820937939","n1820938080","n1820937754","n1820937753","n1820938530","n1820937886","n1820939689","n1820939124","n1820938697","n1820938789","n1820939105","n1820938860","n1820938853","n1820939400","n1820937561","n1820938404","n1820938774","n1820939316","n1820937696","n1820938782","n1820938975","n1820937564","n1820939730","n1820938257","n1820937853","n1820938487","n1820938848","n1820938906","n1820939230","n1820938424","n1820938051","n1820937771","n1820939587","n1820939149","n1820938792","n1820939041","n1820938934","n1820939777","n1820937515","n1820939058","n1820938312","n1820939264","n1820939631","n1820939109","n1820939403","n1820939664","n1820938724","n1820938929","n1820939399","n1820939776","n1820939369","n1820939185","n1820937701","n1820938126","n1820938336","n1820938219","n1820939080","n1820938642","n1820938043","n1820937725","n1820938548","n1820938552","n1820938035","n1820938684","n1820937778","n1820938764","n1820939021","n1820939346","n1820937712","n1820939761","n1820938397","n1820937747","n1820938566","n1820939161","n1820939090","n1820939752","n1820939271","n1820938878","n1820938110","n1820938346","n1820938499","n1820938151","n1820939538","n1820938281","n1820939153","n1820938551","n1820939285","n1820938197","n1820938408","n1820938482","n1820939036","n1820939579","n1820938489","n1820938483","n1820938189","n1820938123","n1820938087","n1820937741","n1820938485","n1820937590","n1820938972","n1820937773","n1820937520","n1820938872","n1820938131","n1820938452","n1820938328","n1820939620","n1820937641","n1820938353","n1820939693","n1820938705","n1820937640","n1820939189","n1820938144","n1820939774","n1820938694","n1820938238","n1820939397","n1820937917","n1820938454","n1820938567","n1820938979","n1820938060","n1820938204","n1820937828","n1820939232","n1820938806","n1820938857","n1820938078","n1820938105","n1820939228","n1820938604","n1820937763","n1820937854","n1820938289","n1820939736","n1820937937","n1820937714","n1820938278","n1820938058","n1820938706","n1820938989","n1820938313","n1820938520","n1820938288","n1820937689","n1820939537","n1820939531","n1820939019","n1820937527","n1820938455","n1820938814","n1820938045","n1820939627","n1820938213","n1820938161","n1820938331","n1820938024","n1820938220","n1820938062","n1820938178","n1820937796","n1820937644","n1820938490","n1820937589","n1820937879","n1820939614","n1820938882","n1820938039","n1820938538","n1820937667","n1820937719","n1820938561","n1820939658","n1820938783","n1820938601","n1820938198","n1820938388","n1820938969","n1820937687","n1820939086","n1820939665","n1820939187","n1820938498","n1820938261","n1820937983","n1820938068","n1820938136","n1820939061","n1820938137","n1820938186","n1820939071","n1820937592","n1820939669","n1820937553","n1820939357","n1820938727","n1820939371","n1820939112","n1820939079","n1820938743","n1820938467","n1820938834","n1820938022","n1820938537","n1820938122","n1820938516","n1820937614","n1820937612","n1820939469","n1820939636","n1820939050","n1820939552","n1820938157","n1820938663","n1820938955","n1820939091","n1820938430","n1820938471","n1820937809","n1820938074","n1820938208","n1820938914","n1820938858","n1820938417","n1820937531","n1820938107","n1820939100","n1820938751","n1820937711","n1820938824","n1820939745","n1820937572","n1820938602","n1820938212","n1820938097","n1820937921","n1820938090","n1820938511","n1820938876","n1820939762","n1820938234","n1820938048","n1820937774","n1820937856","n1820937749","n1820937765","n1820938286","n1820939095","n1820938480","n1820939229","n1820938277","n1820937617","n1820938311","n1820937622","n1820939196","n1820937690","n1820939006","n1820939287","n1820939131","n1820938106","n1820938784","n1820938335","n1820938095","n1820938182","n1820937715","n1820937683","n1820938070","n1820939605","n1820938527","n1820938763","n1820938398","n1820937686","n1820939621","n1820937664","n1820939277","n1820938565","n1820939539","n1820938099","n1820939646","n1820938556","n1820937548","n1820938729","n1820939336","n1820938259","n1820938728","n1820938361","n1820937643","n1820938644","n1820939007","n1820939690","n1820939227","n1820937635","n1820937950","n1820938682","n1820939150","n1820939012","n1820939261","n1820939111","n1820937805","n1820939691","n1820939677","n1820937628","n1820937811","n1820938790","n1820938251","n1820938226","n1820938942","n1820937633","n1820937984","n1820937751","n1820939673","n1820938970","n1820938415","n1820938597","n1820938309","n1820938111","n1820938472","n1820938894","n1820938402","n1820937593","n1820938570","n1820939102","n1820939775","n1820937948","n1820939121","n1820937511","n1820938787","n1820939720","n1820939075","n1820937880","n1820937742","n1820937721","n1820939535","n1820938486","n1820938354","n1820937632","n1820939010","n1820938885","n1820938089","n1820937613","n1820938442","n1820938245","n1820938272","n1820937566","n1820938295","n1820938532","n1820938883","n1820937713","n1820937674","n1820939635","n1820938448","n1820938355","n1820938587","n1820938559","n1820937787","n1820939301","n1820937723","n1820939056","n1820937560","n1820938323","n1820938230","n1820938453","n1820938377","n1820938357","n1820939637","n1820938017","n1820939540","n1820939376","n1820937639","n1820937642","n1820938075","n1820938351","n1820938766","n1820937897","n1820938973","n1820938066","n1820939547","n1820939652","n1820937944","n1820937748","n1820939234","n1820939193","n1820937891","n1820938785","n1820939132","n1820938523","n1820938884","n1820938411","n1820939554","n1820938791","n1820937655","n1820938368","n1820939152","n1820938030","n1820938447","n1820937580","n1820939628","n1820937588","n1820937894","n1820939201","n1820938086","n1820937650","n1820938379","n1820939008","n1820938999","n1820937524","n1820937872","n1820938389","n1820939197","n1820938422","n1820938936","n1820939262","n1820937634","n1820938583","n1820939589","n1820937901","n1820939034","n1820939065","n1820938290","n1820939195","n1820938228","n1820937884","n1820938797","n1820938191","n1820939191","n1820939198","n1820937892","n1820939679","n1820938507","n1820937647","n1820937909","n1820938542","n1820939598","n1820937851","n1820939084","n1820939728","n1820937688","n1820938263","n1820938670","n1820937762","n1820939310","n1820938925","n1820938862","n1820938822","n1820938547","n1820937731","n1820938594","n1820938592","n1820938214","n1820938284","n1820937835","n1820938599","n1820939437","n1820937834","n1820937576","n1820937692","n1820939586","n1820939546","n1820938403","n1820937970","n1820939561","n1820938098","n1820938851","n1820938477","n1820938892","n1820939045","n1820939758","n1820939350","n1820938321","n1820938440","n1820938595","n1820938364","n1820938962","n1820938118","n1820939678","n1820938406","n1820938549","n1820937555","n1820938823","n1820937521","n1820939471","n1820939487","n1820938799","n1820938605","n1820937928","n1820938373","n1820939747","n1820939629","n1820937557","n1820937526","n1820938958","n1820938833","n1820937636","n1820938967","n1820938760","n1820938842","n1820938067","n1820939077","n1820939224","n1820938185","n1820939110","n1820938372","n1820939757","n1820939063","n1820939660","n1820938813","n1820937528","n1820938369","n1820938896","n1820939551","n1820939683","n1820937660","n1820937873","n1820938810","n1820938478","n1820939662","n1820937595","n1820939052","n1820938113","n1820939070","n1820938733","n1820937878","n1820938300","n1820939760","n1820939718","n1820937646","n1820939057","n1820939443","n1914861306","n1820938013","n1820937529","n1820939764","n1820938826","n1820937885","n1820939588","n1820937865","n1820937833","n1914861112","n1820938761","n1914861007","n1820937905","n1820938541","n1820939092","n1914861057","n1820938153","n1820938267","n1820939265","n1820938085","n1820939018","n1820939755","n1820938474","n1820939027","n1820938593","n1820938202","n1820939599","n1820939695","n1820938077","n1820938012","n1820939545","n1820939596","n1820939337","n1820938227","n1820937698","n1820938475","n1820939465","n1820938165","n1820938698","n1820938525","n1820938529","n1820938553","n1820938940","n1820939498","n1820938501","n1820939533","n1820938924","n1820939634","n1820939220","n1820939657","n1820938887","n1820938838","n1820938114","n1820937823","n1820938778","n1820938801","n1820939096","n1820938981","n1820937953","n1820938732","n1820938980","n1820938960","n1820937949","n1820938026","n1820939273","n1841425201","n1820938629","n1820938864","n1820938554","n1820938088","n1820937685","n1841425222","n1820939729","n1820937665","n1820937838","n1820937739","n1820938780","n1820937821","n1820938825","n1820939055","n1820939485","n1820938041","n1820938746","n1820939562","n1820938459","n1820939489","n1820938050","n1820937980","n1820937695","n1820938413","n1820938555","n1820937703","n1820938536","n1820938196","n1820938287","n1820938169","n1820939279","n1820938531","n1820938959","n1820939741","n1820938665","n1820938963","n1820939611","n1820937653","n1820939618","n1820939492","n1820938600","n1820938628","n1820939312","n1820939616","n1820937738","n1820939001","n1820939062","n1820938794","n1820938558","n1820937822","n1820937532","n1820939073","n1820938200","n1820938241","n1820938968","n1820938927","n1820938306","n1820937630","n1820938456","n1820937694","n1820938908","n1820939076","n1820937522","n1820939659","n1820938522","n1820939318","n1820938932","n1820938841","n1820937579","n1820937540","n1820938560","n1821139530","n1820938964","n1820937662","n1820939281","n1821139533","n1820937797","n1821139532","n1820939751","n1821139531","n1820939291","n1820938420","n1820939696","n1820938904","n1820938484","n1820939448","n1820939009","n1820938735","n1820938986","n1820938937","n1820939030","n1820938734","n1820938745","n1820939106","n1820938987","n1820937858","n1820938673","n1820938620","n1820937808","n1820937700","n1820939573","n1820938540","n1820937661","n1820937570","n1820938396","n1820937875","n1820939048","n1820938233","n1820938793","n1820939584","n1820938412","n1820938394","n1820937846","n1820938800","n1820938690","n1820939331","n1820939630","n1820938762","n1820938710","n1820939322","n1820938992","n1821137608","n1821137607","n1820937924","n1820939139","n1820939463","n1820939574","n1820938294","n1820938071","n1820938307","n1820938061","n1820939260","n1820937899","n1820938310","n1820938983","n1820937530","n1820938993","n1820938890","n1820937915","n1820938231","n1820938040","n1820938920","n1820939585","n1820938135","n1820939700","n1820937824","n1820939667","n1820937930","n1820938134","n1820937551","n1820939405","n1820938232","n1820937716","n1820937848","n1820939765","n1820939068","n1820939766","n1820937933","n1820937720","n1820939222","n1820939772","n1820939022","n1820939732","n1820937702","n1820937691","n1820938945","n1820937756","n1820938451","n1820938410","n1820938798","n1820937945","n1820937654","n1820938598","n1820938836","n1820937571","n1820937556","n1820938994","n1820938919","n1820938863","n1820939064","n1820938018","n1820937658","n1820937537","n1820938142","n1820938666","n1820937535","n1820939571","n1820938465","n1820939638","n1820937533","n1820939656","n1820939422","n1820938109","n1820938405","n1820938028","n1820937649","n1820938829","n1820939031","n1820939155","n1820938350","n1820938463","n1820938425","n1820939047","n1820938831","n1820938494","n1820937697","n1820938504","n1820938900","n1820937784","n1820938414","n1820938076","n1820938723","n1820937722","n1820938739","n1820937791","n1820938985","n1820938352","n1820938293","n1820938274","n1820939692","n1820937871","n1820939059","n1820938868","n1820937877","n1820937743","n1820938429","n1820937545","n1820937575","n1820938302","n1820938505","n1820938916","n1820938374","n1820938329","n1820937790","n1820939735","n1820938930","n1820937995","n1820938512","n1820938130","n1820938194","n1820938671","n1820938802","n1820937542","n1820937602","n1820939069","n1820938901","n1820939654","n1820937727","n1820939569","n1820938375","n1820939306","n1820938479","n1820938376","n1820938667","n1820937766","n1820939467","n1820939567","n1820937806","n1820938943","n1820938931","n1820937745","n1820939452","n1820938738","n1820938053","n1820939653","n1820938640","n1820937604","n1820937536","n1820938701","n1820939625","n1820939744","n1820939572","n1820937577","n1820937541","n1820938891","n1820937597","n1820938469","n1820939194","n1820937539","n1820938911","n1820939017","n1820939650","n1820939103","n1820939578","n1820938132","n1820937549","n1820938634","n1820939743","n1820937544","n1820937826","n1820937598","n1820937547","n1820938032","n1820939142"]},"w17963021":{"id":"w17963021","tags":{"highway":"residential"},"nodes":["n185948706","n185948708","n185948710"]},"w203974069":{"id":"w203974069","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870431","n2139870432","n2139870433","n2139870434","n2139870431"]},"w209816575":{"id":"w209816575","tags":{"area":"yes","building":"yes"},"nodes":["n2199856288","n2199856289","n2199856290","n2199856291","n2199856292","n2199856293","n2199856294","n2199856295","n2199856296","n2199856297","n2199856298","n2199856299","n2199856300","n2199856301","n2199856302","n2199856303","n2199856288"]},"w203841838":{"id":"w203841838","tags":{"area":"yes","natural":"water"},"nodes":["n2138493826","n2138493827","n2138493828","n2138493829","n2138493830","n2138493831","n2138493833","n2138493832","n2138493826"]},"w203972937":{"id":"w203972937","tags":{"highway":"path","name":"Riverwalk Trail","surface":"asphalt","width":"3"},"nodes":["n2139858882","n2139858883","n2139858884","n2139858885","n2139858886","n2139858887","n2139858882","n2139858888","n2139858889","n2139858890","n2139858891","n2139858892","n2139858893","n2139858894","n2139858895","n2139858896","n2139858897","n2139858898","n2139858899","n2139858900","n2139858901","n2139858902","n2139858903","n2139858986","n2139858904","n2139858995","n2139858905","n2139858906","n2139858907","n2139858908","n2139858909","n2139858910","n2139858911","n2139858912","n2139858913","n2139858914","n2139858915","n2139858916","n2139858917","n2139858918","n2139858919","n2139858920","n2139858921","n2139858922","n2139858923","n2139858924","n2139858925","n2139858926","n2139858927","n2139858982","n2139858928","n2139858929","n2139858930","n2139858931","n2139858932","n2139858981","n2139858933","n2139858934","n2139858935","n2139858936","n2139858937","n2139858938","n2139858939","n2139858940","n2139858941","n2139858942","n2139858943","n2140006437","n2139858964","n2139858944","n2139858966","n2139858945","n2139858946","n2139858947","n2139858948","n2139858949","n2139858950","n2139858951"]},"w17964015":{"id":"w17964015","tags":{"highway":"residential"},"nodes":["n185954680","n185954683","n185954685","n185954687","n185954689","n185954690","n185954691","n2139870379","n2139870456","n185954692","n185954693","n185954695"]},"w17967315":{"id":"w17967315","tags":{"highway":"residential","name":"South Andrews Street"},"nodes":["n185981999","n185974477","n185964963"]},"w203974071":{"id":"w203974071","tags":{"highway":"footway"},"nodes":["n2139870439","n2139870440","n2139870441","n2139870442","n2139870443","n2139870444","n2139870445","n2139870446","n2139870447","n2139870448","n2139870449"]},"w170848824":{"id":"w170848824","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819858503","n1819858531","n1819858526","n1819858518","n1819858505","n1819858508","n1819858512","n1819858514","n1819858528","n1819858509","n1819858511","n1819858507","n1819858521"]},"w203986458":{"id":"w203986458","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989357","n2139989359","n2139989360","n2139989362","n2139989357"]},"w170844917":{"id":"w170844917","tags":{"waterway":"riverbank"},"nodes":["n1819805911","n1819805690","n1819805812","n1819805766","n1819805802","n1819805885","n1819805626","n1819805842","n1819805715","n1819805694","n1819805618","n1819805629","n1819805731","n1819805636","n1819805878","n1819805718","n1819805798","n1819849057","n1819805666","n1819805852","n1819805805","n1819805789","n1819805868","n1819805680","n1819805918","n1819848888","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n1819858521","n1819805854","n1819805876","n1819805864","n1819805922","n2139859004","n1819805702","n2139859003","n1819805614","n1819805792","n1819805786","n1819805777","n1819805645","n1819805838","n1819805889","n1819805795","n1819805707","n1819805774","n1819805808","n1819805810","n1819805724","n1819805676","n1819805728","n1819805783","n1819805687","n1819805727","n2189123379","n1819805632","n1819805641","n1819805760","n1819805887","n1819805861","n1819805722","n1819805880","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n1819805698","n1819805647","n1819805870","n1819805683","n1819805622","n1819805639","n1819805858","n1819805643","n1819805673","n1819805925","n1819805849","n1819805711","n1819805846","n1819805669","n1819805883","n1819805814","n1819805873","n1819805911"]},"w17967326":{"id":"w17967326","tags":{"highway":"residential","name":"North Constantine Street"},"nodes":["n185985217","n185985219","n185985221","n185985222","n185985223","n185985225","n2140006431","n185985227","n185985229","n185985231","n185985233","n185985235","n185985238","n185985240","n2140018998","n185964965"]},"w134150789":{"id":"w134150789","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964971","n2139870406","n185964972"]},"w17966400":{"id":"w17966400","tags":{"highway":"tertiary","name":"South Constantine Street"},"nodes":["n185958672","n185964965"]},"w203974066":{"id":"w203974066","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870417","n2139870418","n2139870420","n2139870419"]},"w17965998":{"id":"w17965998","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972775","n185972777","n185972779","n185972781","n185972783","n185972785","n185972787","n185972788","n185972789","n185972790","n185972791","n185972793","n185972795","n185972797","n185972798","n185972800","n185972802","n185972805","n185972807","n185972809","n185972811","n185972813","n185972814","n185972815","n185972816","n185972817","n185972819","n185972821","n185972824","n185972826","n185972830","n185972832","n185972834","n185972835","n185972836","n185972839","n185990434","n2114807572","n2114807568","n185972845","n2114807583","n185972847","n185972849","n185972851","n2114807578","n1475293254","n2114807593","n1475293226","n185972862","n2114807565","n185951869","n1475293234","n1475293252","n185972868","n1475293264","n1475293222","n185972878","n1475293261","n185972882","n185972885","n1475293260","n1475293240","n185972891","n185972895","n185972897","n185972899","n2130304159","n1475284023","n185972903"]},"w134150795":{"id":"w134150795","tags":{"bridge":"yes","highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964970","n185964971"]},"w203974067":{"id":"w203974067","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870420","n2139870421"]},"w170995908":{"id":"w170995908","tags":{"highway":"residential","name":"Thomas Street"},"nodes":["n1821006702","n1821006700","n1821006698","n2139858990","n1821006716","n1821006725","n1821006712","n1821006704","n1821006708","n1821006710","n1821006706"]},"w17965834":{"id":"w17965834","tags":{"highway":"residential","name":"Spring Street"},"nodes":["n185971361","n185971364","n185971366","n185971368","n185954695","n185964968"]},"w203974070":{"id":"w203974070","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870435","n2139870436","n2139870437","n2139870438","n2139870435"]},"w203989879":{"id":"w203989879","tags":{"highway":"service"},"nodes":["n2140018998","n2140018999","n2140019000"]},"w203974062":{"id":"w203974062","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870387","n2139870388","n2139870389","n2139870390","n2139870391","n2139870392","n2139870397","n2139870393","n2139870396","n2139870395","n2139870394","n2139870387"]},"w203974061":{"id":"w203974061","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2139870382","n2139870383"]},"w203049587":{"id":"w203049587","tags":{"area":"yes","name":"Scidmore Park Petting Zoo","tourism":"zoo","zoo":"petting_zoo"},"nodes":["n2130304133","n2130304136","n2130304138","n2130304140","n2130304142","n2130304144","n2130304146","n2130304147","n2130304148","n2130304149","n2130304150","n2130304151","n2130304133"]},"w203972941":{"id":"w203972941","tags":{"highway":"path"},"nodes":["n2139858982","n2139858983","n2139858984","n2139858985","n2139858927"]},"w203974065":{"id":"w203974065","tags":{"highway":"service"},"nodes":["n2139870406","n2139870407","n2139870408","n2139870417","n2139870409","n2139870410","n2139870411","n2139870412","n2139870426","n2139870413","n2139870414","n2139870415","n2139870419","n2139870416","n2139870421","n2139870408"]},"w203972940":{"id":"w203972940","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858934","n2139858967","n2139858968","n2139858969","n2139858970","n2139858971","n2139858972","n2139858973","n2139858974","n2139858975","n2139858976","n2139858977","n2139858978","n2139858979","n2139858980","n2139858981"]},"w203974072":{"id":"w203974072","tags":{"highway":"footway"},"nodes":["n2139858925","n2139870450","n2139870453","n2139870451","n2139870452","n2139870441"]},"w203974074":{"id":"w203974074","tags":{"highway":"footway"},"nodes":["n2139870454","n2139870456","n2139870429"]},"w203974060":{"id":"w203974060","tags":{"highway":"footway"},"nodes":["n2139870383","n2139870384","n2139870422","n2139870385","n2139870386","n2139870388"]},"w203841837":{"id":"w203841837","tags":{"area":"yes","natural":"water"},"nodes":["n2138493807","n2138493808","n2138493809","n2138493810","n2138493811","n2138493812","n2138493813","n2138493814","n2138493815","n2138493816","n2138493825","n2138493817","n2138493824","n2138493818","n2138493819","n2138493820","n2138493821","n2138493822","n2138493823","n2138493807"]},"w134150845":{"id":"w134150845","tags":{"bridge":"yes","name":"Conrail Railroad","railway":"rail"},"nodes":["n185972903","n185972905"]},"w203974059":{"id":"w203974059","tags":{"highway":"footway"},"nodes":["n2139870430","n2139870439","n2139870429","n2139870428","n2139870379","n2139870455","n2139870380","n2139870381","n2139858925","n2139870382"]},"w203986457":{"id":"w203986457","tags":{"area":"yes","leisure":"park","name":"Scidmore Park"},"nodes":["n2139989333","n2139989335","n2139989337","n2139989339","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n2139989341","n2139989344","n2139989346","n2139989348","n2139989350","n2139989351","n2139989353","n2139989355","n2139989333"]},"w170848331":{"id":"w170848331","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819848937","n1819849104","n1819849076","n1819849183","n1819848928","n1819848972","n1819848948","n1819848971","n1819848859","n1819849008","n1819848889","n1819849026","n1819849094","n1819849083","n1819849079","n1819849187","n1819848992","n1819849060","n1819849056","n1819849071","n1819849067","n1819849048","n1819849036","n1819849150","n1819849075","n1819849051","n1819849062","n1819848926","n1819849035","n1819848987","n1819849012","n1819848933","n1819848996","n1819848990","n1819849005","n1819849021","n1819848892","n1819849092","n1819848863","n1819848922","n1819848858","n1819848855","n1819848974","n1819848953","n1819849019","n1819849049","n1819848979","n1819849140","n1819849193","n1819849147","n1819849151","n1819849163","n1819849023","n1819848878","n1819849004","n1819848857","n1819848879","n1819849041","n1819849165","n1819849107","n1819849156","n1819848934","n1819848914","n1819848955","n1819848931","n1819848927","n1819849084","n1819849169","n1819849045","n1819848945","n1819849095","n1819848924","n1819849171","n1819849141","n1819849046","n1819849197","n1819849011","n1819849108","n1819849158","n1819849160","n1819848870","n1819849006","n1819849157","n1819848993","n1819848970","n1819849202","n1819848903","n1819848975","n1819848849","n1819849025","n1819849105","n1819849033","n1819849176","n1819849099","n1819849086","n1819848960","n1819848961","n1819849001","n1819848980","n1819849038","n1819848854","n1819849127","n1819849170","n1819849139","n1819848873","n1819848929","n1819849201","n1819849121","n1819849031","n1819849131","n1819848875","n1819849080","n1819849066","n1819849081","n1819849096","n1819849172","n1819849114","n1819849182","n1819848905","n1819849054","n1819848920","n1819848851","n1819848968","n1819848917","n1819849111","n1819849119","n1819849074","n1819848893","n1819849129","n1819848850","n1819848956","n1819849154","n1819848877","n1819848986","n1819849191","n1819848952","n1819848954","n1819848942","n1819849028","n1819849195","n1819848938","n1819848962","n1819849070","n1819849034","n1819849052","n1819849059","n1819848916","n1819849162","n1819849167","n1819849093","n1819849030","n1819849002","n1819849161","n1819848886","n1819848958","n1819849064","n1819849112","n1819849148","n1819848856","n1819848976","n1819848977","n1819849144","n1819848918","n1819849200","n1819848919","n1819849042","n1819849166","n1819849186","n1819849152","n1819849058","n1819849185","n1819849199","n1819849053","n1819849194","n1819849068","n1819849146","n1819849174","n1819848967","n1819848932","n1819849155","n1819849198","n1819848964","n1819848894","n1819848969","n1819849184","n1819849055","n1819849179","n1819848865","n1819848860","n1819849082","n1819848966","n1819849040","n1819849069","n1819849078","n1819849077","n1819848904","n1819848959","n1819849133","n1819849089","n1819849000","n1819849124","n1819849032","n1819849097","n1819848939","n1819849072","n1819848915","n1819849196","n1819848946","n1819849047","n1819849029","n1819849164","n1819848994","n1819849022","n1819858513","n1819849126","n1819849063","n1819848941","n1819849085","n1819848871","n1819848943","n1819849192","n1819858501","n1819849159","n1819858523","n1819848901","n1819849189","n1819858503","n1819849065","n2139877106","n1819848909","n1819848930","n1819848888"]},"w17967397":{"id":"w17967397","tags":{"highway":"residential","name":"North Andrews Street"},"nodes":["n185964963","n185985217"]},"w17964497":{"id":"w17964497","tags":{"highway":"tertiary","name":"Constantine St"},"nodes":["n185958643","n185958645","n2139795852","n185958647","n185958649","n185958651","n185958653","n185958656","n185958658","n185958660","n185958662","n185958664","n185958666","n185958668","n185958670","n185948710","n185958672"]},"w203974068":{"id":"w203974068","tags":{"highway":"footway"},"nodes":["n2139870422","n2139870423","n2139870424","n2139870425","n2139870426","n2139870427"]},"w203974063":{"id":"w203974063","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870398","n2139870399","n2139870400","n2139870401","n2139870398"]},"w203986459":{"id":"w203986459","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989364","n2139989366","n2139989368","n2139989370","n2139989364"]},"w203988286":{"id":"w203988286","tags":{"area":"yes","leisure":"park","name":"Memory Isle Park"},"nodes":["n2140006331","n2140006334","n2140006336","n2140006338","n2140006340","n2140006342","n2140006344","n2140006346","n2140006348","n2140006351","n2140006353","n2140006355","n2140006357","n2140006359","n2140006361","n2140006363","n2140006364","n2140006365","n2140006395","n2140006366","n2140006401","n2140006397","n2140006399","n2140006331"]},"w203974073":{"id":"w203974073","tags":{"highway":"footway"},"nodes":["n2139870453","n2139870454","n2139870455"]},"w203974064":{"id":"w203974064","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870402","n2139870403","n2139870404","n2139870405","n2139870402"]},"n185966959":{"id":"n185966959","loc":[-85.642185,41.946411]},"n1475283980":{"id":"n1475283980","loc":[-85.6398249,41.9451425]},"n1475284013":{"id":"n1475284013","loc":[-85.6396448,41.9451666]},"n1475284042":{"id":"n1475284042","loc":[-85.6386382,41.9454789]},"n185975925":{"id":"n185975925","loc":[-85.6393332,41.9452388]},"n185975919":{"id":"n185975919","loc":[-85.6391279,41.9453044]},"n185975917":{"id":"n185975917","loc":[-85.6389034,41.9453872]},"n2140006369":{"id":"n2140006369","loc":[-85.6386163,41.9451631]},"n2140006370":{"id":"n2140006370","loc":[-85.6385144,41.9449357]},"n2140006417":{"id":"n2140006417","loc":[-85.6385785,41.9450299]},"n2140006419":{"id":"n2140006419","loc":[-85.6385781,41.9452152]},"n2189123361":{"id":"n2189123361","loc":[-85.6404948,41.947015]},"n2189123363":{"id":"n2189123363","loc":[-85.6395765,41.946495]},"n2189123365":{"id":"n2189123365","loc":[-85.6389347,41.9460875]},"n185966962":{"id":"n185966962","loc":[-85.644417,41.946364]},"n185975911":{"id":"n185975911","loc":[-85.637532,41.9458276]},"n185975913":{"id":"n185975913","loc":[-85.6376323,41.9457936]},"n185975915":{"id":"n185975915","loc":[-85.6383596,41.9455425]},"n185975932":{"id":"n185975932","loc":[-85.644403,41.945088]},"n185975934":{"id":"n185975934","loc":[-85.645486,41.945084]},"n185979974":{"id":"n185979974","loc":[-85.644381,41.943831]},"n2139795809":{"id":"n2139795809","loc":[-85.6464756,41.9450813]},"n2139795810":{"id":"n2139795810","loc":[-85.6466646,41.945174]},"n2139858952":{"id":"n2139858952","loc":[-85.6383567,41.9454039]},"n2139858953":{"id":"n2139858953","loc":[-85.6380506,41.9455301]},"n2139858954":{"id":"n2139858954","loc":[-85.6377321,41.9455546]},"n2139858955":{"id":"n2139858955","loc":[-85.6376571,41.9455245]},"n2139858956":{"id":"n2139858956","loc":[-85.6375859,41.9454544]},"n2139858957":{"id":"n2139858957","loc":[-85.6376686,41.9453185]},"n2139858958":{"id":"n2139858958","loc":[-85.6378936,41.9451712]},"n2139858959":{"id":"n2139858959","loc":[-85.6379225,41.9450825]},"n2139858960":{"id":"n2139858960","loc":[-85.6379302,41.9447564]},"n2139858961":{"id":"n2139858961","loc":[-85.6379763,41.9446963]},"n2139858962":{"id":"n2139858962","loc":[-85.6380436,41.9446706]},"n2139858963":{"id":"n2139858963","loc":[-85.6381286,41.9445969]},"n2139858965":{"id":"n2139858965","loc":[-85.6382523,41.9444134]},"n2140006367":{"id":"n2140006367","loc":[-85.6380923,41.9454418]},"n2140006368":{"id":"n2140006368","loc":[-85.6384089,41.9453146]},"n2140006372":{"id":"n2140006372","loc":[-85.6383252,41.9447706]},"n2140006374":{"id":"n2140006374","loc":[-85.6381033,41.9447436]},"n2140006376":{"id":"n2140006376","loc":[-85.6379759,41.9447815]},"n2140006378":{"id":"n2140006378","loc":[-85.6379832,41.9448654]},"n2140006380":{"id":"n2140006380","loc":[-85.6380632,41.9450738]},"n2140006382":{"id":"n2140006382","loc":[-85.6380414,41.9452064]},"n2140006389":{"id":"n2140006389","loc":[-85.6379068,41.9453092]},"n2140006391":{"id":"n2140006391","loc":[-85.637925,41.9453904]},"n2140006393":{"id":"n2140006393","loc":[-85.6379977,41.94545]},"n2189123275":{"id":"n2189123275","loc":[-85.6371346,41.9462544]},"n2189123278":{"id":"n2189123278","loc":[-85.6368371,41.9466153]},"n2189123280":{"id":"n2189123280","loc":[-85.6379537,41.9489088]},"n2189123282":{"id":"n2189123282","loc":[-85.6383816,41.9497858]},"n2189123285":{"id":"n2189123285","loc":[-85.6393673,41.9512417]},"n2189123287":{"id":"n2189123287","loc":[-85.640554,41.9517766]},"n2189123289":{"id":"n2189123289","loc":[-85.6411,41.9522344]},"n2189123291":{"id":"n2189123291","loc":[-85.6417418,41.9526574]},"n2189123293":{"id":"n2189123293","loc":[-85.642321,41.9529407]},"n2189123295":{"id":"n2189123295","loc":[-85.6427697,41.9532278]},"n2189123297":{"id":"n2189123297","loc":[-85.6433332,41.9538254]},"n2189123300":{"id":"n2189123300","loc":[-85.6435785,41.9543648]},"n2189123301":{"id":"n2189123301","loc":[-85.6444394,41.9541048]},"n2189123303":{"id":"n2189123303","loc":[-85.6450603,41.954]},"n2189123312":{"id":"n2189123312","loc":[-85.6454829,41.9539108]},"n2189123314":{"id":"n2189123314","loc":[-85.6460464,41.9538526]},"n2189123315":{"id":"n2189123315","loc":[-85.6463178,41.9537167]},"n2189123316":{"id":"n2189123316","loc":[-85.646276,41.9534141]},"n2189123317":{"id":"n2189123317","loc":[-85.6459995,41.9531541]},"n2189123318":{"id":"n2189123318","loc":[-85.645222,41.9531929]},"n2189123319":{"id":"n2189123319","loc":[-85.6447316,41.9531813]},"n2189123320":{"id":"n2189123320","loc":[-85.6440637,41.9532977]},"n2189123321":{"id":"n2189123321","loc":[-85.6438185,41.9531774]},"n2189123322":{"id":"n2189123322","loc":[-85.6440011,41.9528398]},"n2189123323":{"id":"n2189123323","loc":[-85.6442672,41.9525914]},"n2189123324":{"id":"n2189123324","loc":[-85.6442881,41.9523276]},"n2189123326":{"id":"n2189123326","loc":[-85.644262,41.952153]},"n2189123328":{"id":"n2189123328","loc":[-85.6441681,41.9520404]},"n2189123330":{"id":"n2189123330","loc":[-85.6442098,41.9517494]},"n2189123333":{"id":"n2189123333","loc":[-85.6438498,41.9515864]},"n2189123336":{"id":"n2189123336","loc":[-85.6435889,41.9513225]},"n2189123339":{"id":"n2189123339","loc":[-85.6425349,41.9510315]},"n2189123342":{"id":"n2189123342","loc":[-85.6422688,41.9508802]},"n2189123345":{"id":"n2189123345","loc":[-85.6418775,41.9508142]},"n2189123348":{"id":"n2189123348","loc":[-85.6415488,41.9508064]},"n2189123351":{"id":"n2189123351","loc":[-85.6411027,41.9505488]},"n2189123353":{"id":"n2189123353","loc":[-85.6410374,41.9498208]},"n2189123355":{"id":"n2189123355","loc":[-85.6410061,41.9494327]},"n2189123357":{"id":"n2189123357","loc":[-85.6411522,41.9482569]},"n2189123359":{"id":"n2189123359","loc":[-85.6410548,41.9473036]},"n2189123368":{"id":"n2189123368","loc":[-85.6380216,41.9458974]},"n2189123370":{"id":"n2189123370","loc":[-85.6386721,41.9507782]},"w17968193":{"id":"w17968193","tags":{"highway":"residential","name":"French St"},"nodes":["n185970906","n185982877","n185967774","n185985823","n185979974"]},"w203972939":{"id":"w203972939","tags":{"highway":"path"},"nodes":["n2139858965","n2139858966"]},"w203988289":{"id":"w203988289","tags":{"area":"yes","natural":"water"},"nodes":["n2140006367","n2140006368","n2140006419","n2140006369","n2140006417","n2140006370","n2140006372","n2140006374","n2140006376","n2140006378","n2140006380","n2140006382","n2140006389","n2140006391","n2140006393","n2140006367"]},"w208640157":{"id":"w208640157","tags":{"area":"yes","natural":"wetland"},"nodes":["n1819849029","n2189123275","n2189123278","n2189123280","n2189123282","n2189123370","n2189123285","n2189123287","n2189123289","n2189123291","n2189123293","n2189123295","n2189123297","n2189123300","n2189123301","n2189123303","n2189123312","n2189123314","n2189123315","n2189123316","n2189123317","n2189123318","n2189123319","n2189123320","n2189123321","n2189123322","n2189123323","n2189123324","n2189123326","n2189123328","n2189123330","n2189123333","n2189123336","n2189123339","n2189123342","n2189123345","n2189123348","n2189123351","n2189123353","n2189123355","n2189123357","n2189123359","n2189123361","n2189123363","n2189123365","n2189123368","n1819849029"]},"w17966281":{"id":"w17966281","tags":{"highway":"residential","name":"Pealer St"},"nodes":["n185975911","n185975913","n185975915","n1475284042","n185975917","n185975919","n185975925","n185970909","n1475284013","n1475283980","n185975928","n185967775","n185975930","n185975932","n185975934","n2139795809","n2139795810"]},"w17965353":{"id":"w17965353","tags":{"highway":"residential","name":"Yauney St"},"nodes":["n185966958","n185966959","n185966960","n185966962"]},"w203972938":{"id":"w203972938","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858964","n2139858965","n2139858963","n2139858962","n2139858961","n2139858960","n2139858959","n2139858958","n2139858957","n2139858956","n2139858955","n2139858954","n2139858953","n2139858952","n2139858951"]},"n354002665":{"id":"n354002665","loc":[-85.6366599,41.9444923],"tags":{"name":"Memory Isle","place":"island"}},"n354031301":{"id":"n354031301","loc":[-85.635,41.9463889],"tags":{"amenity":"post_office","name":"Three Rivers Post Office"}},"n185963454":{"id":"n185963454","loc":[-85.633686,41.946072]},"n185963455":{"id":"n185963455","loc":[-85.633815,41.946131]},"n185963456":{"id":"n185963456","loc":[-85.633951,41.946174]},"n185978375":{"id":"n185978375","loc":[-85.634385,41.94559]},"n185978377":{"id":"n185978377","loc":[-85.634544,41.945725]},"n185978379":{"id":"n185978379","loc":[-85.634573,41.945764]},"n185978381":{"id":"n185978381","loc":[-85.634616,41.945849]},"n185978383":{"id":"n185978383","loc":[-85.634629,41.945893]},"n185984011":{"id":"n185984011","loc":[-85.636058,41.946201]},"n185984013":{"id":"n185984013","loc":[-85.636112,41.946366]},"n185984015":{"id":"n185984015","loc":[-85.636143,41.946551]},"n185988237":{"id":"n185988237","loc":[-85.6354162,41.946044]},"n185988969":{"id":"n185988969","loc":[-85.635374,41.945325]},"n185988971":{"id":"n185988971","loc":[-85.635643,41.945585]},"n185988972":{"id":"n185988972","loc":[-85.635853,41.94586]},"n1475283992":{"id":"n1475283992","loc":[-85.6372968,41.9459007]},"n1475284011":{"id":"n1475284011","loc":[-85.6359415,41.9459797]},"n1475284019":{"id":"n1475284019","loc":[-85.6364433,41.9460423]},"n185984009":{"id":"n185984009","loc":[-85.6360524,41.9460485]},"n185988239":{"id":"n185988239","loc":[-85.6358187,41.9460423]},"n185988243":{"id":"n185988243","loc":[-85.6366156,41.9460282]},"n185988244":{"id":"n185988244","loc":[-85.6368316,41.9460046]},"n185988245":{"id":"n185988245","loc":[-85.6370133,41.9459704]},"n185988241":{"id":"n185988241","loc":[-85.636291,41.9460461]},"n185964976":{"id":"n185964976","loc":[-85.633923,41.9434157]},"n185964980":{"id":"n185964980","loc":[-85.6333656,41.9437293]},"n185978388":{"id":"n185978388","loc":[-85.6346449,41.9460571]},"n1819858504":{"id":"n1819858504","loc":[-85.6365343,41.9447926]},"n1819858506":{"id":"n1819858506","loc":[-85.6370546,41.9451882]},"n1819858516":{"id":"n1819858516","loc":[-85.6358369,41.9444654]},"n1819858519":{"id":"n1819858519","loc":[-85.6361534,41.9446176]},"n1819858525":{"id":"n1819858525","loc":[-85.6368025,41.9449442]},"n1819858527":{"id":"n1819858527","loc":[-85.6334199,41.9457495]},"n185963452":{"id":"n185963452","loc":[-85.633564,41.9458519]},"n185963453":{"id":"n185963453","loc":[-85.6336152,41.9459804]},"n185963451":{"id":"n185963451","loc":[-85.6332888,41.9456871]},"n2130304152":{"id":"n2130304152","loc":[-85.6359466,41.9454599]},"n2130304153":{"id":"n2130304153","loc":[-85.6362773,41.9452683]},"n2130304154":{"id":"n2130304154","loc":[-85.6352028,41.9442868]},"n2130304155":{"id":"n2130304155","loc":[-85.6348756,41.9444769]},"n2130304156":{"id":"n2130304156","loc":[-85.6349723,41.9444207]},"n2130304157":{"id":"n2130304157","loc":[-85.6338698,41.9434443]},"n2130304158":{"id":"n2130304158","loc":[-85.635094,41.9451026]},"n2130304160":{"id":"n2130304160","loc":[-85.6353716,41.9449322]},"n2130304162":{"id":"n2130304162","loc":[-85.6365942,41.9459352]},"n2130304163":{"id":"n2130304163","loc":[-85.6369006,41.9457469]},"n2130304164":{"id":"n2130304164","loc":[-85.6363292,41.9452278]},"n2130304165":{"id":"n2130304165","loc":[-85.6360248,41.9454175]},"n2139824683":{"id":"n2139824683","loc":[-85.6339825,41.9446441]},"n2139824689":{"id":"n2139824689","loc":[-85.6340437,41.9446925]},"n2139824702":{"id":"n2139824702","loc":[-85.6340961,41.9447551]},"n2139824705":{"id":"n2139824705","loc":[-85.6337467,41.944809]},"n2139824707":{"id":"n2139824707","loc":[-85.6341598,41.9448129]},"n2139824710":{"id":"n2139824710","loc":[-85.6342771,41.9448223]},"n2139824712":{"id":"n2139824712","loc":[-85.6346058,41.944841]},"n2139824713":{"id":"n2139824713","loc":[-85.633808,41.9448574]},"n2139824714":{"id":"n2139824714","loc":[-85.6340889,41.9448589]},"n2139824716":{"id":"n2139824716","loc":[-85.6343335,41.944871]},"n2139824717":{"id":"n2139824717","loc":[-85.6343341,41.9448717]},"n2139824720":{"id":"n2139824720","loc":[-85.6338757,41.9449069]},"n2139824721":{"id":"n2139824721","loc":[-85.6341445,41.9449071]},"n2139824724":{"id":"n2139824724","loc":[-85.6334787,41.9449262]},"n2139824726":{"id":"n2139824726","loc":[-85.6347119,41.9449332]},"n2139824727":{"id":"n2139824727","loc":[-85.6347175,41.9449418]},"n2139824728":{"id":"n2139824728","loc":[-85.6344284,41.9449538]},"n2139824729":{"id":"n2139824729","loc":[-85.6339339,41.9449573]},"n2139824730":{"id":"n2139824730","loc":[-85.6339179,41.9449682]},"n2139824732":{"id":"n2139824732","loc":[-85.6335472,41.9449895]},"n2139824733":{"id":"n2139824733","loc":[-85.6339736,41.9450164]},"n2139824735":{"id":"n2139824735","loc":[-85.6336034,41.9450415]},"n2139824736":{"id":"n2139824736","loc":[-85.6348317,41.945043]},"n2139824737":{"id":"n2139824737","loc":[-85.63403,41.9450651]},"n2139824738":{"id":"n2139824738","loc":[-85.6336611,41.9450949]},"n2139824740":{"id":"n2139824740","loc":[-85.6336582,41.9450966]},"n2139824744":{"id":"n2139824744","loc":[-85.6331702,41.9451107]},"n2139824745":{"id":"n2139824745","loc":[-85.6333388,41.9451142]},"n2139824746":{"id":"n2139824746","loc":[-85.6337131,41.9451341]},"n2139824747":{"id":"n2139824747","loc":[-85.6337021,41.9451372]},"n2139824748":{"id":"n2139824748","loc":[-85.6341244,41.9451472]},"n2139824749":{"id":"n2139824749","loc":[-85.6333952,41.945166]},"n2139824750":{"id":"n2139824750","loc":[-85.633395,41.9451661]},"n2139824751":{"id":"n2139824751","loc":[-85.6346258,41.9451725]},"n2139824752":{"id":"n2139824752","loc":[-85.6332387,41.9451741]},"n2139824753":{"id":"n2139824753","loc":[-85.6346901,41.9451853]},"n2139824754":{"id":"n2139824754","loc":[-85.6346611,41.9452035]},"n2139824755":{"id":"n2139824755","loc":[-85.6346574,41.9452059]},"n2139824756":{"id":"n2139824756","loc":[-85.6345611,41.9452133]},"n2139824757":{"id":"n2139824757","loc":[-85.633453,41.9452194]},"n2139824758":{"id":"n2139824758","loc":[-85.6335508,41.9452283]},"n2139824759":{"id":"n2139824759","loc":[-85.6347424,41.9452312]},"n2139824760":{"id":"n2139824760","loc":[-85.6342305,41.9452395]},"n2139824761":{"id":"n2139824761","loc":[-85.6342319,41.9452449]},"n2139824762":{"id":"n2139824762","loc":[-85.6334969,41.94526]},"n2139824763":{"id":"n2139824763","loc":[-85.63468,41.9452706]},"n2139824764":{"id":"n2139824764","loc":[-85.6346772,41.9452724]},"n2139824765":{"id":"n2139824765","loc":[-85.6338611,41.9452763]},"n2139824766":{"id":"n2139824766","loc":[-85.6347811,41.9452939]},"n2139824767":{"id":"n2139824767","loc":[-85.6347375,41.9453211]},"n2139824768":{"id":"n2139824768","loc":[-85.6339171,41.9453301]},"n2139824769":{"id":"n2139824769","loc":[-85.6348307,41.9453377]},"n2139824770":{"id":"n2139824770","loc":[-85.6347067,41.9453405]},"n2139824771":{"id":"n2139824771","loc":[-85.6343461,41.9453461]},"n2139824772":{"id":"n2139824772","loc":[-85.6343481,41.9453475]},"n2139824773":{"id":"n2139824773","loc":[-85.634805,41.9453538]},"n2139824774":{"id":"n2139824774","loc":[-85.6336997,41.9453692]},"n2139824775":{"id":"n2139824775","loc":[-85.6339709,41.9453818]},"n2139824776":{"id":"n2139824776","loc":[-85.6336229,41.9454134]},"n2139824777":{"id":"n2139824777","loc":[-85.6349022,41.9454141]},"n2139824778":{"id":"n2139824778","loc":[-85.6348854,41.9454246]},"n2139824779":{"id":"n2139824779","loc":[-85.6340286,41.9454373]},"n2139824780":{"id":"n2139824780","loc":[-85.6336963,41.9454572]},"n2139824781":{"id":"n2139824781","loc":[-85.6336789,41.9454672]},"n2139824782":{"id":"n2139824782","loc":[-85.6344933,41.945475]},"n2139824783":{"id":"n2139824783","loc":[-85.6340854,41.9454918]},"n2139824784":{"id":"n2139824784","loc":[-85.6350036,41.9455034]},"n2139824785":{"id":"n2139824785","loc":[-85.6337501,41.9455089]},"n2139824786":{"id":"n2139824786","loc":[-85.6337497,41.9455091]},"n2139824787":{"id":"n2139824787","loc":[-85.6345425,41.9455186]},"n2139824788":{"id":"n2139824788","loc":[-85.6341459,41.9455372]},"n2139824789":{"id":"n2139824789","loc":[-85.6341376,41.945542]},"n2139824790":{"id":"n2139824790","loc":[-85.6338394,41.9455462]},"n2139824791":{"id":"n2139824791","loc":[-85.6349171,41.9455588]},"n2139824792":{"id":"n2139824792","loc":[-85.6338074,41.9455646]},"n2139824793":{"id":"n2139824793","loc":[-85.6346229,41.9455894]},"n2139824794":{"id":"n2139824794","loc":[-85.6338983,41.9455995]},"n2139824795":{"id":"n2139824795","loc":[-85.6338962,41.9456007]},"n2139824796":{"id":"n2139824796","loc":[-85.6342475,41.9456348]},"n2139824797":{"id":"n2139824797","loc":[-85.6339505,41.9456497]},"n2139824798":{"id":"n2139824798","loc":[-85.6347243,41.9456788]},"n2139824799":{"id":"n2139824799","loc":[-85.635057,41.9456831]},"n2139824800":{"id":"n2139824800","loc":[-85.635287,41.9457056]},"n2139824801":{"id":"n2139824801","loc":[-85.6350753,41.9457068]},"n2139824802":{"id":"n2139824802","loc":[-85.6347753,41.9457252]},"n2139824803":{"id":"n2139824803","loc":[-85.6340521,41.9457473]},"n2139824804":{"id":"n2139824804","loc":[-85.6352875,41.9457611]},"n2139824805":{"id":"n2139824805","loc":[-85.6352941,41.9457611]},"n2139824806":{"id":"n2139824806","loc":[-85.6350758,41.9457623]},"n2139824807":{"id":"n2139824807","loc":[-85.6348194,41.9457638]},"n2139824808":{"id":"n2139824808","loc":[-85.635296,41.9459428]},"n2139824809":{"id":"n2139824809","loc":[-85.6348212,41.9459455]},"n2139832635":{"id":"n2139832635","loc":[-85.6354612,41.9448791]},"n2139832636":{"id":"n2139832636","loc":[-85.6360241,41.9453844]},"n2139832637":{"id":"n2139832637","loc":[-85.6361452,41.9453121]},"n2139832639":{"id":"n2139832639","loc":[-85.6355997,41.944797]},"n2139832641":{"id":"n2139832641","loc":[-85.6351346,41.9443541]},"n2139832647":{"id":"n2139832647","loc":[-85.6329883,41.9453692]},"n2139832653":{"id":"n2139832653","loc":[-85.6333643,41.9456293]},"n2139832663":{"id":"n2139832663","loc":[-85.6335394,41.9455339]},"n2139832665":{"id":"n2139832665","loc":[-85.6332375,41.9452476]},"n2139832667":{"id":"n2139832667","loc":[-85.6331664,41.9452161]},"n2139832669":{"id":"n2139832669","loc":[-85.6331144,41.9451875]},"n2139832671":{"id":"n2139832671","loc":[-85.6330779,41.9451274]},"n2139832673":{"id":"n2139832673","loc":[-85.6330664,41.9450802]},"n2139832678":{"id":"n2139832678","loc":[-85.6332218,41.9453585]},"n2139832686":{"id":"n2139832686","loc":[-85.6334246,41.945541]},"n2139832691":{"id":"n2139832691","loc":[-85.6329898,41.9454997]},"n2139832693":{"id":"n2139832693","loc":[-85.6343554,41.9443274]},"n2139832694":{"id":"n2139832694","loc":[-85.6336339,41.9437089]},"n2139832696":{"id":"n2139832696","loc":[-85.633532,41.9437708]},"n2139832697":{"id":"n2139832697","loc":[-85.6338316,41.9440868]},"n2139832698":{"id":"n2139832698","loc":[-85.6342258,41.9444141]},"n2139832699":{"id":"n2139832699","loc":[-85.6339164,41.9442166]},"n2139832700":{"id":"n2139832700","loc":[-85.6341389,41.944384]},"n2139832701":{"id":"n2139832701","loc":[-85.634235,41.9443259]},"n2139832702":{"id":"n2139832702","loc":[-85.633613,41.9437875]},"n2139832703":{"id":"n2139832703","loc":[-85.633915,41.9436132]},"n2139832704":{"id":"n2139832704","loc":[-85.6340019,41.9435613]},"n2139832706":{"id":"n2139832706","loc":[-85.6343197,41.9438427]},"n2139832708":{"id":"n2139832708","loc":[-85.6342361,41.9438936]},"n2139832709":{"id":"n2139832709","loc":[-85.6353839,41.9460401]},"n2139832710":{"id":"n2139832710","loc":[-85.6354032,41.9456763]},"n2139832711":{"id":"n2139832711","loc":[-85.6356839,41.9459252]},"n2139832712":{"id":"n2139832712","loc":[-85.6356109,41.945735]},"n2139832713":{"id":"n2139832713","loc":[-85.6353997,41.9457421]},"n2139832714":{"id":"n2139832714","loc":[-85.6353895,41.9459347]},"n2139832715":{"id":"n2139832715","loc":[-85.6334777,41.9436628]},"n2139832716":{"id":"n2139832716","loc":[-85.6333137,41.9435382]},"n2139832717":{"id":"n2139832717","loc":[-85.6330938,41.9435406]},"n2139832721":{"id":"n2139832721","loc":[-85.6333023,41.9434922]},"n2139832722":{"id":"n2139832722","loc":[-85.6330466,41.943623]},"n2139832723":{"id":"n2139832723","loc":[-85.6332746,41.9435624]},"n2139832724":{"id":"n2139832724","loc":[-85.6333511,41.9435176]},"n2139832725":{"id":"n2139832725","loc":[-85.6332241,41.9434001]},"n2139832726":{"id":"n2139832726","loc":[-85.6332355,41.9433686]},"n2139870373":{"id":"n2139870373","loc":[-85.6351783,41.9439117]},"n2139870374":{"id":"n2139870374","loc":[-85.6351431,41.9439217]},"n2139870375":{"id":"n2139870375","loc":[-85.6348853,41.9439117]},"n2139870376":{"id":"n2139870376","loc":[-85.6348317,41.9439105]},"n2139870377":{"id":"n2139870377","loc":[-85.6346384,41.944007]},"n2139870378":{"id":"n2139870378","loc":[-85.6345563,41.9440523]},"n2140006403":{"id":"n2140006403","loc":[-85.6359942,41.9450097]},"n2140006405":{"id":"n2140006405","loc":[-85.6363884,41.9446079]},"n2140006407":{"id":"n2140006407","loc":[-85.6362148,41.9447874]},"n2140006409":{"id":"n2140006409","loc":[-85.6379476,41.9445869]},"n2140006411":{"id":"n2140006411","loc":[-85.6378485,41.9445674]},"n2140006413":{"id":"n2140006413","loc":[-85.6378952,41.9444547]},"n2140006415":{"id":"n2140006415","loc":[-85.6379962,41.944477]},"n2140006421":{"id":"n2140006421","loc":[-85.6355248,41.9433702]},"n2140006423":{"id":"n2140006423","loc":[-85.6378471,41.9439233]},"n2140006425":{"id":"n2140006425","loc":[-85.6378913,41.9441238]},"n2140006426":{"id":"n2140006426","loc":[-85.6381674,41.9442289]},"n2140006427":{"id":"n2140006427","loc":[-85.6382359,41.9440975]},"n2140006428":{"id":"n2140006428","loc":[-85.6382071,41.9440252]},"n2140006429":{"id":"n2140006429","loc":[-85.6381409,41.9439973]},"n2140006430":{"id":"n2140006430","loc":[-85.6380569,41.9440153]},"n2140006433":{"id":"n2140006433","loc":[-85.6379071,41.9442467]},"n2140006435":{"id":"n2140006435","loc":[-85.6381634,41.9443125]},"n2140006436":{"id":"n2140006436","loc":[-85.6382407,41.944301]},"n2140006438":{"id":"n2140006438","loc":[-85.6382761,41.9442188]},"n2140006439":{"id":"n2140006439","loc":[-85.6382429,41.9441761]},"n2140006440":{"id":"n2140006440","loc":[-85.6382016,41.9441632]},"n2140006441":{"id":"n2140006441","loc":[-85.6378185,41.9439835]},"n2166205688":{"id":"n2166205688","loc":[-85.6349963,41.9444392]},"n2168544780":{"id":"n2168544780","loc":[-85.633944,41.945807]},"n2168544781":{"id":"n2168544781","loc":[-85.6340783,41.9458621]},"n2168544782":{"id":"n2168544782","loc":[-85.6338184,41.9457548]},"n2168544783":{"id":"n2168544783","loc":[-85.6339925,41.9459777]},"n2168544784":{"id":"n2168544784","loc":[-85.6337317,41.9458698]},"n2168544785":{"id":"n2168544785","loc":[-85.6337297,41.9460042]},"n2168544786":{"id":"n2168544786","loc":[-85.633919,41.9460797]},"n2168544787":{"id":"n2168544787","loc":[-85.6338672,41.9459263]},"n2168544788":{"id":"n2168544788","loc":[-85.6338246,41.9459853]},"n2168544789":{"id":"n2168544789","loc":[-85.6337615,41.9459601]},"n2168544790":{"id":"n2168544790","loc":[-85.6342079,41.9460399]},"n2168544791":{"id":"n2168544791","loc":[-85.6343346,41.9458503]},"n2168544792":{"id":"n2168544792","loc":[-85.6343759,41.9458116]},"n2168544793":{"id":"n2168544793","loc":[-85.6344394,41.9458109]},"n2168544795":{"id":"n2168544795","loc":[-85.6344827,41.945851]},"n2168544797":{"id":"n2168544797","loc":[-85.6344807,41.945969]},"n2168544798":{"id":"n2168544798","loc":[-85.6344404,41.9459697]},"n2168544799":{"id":"n2168544799","loc":[-85.6344413,41.9460333]},"n2168544800":{"id":"n2168544800","loc":[-85.6342173,41.9460705]},"n2168544801":{"id":"n2168544801","loc":[-85.6342162,41.9460392]},"n2168544802":{"id":"n2168544802","loc":[-85.6344251,41.9460351]},"n2168544805":{"id":"n2168544805","loc":[-85.6344257,41.9460507]},"n2168544807":{"id":"n2168544807","loc":[-85.6344721,41.9460498]},"n2168544809":{"id":"n2168544809","loc":[-85.6344754,41.9461427]},"n2168544811":{"id":"n2168544811","loc":[-85.6344311,41.9461435]},"n2168544813":{"id":"n2168544813","loc":[-85.6344317,41.9461592]},"n2168544815":{"id":"n2168544815","loc":[-85.6343708,41.9461604]},"n2168544817":{"id":"n2168544817","loc":[-85.6343715,41.9461786]},"n2168544819":{"id":"n2168544819","loc":[-85.6343229,41.9461795]},"n2168544821":{"id":"n2168544821","loc":[-85.6343222,41.9461606]},"n2168544823":{"id":"n2168544823","loc":[-85.6342476,41.9461621]},"n2168544825":{"id":"n2168544825","loc":[-85.6342444,41.94607]},"n2168544827":{"id":"n2168544827","loc":[-85.634138,41.9461632]},"n2168544829":{"id":"n2168544829","loc":[-85.6342016,41.9460703]},"n2168544830":{"id":"n2168544830","loc":[-85.6332929,41.9463092]},"n2168544831":{"id":"n2168544831","loc":[-85.633122,41.946239]},"n2168544832":{"id":"n2168544832","loc":[-85.6332954,41.9460055]},"n2168544833":{"id":"n2168544833","loc":[-85.6333954,41.9460466]},"n2168544834":{"id":"n2168544834","loc":[-85.6334044,41.9460345]},"n2168544835":{"id":"n2168544835","loc":[-85.6334594,41.9460571]},"n2168544836":{"id":"n2168544836","loc":[-85.6333871,41.9461544]},"n2168544837":{"id":"n2168544837","loc":[-85.633403,41.9461609]},"n2168544838":{"id":"n2168544838","loc":[-85.6341683,41.9464167]},"n2168544839":{"id":"n2168544839","loc":[-85.6341711,41.9463411]},"n2168544840":{"id":"n2168544840","loc":[-85.6344471,41.9463469]},"n2168544841":{"id":"n2168544841","loc":[-85.6344441,41.9464243]},"n2168544842":{"id":"n2168544842","loc":[-85.6343622,41.9464226]},"n2168544843":{"id":"n2168544843","loc":[-85.6343593,41.9464989]},"n2168544844":{"id":"n2168544844","loc":[-85.6342812,41.9464973]},"n2168544845":{"id":"n2168544845","loc":[-85.634283,41.9464504]},"n2168544846":{"id":"n2168544846","loc":[-85.6342609,41.9464499]},"n2168544847":{"id":"n2168544847","loc":[-85.6342621,41.9464187]},"n2168544848":{"id":"n2168544848","loc":[-85.6348414,41.9463396]},"n2168544849":{"id":"n2168544849","loc":[-85.6348387,41.9461872]},"n2168544850":{"id":"n2168544850","loc":[-85.6351186,41.9461844]},"n2168544851":{"id":"n2168544851","loc":[-85.635119,41.9462112]},"n2168544852":{"id":"n2168544852","loc":[-85.6351918,41.9462104]},"n2168544853":{"id":"n2168544853","loc":[-85.6351944,41.9463515]},"n2168544854":{"id":"n2168544854","loc":[-85.6351049,41.9463524]},"n2168544855":{"id":"n2168544855","loc":[-85.6351046,41.946337]},"n2189153180":{"id":"n2189153180","loc":[-85.6340369,41.9469572]},"n2189153181":{"id":"n2189153181","loc":[-85.6342531,41.946953]},"n2189153183":{"id":"n2189153183","loc":[-85.6348115,41.9465468]},"n2189153184":{"id":"n2189153184","loc":[-85.6348105,41.9464569]},"n2189153185":{"id":"n2189153185","loc":[-85.6351431,41.9464549]},"n2189153186":{"id":"n2189153186","loc":[-85.6351441,41.9465448]},"n2189153187":{"id":"n2189153187","loc":[-85.6350077,41.9465456]},"n2189153188":{"id":"n2189153188","loc":[-85.635008,41.9465721]},"n2189153189":{"id":"n2189153189","loc":[-85.6348965,41.9465727]},"n2189153190":{"id":"n2189153190","loc":[-85.6348962,41.9465463]},"n2189153191":{"id":"n2189153191","loc":[-85.6348963,41.9471586]},"n2189153192":{"id":"n2189153192","loc":[-85.6348944,41.947032]},"n2189153193":{"id":"n2189153193","loc":[-85.6350241,41.947031]},"n2189153194":{"id":"n2189153194","loc":[-85.635026,41.9471575]},"n2189153195":{"id":"n2189153195","loc":[-85.6352328,41.9471053]},"n2189153196":{"id":"n2189153196","loc":[-85.6352359,41.9469906]},"n2189153197":{"id":"n2189153197","loc":[-85.6353694,41.9469925]},"n2189153198":{"id":"n2189153198","loc":[-85.6353664,41.9471072]},"n2189153199":{"id":"n2189153199","loc":[-85.6348241,41.9469287]},"n2189153200":{"id":"n2189153200","loc":[-85.6348248,41.9468185]},"n2189153201":{"id":"n2189153201","loc":[-85.6351199,41.9468195]},"n2189153202":{"id":"n2189153202","loc":[-85.6351192,41.9469298]},"n2189153203":{"id":"n2189153203","loc":[-85.6347965,41.9468057]},"n2189153204":{"id":"n2189153204","loc":[-85.634792,41.9466044]},"n2189153205":{"id":"n2189153205","loc":[-85.6349483,41.9466025]},"n2189153206":{"id":"n2189153206","loc":[-85.6349493,41.9466448]},"n2189153207":{"id":"n2189153207","loc":[-85.6349753,41.9466445]},"n2189153208":{"id":"n2189153208","loc":[-85.6349743,41.9465995]},"n2189153209":{"id":"n2189153209","loc":[-85.6351173,41.9465977]},"n2189153210":{"id":"n2189153210","loc":[-85.6351219,41.9468015]},"n2189153211":{"id":"n2189153211","loc":[-85.6349806,41.9468032]},"n2189153212":{"id":"n2189153212","loc":[-85.6349794,41.9467519]},"n2189153213":{"id":"n2189153213","loc":[-85.6349521,41.9467523]},"n2189153214":{"id":"n2189153214","loc":[-85.6349532,41.9468037]},"n2189153215":{"id":"n2189153215","loc":[-85.6346302,41.9468381]},"n2189153216":{"id":"n2189153216","loc":[-85.6343028,41.9468449]},"n2189153217":{"id":"n2189153217","loc":[-85.6342006,41.9468297]},"n2189153218":{"id":"n2189153218","loc":[-85.6336698,41.9465918]},"n2189153219":{"id":"n2189153219","loc":[-85.6344663,41.9466639]},"n2189153220":{"id":"n2189153220","loc":[-85.6344639,41.9466015]},"n2189153221":{"id":"n2189153221","loc":[-85.6342283,41.9466065]},"n2189153222":{"id":"n2189153222","loc":[-85.6342303,41.9466587]},"n2189153223":{"id":"n2189153223","loc":[-85.6342843,41.9466575]},"n2189153224":{"id":"n2189153224","loc":[-85.6342851,41.9466794]},"n2189153225":{"id":"n2189153225","loc":[-85.6343475,41.9466781]},"n2189153226":{"id":"n2189153226","loc":[-85.634347,41.9466664]},"n2189153227":{"id":"n2189153227","loc":[-85.6354428,41.9470148]},"n2189153228":{"id":"n2189153228","loc":[-85.6354432,41.9468005]},"n2189153229":{"id":"n2189153229","loc":[-85.6360277,41.9468011]},"n2189153230":{"id":"n2189153230","loc":[-85.6360273,41.9470154]},"n2189153231":{"id":"n2189153231","loc":[-85.6354565,41.9465823]},"n2189153232":{"id":"n2189153232","loc":[-85.6354496,41.946218]},"n2189153233":{"id":"n2189153233","loc":[-85.6356355,41.9465788]},"n2189153234":{"id":"n2189153234","loc":[-85.6357155,41.9468008]},"n2189153235":{"id":"n2189153235","loc":[-85.6359539,41.9467969]},"n2189153236":{"id":"n2189153236","loc":[-85.6359561,41.9463036]},"n2189153237":{"id":"n2189153237","loc":[-85.6360129,41.9464793]},"n2189153238":{"id":"n2189153238","loc":[-85.6360152,41.9463898]},"n2189153239":{"id":"n2189153239","loc":[-85.6359607,41.9464928]},"n2189153240":{"id":"n2189153240","loc":[-85.6356903,41.9462227]},"n2189153242":{"id":"n2189153242","loc":[-85.6354163,41.946142]},"n2189153243":{"id":"n2189153243","loc":[-85.6357546,41.9462214]},"n2189153244":{"id":"n2189153244","loc":[-85.6357937,41.9462542]},"n2189153245":{"id":"n2189153245","loc":[-85.6358723,41.9467048]},"n2189153246":{"id":"n2189153246","loc":[-85.6361494,41.946757]},"n2189153247":{"id":"n2189153247","loc":[-85.6354173,41.9469082]},"n2189153248":{"id":"n2189153248","loc":[-85.635443,41.9469079]},"n2189153249":{"id":"n2189153249","loc":[-85.6360275,41.9469093]},"n2189153250":{"id":"n2189153250","loc":[-85.6361542,41.946915]},"n2189153251":{"id":"n2189153251","loc":[-85.6358654,41.9464843]},"n2189153252":{"id":"n2189153252","loc":[-85.6359549,41.9467499]},"n2189153253":{"id":"n2189153253","loc":[-85.6357172,41.9466335]},"n2189153254":{"id":"n2189153254","loc":[-85.6355644,41.9461768]},"n2189153255":{"id":"n2189153255","loc":[-85.6355655,41.946528]},"n2189153256":{"id":"n2189153256","loc":[-85.6357055,41.9465971]},"n2189153257":{"id":"n2189153257","loc":[-85.635869,41.9465971]},"n2189153259":{"id":"n2189153259","loc":[-85.6354561,41.9470278]},"n2189153260":{"id":"n2189153260","loc":[-85.6357961,41.9470233]},"n2189153261":{"id":"n2189153261","loc":[-85.6357977,41.9470907]},"n2189153262":{"id":"n2189153262","loc":[-85.6357297,41.9470916]},"n2189153263":{"id":"n2189153263","loc":[-85.635733,41.947233]},"n2189153264":{"id":"n2189153264","loc":[-85.6362674,41.9468637]},"n2189153265":{"id":"n2189153265","loc":[-85.6362646,41.9467047]},"n2189153266":{"id":"n2189153266","loc":[-85.6363267,41.9467047]},"n2189153267":{"id":"n2189153267","loc":[-85.6362633,41.9465848]},"n2189153268":{"id":"n2189153268","loc":[-85.6363805,41.9465468]},"n2189153269":{"id":"n2189153269","loc":[-85.6364604,41.9466842]},"n2189153270":{"id":"n2189153270","loc":[-85.6364604,41.9468647]},"n2199109756":{"id":"n2199109756","loc":[-85.6337134,41.9471841]},"n2199109757":{"id":"n2199109757","loc":[-85.6336514,41.94716]},"n2199109758":{"id":"n2199109758","loc":[-85.6337043,41.9470847]},"n2199109759":{"id":"n2199109759","loc":[-85.6335997,41.9470441]},"n2199109760":{"id":"n2199109760","loc":[-85.6335064,41.9471771]},"n185960195":{"id":"n185960195","loc":[-85.6295992,41.9524346]},"n185960796":{"id":"n185960796","loc":[-85.634723,41.953681]},"n185961396":{"id":"n185961396","loc":[-85.634767,41.959009]},"n185962625":{"id":"n185962625","loc":[-85.635175,41.97201]},"n185964982":{"id":"n185964982","loc":[-85.632799,41.9440543]},"n185965289":{"id":"n185965289","loc":[-85.634621,41.947323]},"n185965291":{"id":"n185965291","loc":[-85.636166,41.947296]},"n185965399":{"id":"n185965399","loc":[-85.634776,41.959834]},"n185966937":{"id":"n185966937","loc":[-85.633183,41.947315]},"n185966948":{"id":"n185966948","loc":[-85.626406,41.957188]},"n185967422":{"id":"n185967422","loc":[-85.6320229,41.9490123]},"n185967917":{"id":"n185967917","loc":[-85.634763,41.958292]},"n185967918":{"id":"n185967918","loc":[-85.636271,41.958311]},"n185968100":{"id":"n185968100","loc":[-85.630835,41.950656]},"n185970515":{"id":"n185970515","loc":[-85.634832,41.963866]},"n185971578":{"id":"n185971578","loc":[-85.634641,41.948627]},"n185971580":{"id":"n185971580","loc":[-85.6361818,41.9486135]},"n185971631":{"id":"n185971631","loc":[-85.634729,41.954667]},"n185971632":{"id":"n185971632","loc":[-85.636236,41.954656]},"n185972155":{"id":"n185972155","loc":[-85.623333,41.961987]},"n185974583":{"id":"n185974583","loc":[-85.634686,41.951158]},"n185974585":{"id":"n185974585","loc":[-85.6362059,41.9511457]},"n185975064":{"id":"n185975064","loc":[-85.636218,41.953667]},"n185975735":{"id":"n185975735","loc":[-85.634923,41.969269]},"n185978390":{"id":"n185978390","loc":[-85.634668,41.949875]},"n185978392":{"id":"n185978392","loc":[-85.634686,41.952415]},"n185978394":{"id":"n185978394","loc":[-85.634726,41.955921]},"n185978399":{"id":"n185978399","loc":[-85.6347861,41.9606613]},"n185978402":{"id":"n185978402","loc":[-85.634806,41.961485]},"n185978406":{"id":"n185978406","loc":[-85.6348298,41.964783]},"n185978410":{"id":"n185978410","loc":[-85.6348766,41.9677088]},"n185978414":{"id":"n185978414","loc":[-85.634938,41.971566]},"n185978415":{"id":"n185978415","loc":[-85.634942,41.971611]},"n185978417":{"id":"n185978417","loc":[-85.634952,41.971655]},"n185978419":{"id":"n185978419","loc":[-85.634989,41.971741]},"n185978420":{"id":"n185978420","loc":[-85.635063,41.971864]},"n185978787":{"id":"n185978787","loc":[-85.627936,41.954693]},"n185978790":{"id":"n185978790","loc":[-85.626832,41.954677]},"n185978967":{"id":"n185978967","loc":[-85.632278,41.948613]},"n185980735":{"id":"n185980735","loc":[-85.628639,41.953725]},"n185982163":{"id":"n185982163","loc":[-85.636233,41.952398]},"n185982193":{"id":"n185982193","loc":[-85.6313855,41.9499125]},"n185982195":{"id":"n185982195","loc":[-85.6304857,41.9511945]},"n185982196":{"id":"n185982196","loc":[-85.626336,41.957291]},"n185982197":{"id":"n185982197","loc":[-85.625578,41.958664]},"n185982198":{"id":"n185982198","loc":[-85.624619,41.960145]},"n185982200":{"id":"n185982200","loc":[-85.624494,41.960338]},"n185984017":{"id":"n185984017","loc":[-85.636163,41.947382]},"n185984020":{"id":"n185984020","loc":[-85.636188,41.9498803]},"n185984022":{"id":"n185984022","loc":[-85.636276,41.955919]},"n185984024":{"id":"n185984024","loc":[-85.636279,41.956901]},"n185988036":{"id":"n185988036","loc":[-85.631422,41.948294]},"n185988867":{"id":"n185988867","loc":[-85.63102,41.948805]},"n185988869":{"id":"n185988869","loc":[-85.630773,41.949209]},"n185988871":{"id":"n185988871","loc":[-85.63005,41.95016]},"n185988872":{"id":"n185988872","loc":[-85.629423,41.951016]},"n185988873":{"id":"n185988873","loc":[-85.629252,41.951256]},"n185988875":{"id":"n185988875","loc":[-85.629126,41.951489]},"n185988877":{"id":"n185988877","loc":[-85.628991,41.951704]},"n185988878":{"id":"n185988878","loc":[-85.628689,41.952112]},"n185988879":{"id":"n185988879","loc":[-85.628313,41.952666]},"n185988880":{"id":"n185988880","loc":[-85.627687,41.953529]},"n185988882":{"id":"n185988882","loc":[-85.627394,41.953947]},"n185988884":{"id":"n185988884","loc":[-85.627287,41.954128]},"n1819858502":{"id":"n1819858502","loc":[-85.6328435,41.9455473]},"n1819858510":{"id":"n1819858510","loc":[-85.6324841,41.9453438]},"n1819858515":{"id":"n1819858515","loc":[-85.6318511,41.9446409]},"n1819858520":{"id":"n1819858520","loc":[-85.6326558,41.9454708]},"n1819858522":{"id":"n1819858522","loc":[-85.6319048,41.9447407]},"n1819858524":{"id":"n1819858524","loc":[-85.6317718,41.9443666]},"n1819858530":{"id":"n1819858530","loc":[-85.632055,41.9449128]},"n2139795768":{"id":"n2139795768","loc":[-85.6243023,41.9606102]},"n2139832645":{"id":"n2139832645","loc":[-85.6324455,41.9448607]},"n2139832649":{"id":"n2139832649","loc":[-85.6328043,41.9454773]},"n2139832651":{"id":"n2139832651","loc":[-85.6322547,41.9449621]},"n2139832675":{"id":"n2139832675","loc":[-85.6327356,41.944757]},"n2139832677":{"id":"n2139832677","loc":[-85.6325433,41.9448599]},"n2139832680":{"id":"n2139832680","loc":[-85.6328885,41.9455614]},"n2139832682":{"id":"n2139832682","loc":[-85.6320913,41.9449492]},"n2139832684":{"id":"n2139832684","loc":[-85.6325366,41.9447133]},"n2139832688":{"id":"n2139832688","loc":[-85.6322786,41.94485]},"n2139832718":{"id":"n2139832718","loc":[-85.6327486,41.9432475]},"n2139832719":{"id":"n2139832719","loc":[-85.6327926,41.9431773]},"n2139832720":{"id":"n2139832720","loc":[-85.6329033,41.943153]},"n2139832727":{"id":"n2139832727","loc":[-85.6328975,41.9430783]},"n2139844839":{"id":"n2139844839","loc":[-85.6326261,41.9432308]},"n2189015992":{"id":"n2189015992","loc":[-85.6347706,41.9593383]},"n2189153179":{"id":"n2189153179","loc":[-85.6340476,41.9472565]},"n2189153182":{"id":"n2189153182","loc":[-85.6342638,41.9472522]},"n2189153241":{"id":"n2189153241","loc":[-85.6354184,41.9473091]},"n2189153258":{"id":"n2189153258","loc":[-85.6354611,41.9472366]},"n2189153277":{"id":"n2189153277","loc":[-85.6328948,41.9462374]},"n2199109755":{"id":"n2199109755","loc":[-85.6336729,41.9472417]},"w203970139":{"id":"w203970139","tags":{"building":"yes"},"nodes":["n2139824793","n2139824787","n2139824773","n2139824778","n2139824793"]},"w203970098":{"id":"w203970098","tags":{"building":"yes"},"nodes":["n2139824748","n2139824712","n2139824726","n2139824760","n2139824748"]},"w208643132":{"id":"w208643132","tags":{"area":"yes","building":"yes"},"nodes":["n2189153195","n2189153196","n2189153197","n2189153198","n2189153195"]},"w203970094":{"id":"w203970094","tags":{"building":"yes"},"nodes":["n2139824755","n2139824753","n2139824759","n2139824764","n2139824763","n2139824767","n2139824770","n2139824782","n2139824772","n2139824756","n2139824751","n2139824754","n2139824755"]},"w208643138":{"id":"w208643138","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153231","n2189153232","n2189153240","n2189153244","n2189153236","n2189153238","n2189153237","n2189153239","n2189153252","n2189153235","n2189153234","n2189153253","n2189153233","n2189153231"]},"w203970125":{"id":"w203970125","tags":{"building":"yes"},"nodes":["n2139824735","n2139824738","n2139824757","n2139824749","n2139824735"]},"w170848823":{"id":"w170848823","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819849189","n1819858516","n1819858519","n1819858504","n1819858525","n1819858506","n1819858513"]},"w203970898":{"id":"w203970898","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832645","n2139832647","n2139832649","n2139832651","n2139832645"]},"w203970134":{"id":"w203970134","tags":{"building":"yes"},"nodes":["n2139824796","n2139824803","n2139824797","n2139824788","n2139824796"]},"w203970104":{"id":"w203970104","tags":{"building":"yes"},"nodes":["n2139824733","n2139824730","n2139824714","n2139824721","n2139824733"]},"w206805245":{"id":"w206805245","tags":{"area":"yes","building":"yes"},"nodes":["n2168544780","n2168544781","n2139824796","n2139824803","n2168544780"]},"w206805252":{"id":"w206805252","tags":{"area":"yes","building":"yes"},"nodes":["n2168544838","n2168544839","n2168544840","n2168544841","n2168544842","n2168544843","n2168544844","n2168544845","n2168544846","n2168544847","n2168544838"]},"w203970099":{"id":"w203970099","tags":{"building":"yes"},"nodes":["n2139824783","n2139824795","n2139824790","n2139824779","n2139824783"]},"w17967730":{"id":"w17967730","tags":{"highway":"residential","name":"Water St"},"nodes":["n185963451","n2189153277","n185988036","n185988867","n185988869","n185988871","n185988872","n185988873","n185988875","n185988877","n185988878","n185988879","n185988880","n185988882","n185988884","n185978790"]},"w208643133":{"id":"w208643133","tags":{"area":"yes","building":"yes"},"nodes":["n2189153199","n2189153200","n2189153201","n2189153202","n2189153199"]},"w203970127":{"id":"w203970127","tags":{"building":"yes"},"nodes":["n2139824794","n2139824783","n2139824789","n2139824797","n2139824794"]},"w208643139":{"id":"w208643139","tags":{"highway":"service"},"nodes":["n185988237","n2189153242","n2189153247","n2189153241"]},"w203988297":{"id":"w203988297","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140006423","n2140006441","n2140006425","n2140006426","n2140006440","n2140006427","n2140006428","n2140006429","n2140006430","n2140006423"]},"w206805250":{"id":"w206805250","tags":{"area":"yes","building":"yes"},"nodes":["n2168544827","n2168544823","n2168544825","n2168544800","n2168544829","n2168544827"]},"w208643140":{"id":"w208643140","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153242","n2189153254","n2189153243","n2189153244","n2189153251","n2189153257","n2189153245","n2189153252","n2189153246"]},"w203974055":{"id":"w203974055","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870376","n2139870377"]},"w206805247":{"id":"w206805247","tags":{"area":"yes","building":"yes"},"nodes":["n2168544785","n2168544786","n2168544783","n2168544787","n2168544788","n2168544789","n2168544785"]},"w17964996":{"id":"w17964996","tags":{"highway":"residential","name":"Foster St"},"nodes":["n1819858524","n1819858515","n1819858522","n1819858530","n2139832682","n1819858510","n1819858520","n1819858502","n2139832680","n185963451","n1819858527","n185963452","n185963453","n185963454","n185963455","n185963456"]},"w208643144":{"id":"w208643144","tags":{"area":"yes","building":"yes"},"nodes":["n2189153264","n2189153265","n2189153266","n2189153267","n2189153268","n2189153269","n2189153270","n2189153264"]},"w203970914":{"id":"w203970914","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832722","n2139832723","n2139832724","n2139832725","n2139832726","n2139832727","n2139844839","n2139832722"]},"w208643143":{"id":"w208643143","tags":{"area":"yes","building":"yes"},"nodes":["n2189153258","n2189153259","n2189153260","n2189153261","n2189153262","n2189153263","n2189153258"]},"w203049590":{"id":"w203049590","tags":{"amenity":"parking","area":"yes"},"nodes":["n2130304152","n2130304153","n2140006403","n2130304154","n2130304156","n2130304155","n2130304160","n2130304152"]},"w203974054":{"id":"w203974054","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858971","n2139870373","n2139870374"]},"w203049595":{"id":"w203049595","tags":{"highway":"service"},"nodes":["n2130304158","n2130304159","n2130304160","n2139832635","n2139832639"]},"w203970913":{"id":"w203970913","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832715","n2139832716","n2139832717","n2139832718","n2139832719","n2139832720","n2139832721","n2139832716"]},"w208643134":{"id":"w208643134","tags":{"area":"yes","building":"yes"},"nodes":["n2189153203","n2189153204","n2189153205","n2189153206","n2189153207","n2189153208","n2189153209","n2189153210","n2189153211","n2189153212","n2189153213","n2189153214","n2189153203"]},"w134150808":{"id":"w134150808","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988239","n185984009","n185988241","n1475284019"]},"w203970115":{"id":"w203970115","tags":{"building":"yes"},"nodes":["n2139824761","n2139824727","n2139824736","n2139824771","n2139824761"]},"w208643130":{"id":"w208643130","tags":{"area":"yes","building":"yes"},"nodes":["n2189153183","n2189153184","n2189153185","n2189153186","n2189153187","n2189153188","n2189153189","n2189153190","n2189153183"]},"w206805246":{"id":"w206805246","tags":{"area":"yes","building":"yes"},"nodes":["n2168544782","n2168544780","n2168544781","n2168544783","n2168544787","n2168544784","n2168544782"]},"w203970138":{"id":"w203970138","tags":{"building":"yes"},"nodes":["n2139824729","n2139824720","n2139824702","n2139824707","n2139824729"]},"w203970133":{"id":"w203970133","tags":{"building":"yes"},"nodes":["n2139824748","n2139824737","n2139824717","n2139824728","n2139824748"]},"w203970907":{"id":"w203970907","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832700","n2139832701","n2139832702"]},"w203974056":{"id":"w203974056","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870377","n2139870378"]},"w203970897":{"id":"w203970897","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2130304156","n2166205688","n2139832635","n2139832636","n2139832637","n2139832639","n2139832641","n2166205688"]},"w203974057":{"id":"w203974057","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870375","n2139870376"]},"w203049594":{"id":"w203049594","tags":{"highway":"service"},"nodes":["n2130304156","n2139870378","n2139832706","n2139832704","n2130304157"]},"w203970122":{"id":"w203970122","tags":{"building":"yes"},"nodes":["n2139824757","n2139824740","n2139824747","n2139824762","n2139824757"]},"w208643136":{"id":"w208643136","tags":{"area":"yes","building":"yes"},"nodes":["n2189153219","n2189153220","n2189153221","n2189153222","n2189153223","n2189153224","n2189153225","n2189153226","n2189153219"]},"w203970128":{"id":"w203970128","tags":{"building":"yes"},"nodes":["n2139824732","n2139824752","n2139824744","n2139824724","n2139824732"]},"w203970097":{"id":"w203970097","tags":{"building":"yes"},"nodes":["n2139824737","n2139824733","n2139824710","n2139824716","n2139824737"]},"w203970137":{"id":"w203970137","tags":{"building":"yes"},"nodes":["n2139824765","n2139824774","n2139824758","n2139824746","n2139824765"]},"w134150840":{"id":"w134150840","tags":{"highway":"residential","name":"Moore St"},"nodes":["n1475284019","n185988243","n185988244","n185988245"]},"w17967628":{"id":"w17967628","tags":{"highway":"residential","name":"Moore St"},"nodes":["n185978388","n2139832709","n185988237","n185988239"]},"w203988292":{"id":"w203988292","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2140006407","n2140006405"]},"w203970118":{"id":"w203970118","tags":{"building":"yes"},"nodes":["n2139824775","n2139824785","n2139824780","n2139824768","n2139824775"]},"w203970121":{"id":"w203970121","tags":{"building":"yes"},"nodes":["n2139824768","n2139824781","n2139824776","n2139824765","n2139824768"]},"w17967752":{"id":"w17967752","tags":{"highway":"residential","name":"Railroad Drive"},"nodes":["n185964980","n2139832699","n2139832700","n2130304158","n185988969","n185988971","n185988972","n1475284011"]},"w203970136":{"id":"w203970136","tags":{"building":"yes"},"nodes":["n2139824798","n2139824793","n2139824777","n2139824784","n2139824798"]},"w203970142":{"id":"w203970142","tags":{"building":"yes"},"nodes":["n2139824808","n2139824809","n2139824807","n2139824806","n2139824801","n2139824800","n2139824804","n2139824805","n2139824808"]},"w208643137":{"id":"w208643137","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153227","n2189153248","n2189153228","n2189153234","n2189153235","n2189153229","n2189153249","n2189153230","n2189153227"]},"w208643129":{"id":"w208643129","tags":{"area":"yes","building":"yes"},"nodes":["n2189153179","n2189153180","n2189153181","n2189153182","n2189153179"]},"w203970909":{"id":"w203970909","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832703","n2139832704","n2139832706","n2139832708","n2139832703"]},"w203970905":{"id":"w203970905","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832688","n2139832691"]},"w203988298":{"id":"w203988298","tags":{"highway":"service"},"nodes":["n2140006431","n2140006433","n2140006435","n2140006436","n2140006437","n2140006438","n2140006439","n2140006440"]},"w203970106":{"id":"w203970106","tags":{"building":"yes"},"nodes":["n2139824798","n2139824791","n2139824799","n2139824802","n2139824798"]},"w203970129":{"id":"w203970129","tags":{"building":"yes"},"nodes":["n2139824787","n2139824782","n2139824766","n2139824769","n2139824787"]},"w208643131":{"id":"w208643131","tags":{"area":"yes","building":"yes"},"nodes":["n2189153191","n2189153192","n2189153193","n2189153194","n2189153191"]},"w206805249":{"id":"w206805249","tags":{"area":"yes","building":"yes"},"nodes":["n2168544800","n2168544801","n2168544802","n2168544805","n2168544807","n2168544809","n2168544811","n2168544813","n2168544815","n2168544817","n2168544819","n2168544821","n2168544823","n2168544825","n2168544800"]},"w134150800":{"id":"w134150800","tags":{"bridge":"yes","highway":"primary","name":"W Michigan Ave","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964972","n185964976"]},"w17966984":{"id":"w17966984","tags":{"highway":"residential","name":"Portage Avenue"},"nodes":["n185978375","n185963456","n2189153218","n185966937","n185978967","n185967422","n185982193","n185968100","n185982195","n185960195","n185980735","n185978787","n185966948","n185982196","n185982197","n185982198","n185982200","n2139795768","n185972155"]},"w203988294":{"id":"w203988294","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2140006409","n2140006411","n2140006413","n2140006415","n2140006409"]},"w203970912":{"id":"w203970912","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832711","n2139832712","n2139832713","n2139832714","n2139832711"]},"w203970119":{"id":"w203970119","tags":{"building":"yes"},"nodes":["n2139824713","n2139824705","n2139824683","n2139824689","n2139824713"]},"w203970114":{"id":"w203970114","tags":{"building":"yes"},"nodes":["n2139824735","n2139824750","n2139824745","n2139824732","n2139824735"]},"w208643142":{"id":"w208643142","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153254","n2189153255","n2189153256","n2189153257"]},"w206805253":{"id":"w206805253","tags":{"area":"yes","building":"yes"},"nodes":["n2168544848","n2168544849","n2168544850","n2168544851","n2168544852","n2168544853","n2168544854","n2168544855","n2168544848"]},"w143497377":{"id":"w143497377","tags":{"highway":"primary","name":"North Main Street","old_ref":"US 131","ref":"US 131 Business"},"nodes":["n185962625","n185978420","n185978419","n185978417","n185978415","n185978414","n185975735","n1475293254","n185978410","n185978406","n185970515","n185978402","n185978399","n185965399","n2189015992","n185961396","n185967917","n185978394","n185971631","n185960796","n185978392","n185974583","n185978390","n185971578","n185965289","n2189153215","n185978388","n185978383","n185978381","n185978379","n185978377","n185978375","n185964982"]},"w134150811":{"id":"w134150811","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964976","n2130304157","n1475284023","n2139832715","n185964980","n185964982"]},"w208643135":{"id":"w208643135","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153215","n2189153216","n2189153217","n2189153218"]},"w17967183":{"id":"w17967183","tags":{"highway":"residential","name":"West Street"},"nodes":["n1475284011","n185984011","n185984013","n185984015","n2189153246","n2189153250","n185965291","n185984017","n185971580","n185984020","n185974585","n185982163","n185975064","n185971632","n185984022","n185984024","n185967918"]},"w134150778":{"id":"w134150778","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988245","n1475283992","n185975911"]},"w206805248":{"id":"w206805248","tags":{"area":"yes","building":"yes"},"nodes":["n2168544790","n2168544791","n2168544792","n2168544793","n2168544795","n2168544797","n2168544798","n2168544799","n2168544802","n2168544801","n2168544790"]},"w203974058":{"id":"w203974058","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870374","n2139870375"]},"w203970902":{"id":"w203970902","tags":{"highway":"service"},"nodes":["n2139832678","n2139832691","n2139832680"]},"w203988296":{"id":"w203988296","tags":{"highway":"path"},"nodes":["n2139858967","n2140006421","n2139858935"]},"w206805251":{"id":"w206805251","tags":{"area":"yes","building":"yes"},"nodes":["n2168544830","n2168544831","n2168544832","n2168544833","n2168544834","n2168544835","n2168544836","n2168544837","n2168544830"]},"w203970906":{"id":"w203970906","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832693","n2139832694","n2139832696","n2139832697","n2139832698","n2139832693"]},"w203049598":{"id":"w203049598","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2130304162","n2130304163","n2130304164","n2130304165","n2130304162"]},"w203970911":{"id":"w203970911","tags":{"highway":"service"},"nodes":["n2139832709","n2139832714","n2139832713","n2139832710","n185988971"]},"w203970105":{"id":"w203970105","tags":{"building":"yes"},"nodes":["n2139824779","n2139824792","n2139824786","n2139824775","n2139824779"]},"w203988290":{"id":"w203988290","tags":{"highway":"footway"},"nodes":["n2140006403","n2140006407"]},"w203970900":{"id":"w203970900","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832653","n2139832663","n2139832665","n2139832667","n2139832669","n2139832671","n2139832673","n2139832675","n2139832677","n2139832653"]},"w209717048":{"id":"w209717048","tags":{"area":"yes","building":"yes"},"nodes":["n2199109755","n2199109756","n2199109757","n2199109758","n2199109759","n2199109760","n2199109755"]},"w208643141":{"id":"w208643141","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153247","n2189153248","n2189153249","n2189153250"]},"w203970903":{"id":"w203970903","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832682","n2139832688","n2139832684","n2139832678","n2139832686"]},"n354002527":{"id":"n354002527","loc":[-85.6236039,41.9458813],"tags":{"amenity":"school","name":"Barrows School"}},"n185963396":{"id":"n185963396","loc":[-85.627401,41.943496]},"n185963397":{"id":"n185963397","loc":[-85.627403,41.943625]},"n185965101":{"id":"n185965101","loc":[-85.626409,41.943215]},"n185971474":{"id":"n185971474","loc":[-85.624884,41.943508]},"n185971475":{"id":"n185971475","loc":[-85.625191,41.943509]},"n185971482":{"id":"n185971482","loc":[-85.624882,41.94382]},"n185983135":{"id":"n185983135","loc":[-85.624893,41.945616]},"n185983137":{"id":"n185983137","loc":[-85.624912,41.946524]},"n185988027":{"id":"n185988027","loc":[-85.622721,41.946535]},"n185963398":{"id":"n185963398","loc":[-85.6273993,41.9446899]},"n185983238":{"id":"n185983238","loc":[-85.6227157,41.9456321]},"n185980374":{"id":"n185980374","loc":[-85.6248856,41.9447242]},"n185980373":{"id":"n185980373","loc":[-85.6226744,41.9447371]},"n2196831342":{"id":"n2196831342","loc":[-85.6250924,41.945063]},"n2196831343":{"id":"n2196831343","loc":[-85.6252335,41.9450636]},"n2196831344":{"id":"n2196831344","loc":[-85.6252286,41.9448707]},"n2196831345":{"id":"n2196831345","loc":[-85.6250661,41.9448707]},"n2196831346":{"id":"n2196831346","loc":[-85.6250243,41.9449012]},"n2196831347":{"id":"n2196831347","loc":[-85.6250251,41.9449244]},"n2196831348":{"id":"n2196831348","loc":[-85.6250867,41.9449257]},"n2196831349":{"id":"n2196831349","loc":[-85.625349,41.9445058]},"n2196831350":{"id":"n2196831350","loc":[-85.6253471,41.9443882]},"n2196831351":{"id":"n2196831351","loc":[-85.6251516,41.94439]},"n2196831352":{"id":"n2196831352","loc":[-85.6251522,41.9444308]},"n2196831353":{"id":"n2196831353","loc":[-85.6251344,41.9444309]},"n2196831354":{"id":"n2196831354","loc":[-85.6251356,41.9445077]},"n2196831355":{"id":"n2196831355","loc":[-85.6232357,41.9463406]},"n2196831356":{"id":"n2196831356","loc":[-85.6232409,41.9460668]},"n2196831357":{"id":"n2196831357","loc":[-85.6232072,41.9460665]},"n2196831358":{"id":"n2196831358","loc":[-85.6232117,41.9458272]},"n2196831359":{"id":"n2196831359","loc":[-85.6229808,41.9458248]},"n2196831360":{"id":"n2196831360","loc":[-85.6229763,41.9460627]},"n2196831361":{"id":"n2196831361","loc":[-85.623006,41.946063]},"n2196831362":{"id":"n2196831362","loc":[-85.6230023,41.9462557]},"n2196831363":{"id":"n2196831363","loc":[-85.6230755,41.9462565]},"n2196831364":{"id":"n2196831364","loc":[-85.6230739,41.9463389]},"n185947349":{"id":"n185947349","loc":[-85.618327,41.945607]},"n185947359":{"id":"n185947359","loc":[-85.615453,41.945597]},"n185947378":{"id":"n185947378","loc":[-85.617231,41.945603]},"n185947474":{"id":"n185947474","loc":[-85.616136,41.945602]},"n185948972":{"id":"n185948972","loc":[-85.615273,41.945637]},"n185955019":{"id":"n185955019","loc":[-85.620172,41.945627]},"n185960682":{"id":"n185960682","loc":[-85.622759,41.951845]},"n185961369":{"id":"n185961369","loc":[-85.622758,41.947444]},"n185961371":{"id":"n185961371","loc":[-85.624908,41.947416]},"n185963392":{"id":"n185963392","loc":[-85.6270462,41.9409953]},"n185963393":{"id":"n185963393","loc":[-85.627295,41.941304]},"n185963394":{"id":"n185963394","loc":[-85.627352,41.94148]},"n185963395":{"id":"n185963395","loc":[-85.62737,41.942261]},"n185965099":{"id":"n185965099","loc":[-85.6264,41.942263]},"n185965108":{"id":"n185965108","loc":[-85.622769,41.949224]},"n185965110":{"id":"n185965110","loc":[-85.624937,41.949237]},"n185966295":{"id":"n185966295","loc":[-85.6299942,41.9446689]},"n185966342":{"id":"n185966342","loc":[-85.624873,41.942022]},"n185970222":{"id":"n185970222","loc":[-85.622761,41.948357]},"n185970224":{"id":"n185970224","loc":[-85.624924,41.9483338]},"n185971477":{"id":"n185971477","loc":[-85.620051,41.94383]},"n185971478":{"id":"n185971478","loc":[-85.621219,41.943801]},"n185971481":{"id":"n185971481","loc":[-85.621812,41.943807]},"n185973866":{"id":"n185973866","loc":[-85.627629,41.946498]},"n185974699":{"id":"n185974699","loc":[-85.6227688,41.950119]},"n185978800":{"id":"n185978800","loc":[-85.623953,41.954684]},"n185980372":{"id":"n185980372","loc":[-85.621459,41.944756]},"n185980378":{"id":"n185980378","loc":[-85.6286375,41.9446764]},"n185980380":{"id":"n185980380","loc":[-85.630139,41.944661]},"n185980382":{"id":"n185980382","loc":[-85.630298,41.944635]},"n185980384":{"id":"n185980384","loc":[-85.630759,41.94454]},"n185980386":{"id":"n185980386","loc":[-85.6312369,41.9444548]},"n185983133":{"id":"n185983133","loc":[-85.6248672,41.9415903]},"n185983139":{"id":"n185983139","loc":[-85.624951,41.950239]},"n185983140":{"id":"n185983140","loc":[-85.624934,41.950681]},"n185983141":{"id":"n185983141","loc":[-85.624813,41.950983]},"n185983143":{"id":"n185983143","loc":[-85.6246225,41.951591]},"n185983144":{"id":"n185983144","loc":[-85.623908,41.9539165]},"n185983145":{"id":"n185983145","loc":[-85.6238903,41.9540956]},"n185983146":{"id":"n185983146","loc":[-85.623898,41.95431]},"n185983236":{"id":"n185983236","loc":[-85.628481,41.945611]},"n185985914":{"id":"n185985914","loc":[-85.620072,41.946538]},"n185986812":{"id":"n185986812","loc":[-85.6227785,41.9510005]},"n185988028":{"id":"n185988028","loc":[-85.6281401,41.9469632]},"n185988030":{"id":"n185988030","loc":[-85.6282451,41.9470314]},"n185988032":{"id":"n185988032","loc":[-85.6283312,41.9470656]},"w17964989":{"id":"w17964989","tags":{"highway":"residential","name":"Middle St"},"nodes":["n185963392","n185963393","n185963394","n185963395","n185963396","n185963397","n185963398"]},"w17965158":{"id":"w17965158","tags":{"access":"private","highway":"service","name":"Battle St"},"nodes":["n185965099","n185965101"]},"w41074896":{"id":"w41074896","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980372","n185980373","n185980374","n185963398","n185980378","n185966295","n185980380","n185980382","n185980384","n185980386"]},"w17965846":{"id":"w17965846","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971477","n185971478","n185971481","n185971482"]},"w209470306":{"id":"w209470306","tags":{"area":"yes","building":"yes"},"nodes":["n2196831349","n2196831350","n2196831351","n2196831352","n2196831353","n2196831354","n2196831349"]},"w17965845":{"id":"w17965845","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971474","n185971475","n185963396"]},"w209470307":{"id":"w209470307","tags":{"area":"yes","building":"yes"},"nodes":["n2196831355","n2196831356","n2196831357","n2196831358","n2196831359","n2196831360","n2196831361","n2196831362","n2196831363","n2196831364","n2196831355"]},"w17968192":{"id":"w17968192","tags":{"highway":"residential","name":"Washington St"},"nodes":["n185980373","n185983238","n185988027","n185961369","n185970222","n185965108","n185974699","n185986812","n185960682"]},"w17967603":{"id":"w17967603","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185985914","n185988027","n185983137","n185973866","n185988028","n185988030","n185988032"]},"w209470305":{"id":"w209470305","tags":{"area":"yes","building":"yes"},"nodes":["n2196831342","n2196831343","n2196831344","n2196831345","n2196831346","n2196831347","n2196831348","n2196831342"]},"w17967092":{"id":"w17967092","tags":{"highway":"residential","name":"Wood St"},"nodes":["n185983133","n185966342","n185971474","n185971482","n185980374","n185983135","n185983137","n185961371","n185970224","n185965110","n185983139","n185983140","n185983141","n185983143","n185983144","n185983145","n185983146","n185978800"]},"w17967107":{"id":"w17967107","tags":{"highway":"residential","name":"4th Ave"},"nodes":["n185983236","n185983135","n185983238","n185955019","n185947349","n185947378","n185947474","n185947359","n185948972"]},"n354030330":{"id":"n354030330","loc":[-85.6297222,41.9444444],"tags":{"leisure":"park","name":"Scouter Park"}},"n185966296":{"id":"n185966296","loc":[-85.629998,41.944078]},"n185966298":{"id":"n185966298","loc":[-85.629972,41.943927]},"n185966300":{"id":"n185966300","loc":[-85.629948,41.943783]},"n185980391":{"id":"n185980391","loc":[-85.6322992,41.9442766]},"n185980393":{"id":"n185980393","loc":[-85.6324925,41.9442136]},"n185980389":{"id":"n185980389","loc":[-85.6320272,41.9443281]},"n185980388":{"id":"n185980388","loc":[-85.6315778,41.9443959]},"n354031320":{"id":"n354031320","loc":[-85.6280556,41.9447222],"tags":{"amenity":"place_of_worship","name":"Riverside Church","religion":"christian"}},"n185987309":{"id":"n185987309","loc":[-85.6286497,41.9453531]},"n185987311":{"id":"n185987311","loc":[-85.6285942,41.9454805]},"n185988034":{"id":"n185988034","loc":[-85.6285815,41.9471692]},"n185988896":{"id":"n185988896","loc":[-85.6318433,41.9437929]},"n185977764":{"id":"n185977764","loc":[-85.6322988,41.943472]},"n1819848852":{"id":"n1819848852","loc":[-85.6315188,41.9448808]},"n1819848912":{"id":"n1819848912","loc":[-85.6284289,41.9472189]},"n1819848925":{"id":"n1819848925","loc":[-85.6314501,41.9451617]},"n1819848949":{"id":"n1819848949","loc":[-85.6309394,41.9455192]},"n1819848951":{"id":"n1819848951","loc":[-85.6290297,41.9457187]},"n1819848963":{"id":"n1819848963","loc":[-85.630521,41.9455591]},"n1819848981":{"id":"n1819848981","loc":[-85.6292936,41.9455846]},"n1819848989":{"id":"n1819848989","loc":[-85.6298451,41.9455431]},"n1819848998":{"id":"n1819848998","loc":[-85.6314973,41.9446254]},"n1819849018":{"id":"n1819849018","loc":[-85.6302807,41.9455527]},"n1819849043":{"id":"n1819849043","loc":[-85.6285533,41.9469731]},"n1819849087":{"id":"n1819849087","loc":[-85.6314501,41.9453532]},"n1819849090":{"id":"n1819849090","loc":[-85.628843,41.9461033]},"n1819849109":{"id":"n1819849109","loc":[-85.6311926,41.9454729]},"n1819849116":{"id":"n1819849116","loc":[-85.6288967,41.9459437]},"n1819849177":{"id":"n1819849177","loc":[-85.6287894,41.9464544]},"n1819858529":{"id":"n1819858529","loc":[-85.6325485,41.9445625]},"n2189112797":{"id":"n2189112797","loc":[-85.6275271,41.944555]},"n2189112798":{"id":"n2189112798","loc":[-85.6275196,41.9437258]},"n2189112799":{"id":"n2189112799","loc":[-85.6278937,41.943723]},"n2189112800":{"id":"n2189112800","loc":[-85.6278969,41.9439191]},"n2189112801":{"id":"n2189112801","loc":[-85.6279907,41.9439345]},"n2189112802":{"id":"n2189112802","loc":[-85.6280817,41.9439663]},"n2189112803":{"id":"n2189112803","loc":[-85.6281768,41.9440145]},"n2189112804":{"id":"n2189112804","loc":[-85.6281933,41.9440483]},"n2189112805":{"id":"n2189112805","loc":[-85.6281671,41.9440535]},"n2189112806":{"id":"n2189112806","loc":[-85.6281933,41.9440935]},"n2189112807":{"id":"n2189112807","loc":[-85.6282126,41.9441437]},"n2189112808":{"id":"n2189112808","loc":[-85.628214,41.9441991]},"n2189112809":{"id":"n2189112809","loc":[-85.6283298,41.944196]},"n2189112810":{"id":"n2189112810","loc":[-85.6283285,41.9442616]},"n2189112811":{"id":"n2189112811","loc":[-85.6281727,41.9442616]},"n2189112812":{"id":"n2189112812","loc":[-85.6281713,41.9442934]},"n2189112813":{"id":"n2189112813","loc":[-85.6280386,41.9442963]},"n2189112814":{"id":"n2189112814","loc":[-85.6280405,41.9443292]},"n2189112815":{"id":"n2189112815","loc":[-85.627829,41.9443349]},"n2189112816":{"id":"n2189112816","loc":[-85.6278347,41.9445495]},"n2189153271":{"id":"n2189153271","loc":[-85.6321053,41.9460342]},"n2189153272":{"id":"n2189153272","loc":[-85.632278,41.9457841]},"n2189153273":{"id":"n2189153273","loc":[-85.632823,41.9459936]},"n2189153274":{"id":"n2189153274","loc":[-85.6326845,41.9461963]},"n2189153275":{"id":"n2189153275","loc":[-85.6325664,41.9461507]},"n2189153276":{"id":"n2189153276","loc":[-85.6325323,41.946198]},"n2189153278":{"id":"n2189153278","loc":[-85.6321916,41.9459733]},"n2189153279":{"id":"n2189153279","loc":[-85.6322598,41.9458703]},"n2189153280":{"id":"n2189153280","loc":[-85.6327208,41.9460358]},"n2189153281":{"id":"n2189153281","loc":[-85.6326413,41.9461422]},"n185959079":{"id":"n185959079","loc":[-85.6293702,41.9474668]},"n185966301":{"id":"n185966301","loc":[-85.629692,41.943136]},"n185966304":{"id":"n185966304","loc":[-85.629565,41.942916]},"n185966308":{"id":"n185966308","loc":[-85.629501,41.942751]},"n185966315":{"id":"n185966315","loc":[-85.629472,41.942578]},"n185966319":{"id":"n185966319","loc":[-85.629444,41.942414]},"n185966321":{"id":"n185966321","loc":[-85.629391,41.94205]},"n185966323":{"id":"n185966323","loc":[-85.629369,41.941858]},"n185966327":{"id":"n185966327","loc":[-85.629297,41.941604]},"n185966331":{"id":"n185966331","loc":[-85.629233,41.941549]},"n185966336":{"id":"n185966336","loc":[-85.628504,41.941364]},"n185966338":{"id":"n185966338","loc":[-85.628275,41.941303]},"n185966340":{"id":"n185966340","loc":[-85.6269038,41.9410983]},"n185973867":{"id":"n185973867","loc":[-85.626843,41.947333]},"n185977762":{"id":"n185977762","loc":[-85.6318441,41.9429453]},"n1819848853":{"id":"n1819848853","loc":[-85.625854,41.9492218]},"n1819848861":{"id":"n1819848861","loc":[-85.6251459,41.9552376]},"n1819848874":{"id":"n1819848874","loc":[-85.6267445,41.9482961]},"n1819848882":{"id":"n1819848882","loc":[-85.6257209,41.9552396]},"n1819848883":{"id":"n1819848883","loc":[-85.624706,41.9523173]},"n1819848907":{"id":"n1819848907","loc":[-85.62609,41.9561471]},"n1819848908":{"id":"n1819848908","loc":[-85.6244013,41.9549284]},"n1819848911":{"id":"n1819848911","loc":[-85.6265578,41.9553672]},"n1819848923":{"id":"n1819848923","loc":[-85.6246802,41.9550959]},"n1819848936":{"id":"n1819848936","loc":[-85.6241588,41.9539291]},"n1819848940":{"id":"n1819848940","loc":[-85.62506,41.9511129]},"n1819848944":{"id":"n1819848944","loc":[-85.624942,41.9515912]},"n1819848950":{"id":"n1819848950","loc":[-85.6273989,41.9475461]},"n1819848957":{"id":"n1819848957","loc":[-85.627695,41.947404]},"n1819849009":{"id":"n1819849009","loc":[-85.6259248,41.94896]},"n1819849037":{"id":"n1819849037","loc":[-85.6257252,41.9502112]},"n1819849061":{"id":"n1819849061","loc":[-85.6270084,41.9479626]},"n1819849073":{"id":"n1819849073","loc":[-85.6243734,41.9534583]},"n1819849091":{"id":"n1819849091","loc":[-85.6241373,41.9543918]},"n1819849130":{"id":"n1819849130","loc":[-85.6282572,41.9473067]},"n1819849143":{"id":"n1819849143","loc":[-85.625281,41.9506596]},"n1819849153":{"id":"n1819849153","loc":[-85.6258647,41.9498043]},"n1819849168":{"id":"n1819849168","loc":[-85.6265084,41.9559317]},"n1819849173":{"id":"n1819849173","loc":[-85.6263325,41.9552156]},"n1819849175":{"id":"n1819849175","loc":[-85.6266372,41.9556764]},"n1819849178":{"id":"n1819849178","loc":[-85.6242232,41.9545993]},"n1819849181":{"id":"n1819849181","loc":[-85.6262187,41.9486712]},"n1819849188":{"id":"n1819849188","loc":[-85.6245558,41.9530434]},"n1819849190":{"id":"n1819849190","loc":[-85.6255982,41.9563017]},"n2168544738":{"id":"n2168544738","loc":[-85.6245707,41.9529711]},"w208643145":{"id":"w208643145","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153271","n2189153272","n2189153273","n2189153274","n2189153275","n2189153276","n2189153271"]},"w17967561":{"id":"w17967561","tags":{"highway":"residential","name":"Garden St"},"nodes":["n185980378","n185987309","n185987311","n185983236","n185973866"]},"w134150802":{"id":"w134150802","tags":{"bridge":"yes","highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980386","n185980388"]},"w208639462":{"id":"w208639462","tags":{"area":"yes","building":"yes"},"nodes":["n2189112797","n2189112798","n2189112799","n2189112800","n2189112801","n2189112802","n2189112803","n2189112804","n2189112805","n2189112806","n2189112807","n2189112808","n2189112809","n2189112810","n2189112811","n2189112812","n2189112813","n2189112814","n2189112815","n2189112816","n2189112797"]},"w134150830":{"id":"w134150830","tags":{"bridge":"yes","highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977762","n185977764"]},"w134150801":{"id":"w134150801","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977764","n185964982"]},"w208643146":{"id":"w208643146","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153277","n2189153281","n2189153278","n2189153279","n2189153280","n2189153281"]},"w17966061":{"id":"w17966061","tags":{"highway":"residential","name":"John Glenn Ct"},"nodes":["n185973866","n185973867"]},"w134150772":{"id":"w134150772","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185988034","n185959079","n185988036","n185978967"]},"w134150836":{"id":"w134150836","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980388","n1819858524","n185980389","n185980391","n185980393","n185964982"]},"w17967734":{"id":"w17967734","tags":{"highway":"residential","name":"Water Street"},"nodes":["n185988896","n185980391","n1819858529"]},"w17965305":{"id":"w17965305","tags":{"highway":"residential","name":"River Dr"},"nodes":["n185966295","n185966296","n185966298","n185966300","n185966301","n185966304","n185966308","n185966315","n185966319","n185966321","n185966323","n185966327","n185966331","n185966336","n185966338","n185963392","n185966340","n185966342"]},"w134150826":{"id":"w134150826","tags":{"bridge":"yes","highway":"residential","name":"5th Ave"},"nodes":["n185988032","n185988034"]},"w170848330":{"id":"w170848330","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1819849190","n1819848907","n1819849168","n1819849175","n1819848911","n1819849173","n1819848882","n1819848861","n1819848923","n1819848908","n1819849178","n1819849091","n1819848936","n1819849073","n1819849188","n2168544738","n1819848883","n1819848944","n1819848940","n1819849143","n1819849037","n1819849153","n1819848853","n1819849009","n1819849181","n1819848874","n1819849061","n1819848950","n1819848957","n1819849130","n1819848912","n1819849043","n1819849177","n1819849090","n1819849116","n1819848951","n1819848981","n1819848989","n1819849018","n1819848963","n1819848949","n1819849109","n1819849087","n1819848925","n1819848852","n1819848998","n1819849057"]},"r270264":{"id":"r270264","tags":{"network":"US:MI","ref":"86","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-86_%28Michigan_highway%29"},"members":[{"id":"w17737723","type":"way","role":""},{"id":"w17735949","type":"way","role":""},{"id":"w17740404","type":"way","role":""},{"id":"w17966273","type":"way","role":""},{"id":"w17964745","type":"way","role":""},{"id":"w151538068","type":"way","role":""},{"id":"w151538067","type":"way","role":""},{"id":"w17964960","type":"way","role":""},{"id":"w17966099","type":"way","role":""},{"id":"w17968009","type":"way","role":""},{"id":"w41259499","type":"way","role":""},{"id":"w151540401","type":"way","role":""},{"id":"w151540418","type":"way","role":""},{"id":"w17967997","type":"way","role":""},{"id":"w17966029","type":"way","role":""},{"id":"w17964801","type":"way","role":""},{"id":"w41259496","type":"way","role":""},{"id":"w151540399","type":"way","role":""},{"id":"w17968004","type":"way","role":""},{"id":"w17966462","type":"way","role":""},{"id":"w134150830","type":"way","role":""},{"id":"w134150801","type":"way","role":""},{"id":"w17732295","type":"way","role":""}]},"n185980093":{"id":"n185980093","loc":[-85.6271414,41.9407274]},"n185964330":{"id":"n185964330","loc":[-85.6235688,41.9399111]},"n185964328":{"id":"n185964328","loc":[-85.6235609,41.9391301]},"n185958034":{"id":"n185958034","loc":[-85.627102,41.939125]},"n185964331":{"id":"n185964331","loc":[-85.623571,41.940124]},"n185964329":{"id":"n185964329","loc":[-85.623562,41.9392411]},"n185972756":{"id":"n185972756","loc":[-85.623802,41.939102]},"n185972757":{"id":"n185972757","loc":[-85.623584,41.93913]},"n185975325":{"id":"n185975325","loc":[-85.624835,41.939318]},"n185975326":{"id":"n185975326","loc":[-85.624811,41.939435]},"n185975327":{"id":"n185975327","loc":[-85.624635,41.939703]},"n185975328":{"id":"n185975328","loc":[-85.624366,41.940055]},"n185975330":{"id":"n185975330","loc":[-85.624287,41.940113]},"n185975332":{"id":"n185975332","loc":[-85.624215,41.940134]},"n185980088":{"id":"n185980088","loc":[-85.627127,41.940086]},"n185988943":{"id":"n185988943","loc":[-85.622643,41.940128]},"n185988961":{"id":"n185988961","loc":[-85.627263,41.940082]},"n185990192":{"id":"n185990192","loc":[-85.622933,41.939224]},"n185990194":{"id":"n185990194","loc":[-85.621976,41.939203]},"n185991378":{"id":"n185991378","loc":[-85.622643,41.940635]},"n1475283999":{"id":"n1475283999","loc":[-85.6271165,41.9408429]},"n185980090":{"id":"n185980090","loc":[-85.6271315,41.9402001]},"n185958036":{"id":"n185958036","loc":[-85.6248366,41.9391615]},"n1819800188":{"id":"n1819800188","loc":[-85.6246947,41.9401644]},"n1819800199":{"id":"n1819800199","loc":[-85.6233686,41.9430896]},"n1819800204":{"id":"n1819800204","loc":[-85.6223236,41.9408587]},"n1819800213":{"id":"n1819800213","loc":[-85.6247526,41.9414138]},"n1819800216":{"id":"n1819800216","loc":[-85.6230961,41.9407151]},"n1819800218":{"id":"n1819800218","loc":[-85.621991,41.9429336]},"n1819800221":{"id":"n1819800221","loc":[-85.6246088,41.9424708]},"n1819800227":{"id":"n1819800227","loc":[-85.6241368,41.9403081]},"n1819800230":{"id":"n1819800230","loc":[-85.6226776,41.9431012]},"n1819800231":{"id":"n1819800231","loc":[-85.6243728,41.9401644]},"n1819800232":{"id":"n1819800232","loc":[-85.6249629,41.9408907]},"n1819800248":{"id":"n1819800248","loc":[-85.6238685,41.9405555]},"n1819800266":{"id":"n1819800266","loc":[-85.6246882,41.9418367]},"n1819800271":{"id":"n1819800271","loc":[-85.62492,41.9413695]},"n1819800294":{"id":"n1819800294","loc":[-85.6243556,41.9427465]},"n1819800304":{"id":"n1819800304","loc":[-85.6251453,41.94117]},"n1819800325":{"id":"n1819800325","loc":[-85.6248234,41.9405714]},"n1819800362":{"id":"n1819800362","loc":[-85.6239544,41.9429416]},"n1819800368":{"id":"n1819800368","loc":[-85.6243406,41.9402283]},"n1819800375":{"id":"n1819800375","loc":[-85.6226562,41.940755]},"n1819800377":{"id":"n1819800377","loc":[-85.6232033,41.9406512]},"n185945133":{"id":"n185945133","loc":[-85.623501,41.933232]},"n185945135":{"id":"n185945135","loc":[-85.624776,41.933205]},"n185945395":{"id":"n185945395","loc":[-85.624741,41.93019]},"n185952239":{"id":"n185952239","loc":[-85.615166,41.9382]},"n185954490":{"id":"n185954490","loc":[-85.624721,41.929278]},"n185957831":{"id":"n185957831","loc":[-85.625041,41.938662]},"n185958030":{"id":"n185958030","loc":[-85.629033,41.93913]},"n185958032":{"id":"n185958032","loc":[-85.628429,41.939143]},"n185958498":{"id":"n185958498","loc":[-85.621605,41.940143]},"n185961186":{"id":"n185961186","loc":[-85.624792,41.935214]},"n185963099":{"id":"n185963099","loc":[-85.6204461,41.9401485]},"n185963698":{"id":"n185963698","loc":[-85.6297342,41.9400783]},"n185964320":{"id":"n185964320","loc":[-85.623511,41.934216]},"n185964322":{"id":"n185964322","loc":[-85.6235312,41.9362084]},"n185964324":{"id":"n185964324","loc":[-85.6235488,41.9371726]},"n185964326":{"id":"n185964326","loc":[-85.6235512,41.9381718]},"n185967077":{"id":"n185967077","loc":[-85.617359,41.940161]},"n185967634":{"id":"n185967634","loc":[-85.6248039,41.9362012]},"n185970833":{"id":"n185970833","loc":[-85.6248019,41.9381684]},"n185972752":{"id":"n185972752","loc":[-85.624582,41.938848]},"n185972754":{"id":"n185972754","loc":[-85.6242,41.939008]},"n185973251":{"id":"n185973251","loc":[-85.602727,41.936012]},"n185974509":{"id":"n185974509","loc":[-85.62478,41.93217]},"n185975315":{"id":"n185975315","loc":[-85.624703,41.925597]},"n185975316":{"id":"n185975316","loc":[-85.624716,41.927359]},"n185975317":{"id":"n185975317","loc":[-85.62475,41.93119]},"n185975318":{"id":"n185975318","loc":[-85.624782,41.934218]},"n185975320":{"id":"n185975320","loc":[-85.6247949,41.9371708]},"n185977754":{"id":"n185977754","loc":[-85.6276,41.937412]},"n185980075":{"id":"n185980075","loc":[-85.627451,41.937549]},"n185980077":{"id":"n185980077","loc":[-85.627375,41.937618]},"n185980078":{"id":"n185980078","loc":[-85.627278,41.937728]},"n185980079":{"id":"n185980079","loc":[-85.627199,41.937842]},"n185980081":{"id":"n185980081","loc":[-85.627141,41.937981]},"n185980083":{"id":"n185980083","loc":[-85.627109,41.938153]},"n185980085":{"id":"n185980085","loc":[-85.627101,41.938699]},"n185981173":{"id":"n185981173","loc":[-85.61433,41.940167]},"n185987021":{"id":"n185987021","loc":[-85.628311,41.942261]},"n185988963":{"id":"n185988963","loc":[-85.628439,41.940086]},"n185990195":{"id":"n185990195","loc":[-85.621225,41.939143]},"n185990196":{"id":"n185990196","loc":[-85.620576,41.939033]},"n185990198":{"id":"n185990198","loc":[-85.619081,41.938804]},"n185990200":{"id":"n185990200","loc":[-85.617593,41.938552]},"n185990202":{"id":"n185990202","loc":[-85.617372,41.938535]},"n185990204":{"id":"n185990204","loc":[-85.616087,41.93832]},"n185990206":{"id":"n185990206","loc":[-85.615754,41.938289]},"n185990209":{"id":"n185990209","loc":[-85.615438,41.938251]},"n185990211":{"id":"n185990211","loc":[-85.613469,41.937867]},"n185990212":{"id":"n185990212","loc":[-85.610172,41.937298]},"n185990213":{"id":"n185990213","loc":[-85.605537,41.936497]},"n185990214":{"id":"n185990214","loc":[-85.604014,41.936234]},"n1819800180":{"id":"n1819800180","loc":[-85.588775,41.9455032]},"n1819800181":{"id":"n1819800181","loc":[-85.6074212,41.9408827]},"n1819800182":{"id":"n1819800182","loc":[-85.6131397,41.9427022]},"n1819800183":{"id":"n1819800183","loc":[-85.6171523,41.9416807]},"n1819800184":{"id":"n1819800184","loc":[-85.602465,41.9397415]},"n1819800185":{"id":"n1819800185","loc":[-85.6109296,41.9410583]},"n1819800186":{"id":"n1819800186","loc":[-85.6165729,41.9418004]},"n1819800189":{"id":"n1819800189","loc":[-85.5866293,41.9458224]},"n1819800191":{"id":"n1819800191","loc":[-85.5853311,41.9466603]},"n1819800201":{"id":"n1819800201","loc":[-85.6101142,41.9433406]},"n1819800202":{"id":"n1819800202","loc":[-85.600963,41.9428618]},"n1819800206":{"id":"n1819800206","loc":[-85.6154357,41.9427501]},"n1819800207":{"id":"n1819800207","loc":[-85.6040309,41.9414094]},"n1819800209":{"id":"n1819800209","loc":[-85.6113694,41.943189]},"n1819800211":{"id":"n1819800211","loc":[-85.618032,41.9416408]},"n1819800214":{"id":"n1819800214","loc":[-85.5959419,41.9402602]},"n1819800219":{"id":"n1819800219","loc":[-85.5972117,41.9420043]},"n1819800223":{"id":"n1819800223","loc":[-85.6117171,41.9430019]},"n1819800224":{"id":"n1819800224","loc":[-85.5977873,41.9395579]},"n1819800226":{"id":"n1819800226","loc":[-85.5917362,41.9432209]},"n1819800228":{"id":"n1819800228","loc":[-85.6055759,41.9419122]},"n1819800229":{"id":"n1819800229","loc":[-85.6203395,41.9425595]},"n1819800233":{"id":"n1819800233","loc":[-85.6107579,41.9433007]},"n1819800234":{"id":"n1819800234","loc":[-85.6039773,41.9412498]},"n1819800235":{"id":"n1819800235","loc":[-85.6000977,41.9412861]},"n1819800236":{"id":"n1819800236","loc":[-85.6026689,41.9407231]},"n1819800237":{"id":"n1819800237","loc":[-85.615161,41.9428662]},"n1819800238":{"id":"n1819800238","loc":[-85.5878953,41.9454314]},"n1819800239":{"id":"n1819800239","loc":[-85.6035267,41.941569]},"n1819800240":{"id":"n1819800240","loc":[-85.5929738,41.9450208]},"n1819800241":{"id":"n1819800241","loc":[-85.6186329,41.9416488]},"n1819800242":{"id":"n1819800242","loc":[-85.5881136,41.9483963]},"n1819800243":{"id":"n1819800243","loc":[-85.5909208,41.9466922]},"n1819800244":{"id":"n1819800244","loc":[-85.5997721,41.9394941]},"n1819800245":{"id":"n1819800245","loc":[-85.6202064,41.9425712]},"n1819800246":{"id":"n1819800246","loc":[-85.591071,41.9448808]},"n1819800247":{"id":"n1819800247","loc":[-85.5866078,41.9490622]},"n1819800250":{"id":"n1819800250","loc":[-85.602383,41.9420841]},"n1819800251":{"id":"n1819800251","loc":[-85.5957418,41.9426906]},"n1819800255":{"id":"n1819800255","loc":[-85.6157039,41.9416727]},"n1819800256":{"id":"n1819800256","loc":[-85.6080328,41.9410982]},"n1819800258":{"id":"n1819800258","loc":[-85.6192551,41.9414892]},"n1819800260":{"id":"n1819800260","loc":[-85.6104253,41.94117]},"n1819800261":{"id":"n1819800261","loc":[-85.6204503,41.9425709]},"n1819800263":{"id":"n1819800263","loc":[-85.5872194,41.9455431]},"n1819800264":{"id":"n1819800264","loc":[-85.616176,41.9418244]},"n1819800268":{"id":"n1819800268","loc":[-85.6120883,41.9426703]},"n1819800269":{"id":"n1819800269","loc":[-85.5894547,41.9474946]},"n1819800272":{"id":"n1819800272","loc":[-85.6209181,41.9425027]},"n1819800274":{"id":"n1819800274","loc":[-85.6122814,41.9412817]},"n1819800276":{"id":"n1819800276","loc":[-85.5895153,41.9452798]},"n1819800277":{"id":"n1819800277","loc":[-85.5884317,41.9455272]},"n1819800279":{"id":"n1819800279","loc":[-85.5884103,41.9480966]},"n1819800287":{"id":"n1819800287","loc":[-85.5904917,41.9453915]},"n1819800288":{"id":"n1819800288","loc":[-85.6212292,41.9412977]},"n1819800289":{"id":"n1819800289","loc":[-85.5954377,41.9406832]},"n1819800290":{"id":"n1819800290","loc":[-85.593721,41.9420957]},"n1819800291":{"id":"n1819800291","loc":[-85.6162832,41.9427102]},"n1819800292":{"id":"n1819800292","loc":[-85.605018,41.9401804]},"n1819800293":{"id":"n1819800293","loc":[-85.6086443,41.941146]},"n1819800296":{"id":"n1819800296","loc":[-85.6204675,41.9413775]},"n1819800297":{"id":"n1819800297","loc":[-85.612496,41.9424947]},"n1819800299":{"id":"n1819800299","loc":[-85.6065629,41.9423431]},"n1819800301":{"id":"n1819800301","loc":[-85.6036125,41.9398452]},"n1819800303":{"id":"n1819800303","loc":[-85.6114767,41.94117]},"n1819800306":{"id":"n1819800306","loc":[-85.592616,41.9428139]},"n1819800308":{"id":"n1819800308","loc":[-85.6023041,41.9419521]},"n1819800310":{"id":"n1819800310","loc":[-85.6218944,41.9411061]},"n1819800311":{"id":"n1819800311","loc":[-85.6097816,41.941162]},"n1819800312":{"id":"n1819800312","loc":[-85.5922549,41.9457869]},"n1819800313":{"id":"n1819800313","loc":[-85.5986027,41.9417206]},"n1819800314":{"id":"n1819800314","loc":[-85.5918687,41.946138]},"n1819800315":{"id":"n1819800315","loc":[-85.5872875,41.948883]},"n1819800316":{"id":"n1819800316","loc":[-85.594272,41.9436642]},"n1819800317":{"id":"n1819800317","loc":[-85.6176351,41.941577]},"n1819800318":{"id":"n1819800318","loc":[-85.6137834,41.9430853]},"n1819800319":{"id":"n1819800319","loc":[-85.6195383,41.942622],"tags":{"leisure":"slipway"}},"n1819800320":{"id":"n1819800320","loc":[-85.5971006,41.9398053]},"n1819800321":{"id":"n1819800321","loc":[-85.601714,41.9406752]},"n1819800322":{"id":"n1819800322","loc":[-85.5908028,41.9453117]},"n1819800323":{"id":"n1819800323","loc":[-85.6062732,41.9404597]},"n1819800324":{"id":"n1819800324","loc":[-85.62124,41.9425905]},"n1819800327":{"id":"n1819800327","loc":[-85.6008664,41.942766]},"n1819800328":{"id":"n1819800328","loc":[-85.6179355,41.9428538]},"n1819800330":{"id":"n1819800330","loc":[-85.6045566,41.9415131]},"n1819800331":{"id":"n1819800331","loc":[-85.5944935,41.9414653]},"n1819800333":{"id":"n1819800333","loc":[-85.6088911,41.943181]},"n1819800334":{"id":"n1819800334","loc":[-85.5946367,41.943369]},"n1819800336":{"id":"n1819800336","loc":[-85.6150494,41.9429656]},"n1819800343":{"id":"n1819800343","loc":[-85.6096099,41.9433326]},"n1819800345":{"id":"n1819800345","loc":[-85.5915216,41.9435401]},"n1819800347":{"id":"n1819800347","loc":[-85.607786,41.9428698]},"n1819800349":{"id":"n1819800349","loc":[-85.6187616,41.9426623]},"n1819800350":{"id":"n1819800350","loc":[-85.6012527,41.9426064]},"n1819800352":{"id":"n1819800352","loc":[-85.6214867,41.9428379]},"n1819800354":{"id":"n1819800354","loc":[-85.61338,41.94293]},"n1819800355":{"id":"n1819800355","loc":[-85.5923156,41.9428139]},"n1819800357":{"id":"n1819800357","loc":[-85.5901591,41.9453197]},"n1819800359":{"id":"n1819800359","loc":[-85.6033979,41.9408827]},"n1819800360":{"id":"n1819800360","loc":[-85.6186543,41.9414653]},"n1819800363":{"id":"n1819800363","loc":[-85.6128607,41.9425665]},"n1819800365":{"id":"n1819800365","loc":[-85.614234,41.9412977]},"n1819800367":{"id":"n1819800367","loc":[-85.6089662,41.9410902]},"n1819800369":{"id":"n1819800369","loc":[-85.6197379,41.9413695]},"n1819800370":{"id":"n1819800370","loc":[-85.6037348,41.941733]},"n1819800371":{"id":"n1819800371","loc":[-85.5993467,41.9415654]},"n1819800372":{"id":"n1819800372","loc":[-85.598077,41.94196]},"n1819800373":{"id":"n1819800373","loc":[-85.5984203,41.9394781]},"n1819800374":{"id":"n1819800374","loc":[-85.6013315,41.9427066]},"n1819800376":{"id":"n1819800376","loc":[-85.5934673,41.944167]},"n1819800378":{"id":"n1819800378","loc":[-85.6011062,41.9407753]},"n1819800379":{"id":"n1819800379","loc":[-85.6150602,41.9415131]},"n1819800380":{"id":"n1819800380","loc":[-85.6132148,41.9412338]},"n1819800381":{"id":"n1819800381","loc":[-85.5889038,41.9453835]},"n2139966621":{"id":"n2139966621","loc":[-85.6198719,41.9426184]},"n2139966622":{"id":"n2139966622","loc":[-85.6197551,41.9426123]},"n2139966623":{"id":"n2139966623","loc":[-85.6196467,41.9426279]},"n2139966624":{"id":"n2139966624","loc":[-85.6191519,41.9426221]},"n2139966625":{"id":"n2139966625","loc":[-85.6194153,41.9426256]},"n2139966626":{"id":"n2139966626","loc":[-85.6200497,41.9425812]},"n2139966629":{"id":"n2139966629","loc":[-85.6192123,41.9426229]},"n2203933101":{"id":"n2203933101","loc":[-85.6030009,41.9360592]},"w17967539":{"id":"w17967539","tags":{"highway":"residential","name":"1st Ave"},"nodes":["n185965099","n185963395","n185987021"]},"w17967751":{"id":"w17967751","tags":{"highway":"residential","name":"River St"},"nodes":["n185980088","n185988961","n185988963","n185963698"]},"w17965088":{"id":"w17965088","tags":{"highway":"residential","name":"9th St"},"nodes":["n185945133","n185964320","n185964322","n185964324","n185964326","n185964328","n185964329","n185964330","n185964331"]},"w17964467":{"id":"w17964467","tags":{"highway":"residential","name":"Mechanic St"},"nodes":["n185958030","n185958032","n185958034","n185958036"]},"w134150842":{"id":"w134150842","tags":{"bridge":"yes","highway":"residential","name":"6th St"},"nodes":["n185980090","n185980093"]},"w17966740":{"id":"w17966740","tags":{"highway":"residential","name":"6th St"},"nodes":["n185977754","n185980075","n185980077","n185980078","n185980079","n185980081","n185980083","n185980085","n185958034","n185980088","n185980090"]},"w170844765":{"id":"w170844765","tags":{"waterway":"dam"},"nodes":["n1819800304","n1819800232","n1819800325","n1819800188"]},"w17967745":{"id":"w17967745","tags":{"highway":"residential","name":"River St"},"nodes":["n185981173","n185967077","n185963099","n185958498","n185988943","n185964331","n185975332"]},"w17968113":{"id":"w17968113","tags":{"highway":"residential","name":"Green St"},"nodes":["n185988943","n185991378"]},"w134150833":{"id":"w134150833","tags":{"highway":"residential","name":"6th St"},"nodes":["n185980093","n1475283999","n185963392"]},"w17967935":{"id":"w17967935","tags":{"name":"Michigan Central Railroad","railway":"abandoned"},"nodes":["n185972757","n185990192","n185990194","n185990195","n185990196","n185990198","n185990200","n185990202","n185990204","n185990206","n185990209","n185952239","n185990211","n185990212","n185990213","n185990214","n2203933101","n185973251"]},"w17965993":{"id":"w17965993","tags":{"name":"Conrail Railroad","railway":"abandoned"},"nodes":["n185957831","n185972752","n185972754","n185972756","n185972757"]},"w17966211":{"id":"w17966211","tags":{"highway":"residential","name":"8th St"},"nodes":["n185975315","n185975316","n185954490","n185945395","n185975317","n185974509","n185945135","n185975318","n185961186","n185967634","n185975320","n185970833","n185958036","n185975325","n185975326","n185975327","n185975328","n185975330","n185975332"]},"w170844766":{"id":"w170844766","tags":{"waterway":"riverbank"},"nodes":["n1819800229","n1819800245","n2139966626","n2139966621","n2139966622","n2139966623","n1819800319","n2139966625","n2139966629","n2139966624","n1819800349","n1819800328","n1819800291","n1819800206","n1819800237","n1819800336","n1819800318","n1819800354","n1819800182","n1819800363","n1819800297","n1819800268","n1819800223","n1819800209","n1819800233","n1819800201","n1819800343","n1819800333","n1819800347","n1819800299","n1819800228","n1819800330","n1819800370","n1819800250","n1819800374","n1819800202","n1819800327","n1819800350","n1819800308","n1819800239","n1819800207","n1819800234","n1819800359","n1819800236","n1819800321","n1819800378","n1819800235","n1819800371","n1819800313","n1819800372","n1819800219","n1819800251","n1819800334","n1819800316","n1819800376","n1819800240","n1819800312","n1819800314","n1819800243","n1819800269","n1819800279","n1819800242","n1819800315","n1819800247","n1819800191","n1819800189","n1819800263","n1819800238","n1819800277","n1819800180","n1819800381","n1819800276","n1819800357","n1819800287","n1819800322","n1819800246","n1819800345","n1819800226","n1819800355","n1819800306","n1819800290","n1819800331","n1819800289","n1819800214","n1819800320","n1819800224","n1819800373","n1819800244","n1819800184","n1819800301","n1819800292","n1819800323","n1819800181","n1819800256","n1819800293","n1819800367","n1819800311","n1819800260","n1819800185","n1819800303","n1819800274","n1819800380","n1819800365","n1819800379","n1819800255","n1819800264","n1819800186","n1819800183","n1819800317","n1819800211","n1819800241","n1819800360","n1819800258","n1819800369","n1819800296","n1819800288","n1819800310","n1819800204","n1819800375","n1819800216","n1819800377","n1819800248","n1819800227","n1819800368","n1819800231","n1819800188","n1819800325","n1819800232","n1819800304","n1819800271","n1819800213","n1819800266","n1819800221","n1819800294","n1819800362","n1819800199","n1819800230","n1819800218","n1819800352","n1819800324","n1819800272","n1819800261","n1819800229"]},"n1875654132":{"id":"n1875654132","loc":[-85.6297439,41.939808]},"n1475293263":{"id":"n1475293263","loc":[-85.6296235,41.939922]},"n185947850":{"id":"n185947850","loc":[-85.631594,41.942613]},"n185952745":{"id":"n185952745","loc":[-85.630986,41.941786]},"n185972907":{"id":"n185972907","loc":[-85.631797,41.9420055]},"n185972911":{"id":"n185972911","loc":[-85.6309723,41.9411623]},"n185972915":{"id":"n185972915","loc":[-85.6295971,41.939267]},"n1475293223":{"id":"n1475293223","loc":[-85.6313962,41.9416114],"tags":{"railway":"level_crossing"}},"n1475293231":{"id":"n1475293231","loc":[-85.6318779,41.9415447]},"n1475293241":{"id":"n1475293241","loc":[-85.6304613,41.9405499]},"n1475293246":{"id":"n1475293246","loc":[-85.6297512,41.9395393],"tags":{"railway":"level_crossing"}},"n1475293251":{"id":"n1475293251","loc":[-85.6316633,41.9415128]},"n2139982404":{"id":"n2139982404","loc":[-85.6313283,41.9413748]},"n2139982407":{"id":"n2139982407","loc":[-85.6325545,41.9417787]},"n2139982408":{"id":"n2139982408","loc":[-85.6324499,41.9417693]},"n2139982409":{"id":"n2139982409","loc":[-85.6324753,41.9416444]},"n2139982410":{"id":"n2139982410","loc":[-85.6325814,41.9416538]},"n2139982411":{"id":"n2139982411","loc":[-85.6319572,41.9413515]},"n2139982412":{"id":"n2139982412","loc":[-85.6322925,41.941139]},"n2139982413":{"id":"n2139982413","loc":[-85.6323153,41.941153]},"n2139982414":{"id":"n2139982414","loc":[-85.6323019,41.9412617]},"n2139982415":{"id":"n2139982415","loc":[-85.6323703,41.9412667]},"n2139982416":{"id":"n2139982416","loc":[-85.6323555,41.941538]},"n2139982417":{"id":"n2139982417","loc":[-85.6321343,41.9416777]},"n2139982418":{"id":"n2139982418","loc":[-85.6319425,41.9416866]},"n2139982419":{"id":"n2139982419","loc":[-85.6320303,41.9416941]},"n2139982420":{"id":"n2139982420","loc":[-85.6321665,41.9415554]},"n2139982421":{"id":"n2139982421","loc":[-85.632412,41.9414164]},"n2139982422":{"id":"n2139982422","loc":[-85.6324801,41.9413421]},"n2139982423":{"id":"n2139982423","loc":[-85.6325023,41.9412585]},"n2139982424":{"id":"n2139982424","loc":[-85.6324532,41.9411607]},"n2139982425":{"id":"n2139982425","loc":[-85.6323502,41.941103]},"n2139982426":{"id":"n2139982426","loc":[-85.6322362,41.9411183]},"n2139982427":{"id":"n2139982427","loc":[-85.6318941,41.9413551]},"n2139982428":{"id":"n2139982428","loc":[-85.6318592,41.9414105]},"n2139982429":{"id":"n2139982429","loc":[-85.6320111,41.9415866]},"n2139982430":{"id":"n2139982430","loc":[-85.632446,41.9413792]},"n2139982431":{"id":"n2139982431","loc":[-85.6325112,41.941416]},"n2139982432":{"id":"n2139982432","loc":[-85.6325449,41.9416345]},"n2139982433":{"id":"n2139982433","loc":[-85.6326122,41.94164]},"n2139982434":{"id":"n2139982434","loc":[-85.6325954,41.9421966]},"n2139982435":{"id":"n2139982435","loc":[-85.6325655,41.9422411]},"n2139982436":{"id":"n2139982436","loc":[-85.632515,41.9422564]},"n2139982437":{"id":"n2139982437","loc":[-85.6324495,41.94223]},"n2139982438":{"id":"n2139982438","loc":[-85.6324009,41.9421743]},"n2139982439":{"id":"n2139982439","loc":[-85.6323915,41.9421145]},"n2139982440":{"id":"n2139982440","loc":[-85.6320287,41.9418585]},"n2139982441":{"id":"n2139982441","loc":[-85.6318285,41.9416387]},"n1475293258":{"id":"n1475293258","loc":[-85.6318289,41.9415077]},"n2168544754":{"id":"n2168544754","loc":[-85.6312814,41.9431198]},"n2168544755":{"id":"n2168544755","loc":[-85.6314212,41.9430646]},"n2168544756":{"id":"n2168544756","loc":[-85.6313387,41.942949]},"n2168544757":{"id":"n2168544757","loc":[-85.6311989,41.9430041]},"n2168544758":{"id":"n2168544758","loc":[-85.6311024,41.9429313]},"n2168544759":{"id":"n2168544759","loc":[-85.6310087,41.9428087]},"n2168544760":{"id":"n2168544760","loc":[-85.6313831,41.9426504]},"n2168544761":{"id":"n2168544761","loc":[-85.6314768,41.9427729]},"n2168544762":{"id":"n2168544762","loc":[-85.6306376,41.942809]},"n2168544763":{"id":"n2168544763","loc":[-85.6307378,41.9429427]},"n2168544764":{"id":"n2168544764","loc":[-85.630841,41.9428998]},"n2168544765":{"id":"n2168544765","loc":[-85.6307408,41.9427662]},"n2168544766":{"id":"n2168544766","loc":[-85.6305404,41.9426029]},"n2168544767":{"id":"n2168544767","loc":[-85.6304976,41.9426194]},"n2168544768":{"id":"n2168544768","loc":[-85.6305673,41.9427184]},"n2168544769":{"id":"n2168544769","loc":[-85.6306164,41.9426984]},"n2168544770":{"id":"n2168544770","loc":[-85.6306418,41.9427302]},"n2168544771":{"id":"n2168544771","loc":[-85.6306861,41.9427137]},"n2168544772":{"id":"n2168544772","loc":[-85.6307146,41.9427537]},"n2168544773":{"id":"n2168544773","loc":[-85.6308999,41.9426807]},"n2168544774":{"id":"n2168544774","loc":[-85.6308429,41.9426053]},"n2168544775":{"id":"n2168544775","loc":[-85.6308999,41.9425806]},"n2168544776":{"id":"n2168544776","loc":[-85.6308318,41.9424875]},"n2168544777":{"id":"n2168544777","loc":[-85.6307732,41.9425087]},"n2168544778":{"id":"n2168544778","loc":[-85.6307178,41.9424357]},"n2168544779":{"id":"n2168544779","loc":[-85.630485,41.942524]},"n2189099387":{"id":"n2189099387","loc":[-85.631203,41.9393371]},"n2189099404":{"id":"n2189099404","loc":[-85.6301963,41.9391363]},"n2189099405":{"id":"n2189099405","loc":[-85.6304447,41.9391352]},"n2189099406":{"id":"n2189099406","loc":[-85.6304463,41.9393391]},"n2189099407":{"id":"n2189099407","loc":[-85.6308435,41.9393373]},"n2189099408":{"id":"n2189099408","loc":[-85.6308418,41.9391251]},"n2189099409":{"id":"n2189099409","loc":[-85.6310929,41.939124]},"n2189099410":{"id":"n2189099410","loc":[-85.6310946,41.9393376]},"n2189112720":{"id":"n2189112720","loc":[-85.6314677,41.9412327]},"n2189112721":{"id":"n2189112721","loc":[-85.6313337,41.9411397]},"n2189112722":{"id":"n2189112722","loc":[-85.6320521,41.9405678]},"n2189112723":{"id":"n2189112723","loc":[-85.6321899,41.9406633]},"n2189112724":{"id":"n2189112724","loc":[-85.6313229,41.9408344]},"n2189112725":{"id":"n2189112725","loc":[-85.6311223,41.9410018]},"n2189112726":{"id":"n2189112726","loc":[-85.6313205,41.9411333]},"n2189112727":{"id":"n2189112727","loc":[-85.6315211,41.9409659]},"n2189112728":{"id":"n2189112728","loc":[-85.6311035,41.9402529]},"n2189112729":{"id":"n2189112729","loc":[-85.631226,41.9402107]},"n2189112730":{"id":"n2189112730","loc":[-85.6315966,41.9408051]},"n2189112731":{"id":"n2189112731","loc":[-85.6314741,41.9408473]},"n2189112732":{"id":"n2189112732","loc":[-85.6318114,41.940534]},"n2189112733":{"id":"n2189112733","loc":[-85.631588,41.94061]},"n2189112734":{"id":"n2189112734","loc":[-85.6314379,41.940366]},"n2189112735":{"id":"n2189112735","loc":[-85.6316613,41.94029]},"n2189112736":{"id":"n2189112736","loc":[-85.6306214,41.9400415]},"n2189112737":{"id":"n2189112737","loc":[-85.6304362,41.9397728]},"n2189112738":{"id":"n2189112738","loc":[-85.6305899,41.9397142]},"n2189112739":{"id":"n2189112739","loc":[-85.6307751,41.9399828]},"n2189112740":{"id":"n2189112740","loc":[-85.6304695,41.9401673]},"n2189112741":{"id":"n2189112741","loc":[-85.6301298,41.9396855]},"n2189112742":{"id":"n2189112742","loc":[-85.6303016,41.9396184]},"n2189112743":{"id":"n2189112743","loc":[-85.6306413,41.9401003]},"n2189112744":{"id":"n2189112744","loc":[-85.6309656,41.9406189]},"n2189112745":{"id":"n2189112745","loc":[-85.6308738,41.940493]},"n2189112746":{"id":"n2189112746","loc":[-85.6309333,41.940469]},"n2189112747":{"id":"n2189112747","loc":[-85.6307634,41.9402358]},"n2189112748":{"id":"n2189112748","loc":[-85.6308798,41.9401889]},"n2189112749":{"id":"n2189112749","loc":[-85.6311416,41.940548]},"n2189112750":{"id":"n2189112750","loc":[-85.6309577,41.9408708]},"n2189112751":{"id":"n2189112751","loc":[-85.630874,41.9407777]},"n2189112752":{"id":"n2189112752","loc":[-85.6310622,41.9406841]},"n2189112753":{"id":"n2189112753","loc":[-85.6311459,41.9407772]},"n2189112754":{"id":"n2189112754","loc":[-85.6320308,41.9405747]},"n2189112755":{"id":"n2189112755","loc":[-85.6317769,41.9401857]},"n2189112756":{"id":"n2189112756","loc":[-85.6313462,41.9401785]},"n2189112757":{"id":"n2189112757","loc":[-85.6313423,41.9401199]},"n2189112758":{"id":"n2189112758","loc":[-85.6318308,41.9401184]},"n2189112759":{"id":"n2189112759","loc":[-85.6321154,41.9405433]},"n2189112760":{"id":"n2189112760","loc":[-85.6310307,41.941683]},"n2189112761":{"id":"n2189112761","loc":[-85.6309808,41.9416078]},"n2189112762":{"id":"n2189112762","loc":[-85.6312094,41.9415156]},"n2189112763":{"id":"n2189112763","loc":[-85.6312636,41.9415865]},"n2189112764":{"id":"n2189112764","loc":[-85.6309384,41.94155]},"n2189112765":{"id":"n2189112765","loc":[-85.631156,41.9414619]},"n2189112766":{"id":"n2189112766","loc":[-85.6311968,41.94152]},"n2189112767":{"id":"n2189112767","loc":[-85.6308946,41.9414851]},"n2189112768":{"id":"n2189112768","loc":[-85.6308237,41.9413888]},"n2189112769":{"id":"n2189112769","loc":[-85.6309858,41.9413228]},"n2189112770":{"id":"n2189112770","loc":[-85.6310567,41.9414192]},"n2189112771":{"id":"n2189112771","loc":[-85.6307774,41.9413276]},"n2189112772":{"id":"n2189112772","loc":[-85.6309068,41.9412735]},"n2189112773":{"id":"n2189112773","loc":[-85.6309531,41.9413347]},"n2189112774":{"id":"n2189112774","loc":[-85.6307975,41.9412466]},"n2189112775":{"id":"n2189112775","loc":[-85.6307006,41.9411699]},"n2189112776":{"id":"n2189112776","loc":[-85.6308289,41.941113]},"n2189112777":{"id":"n2189112777","loc":[-85.6308997,41.9412012]},"n2189112778":{"id":"n2189112778","loc":[-85.630765,41.9412062]},"n2189112779":{"id":"n2189112779","loc":[-85.630739,41.9412177]},"n2189112780":{"id":"n2189112780","loc":[-85.6305822,41.9410391]},"n2189112781":{"id":"n2189112781","loc":[-85.6304117,41.9408177]},"n2189112782":{"id":"n2189112782","loc":[-85.6305086,41.9407769]},"n2189112783":{"id":"n2189112783","loc":[-85.6306779,41.9410044]},"n2189112784":{"id":"n2189112784","loc":[-85.6307734,41.9421663]},"n2189112785":{"id":"n2189112785","loc":[-85.630708,41.9420741]},"n2189112786":{"id":"n2189112786","loc":[-85.630863,41.9420133]},"n2189112787":{"id":"n2189112787","loc":[-85.6309285,41.9421055]},"n2189112788":{"id":"n2189112788","loc":[-85.6307014,41.9420263]},"n2189112789":{"id":"n2189112789","loc":[-85.6306648,41.941971]},"n2189112790":{"id":"n2189112790","loc":[-85.6307927,41.9419178]},"n2189112791":{"id":"n2189112791","loc":[-85.6308366,41.9419696]},"n2189112792":{"id":"n2189112792","loc":[-85.6307574,41.9418708]},"n2189112793":{"id":"n2189112793","loc":[-85.6306288,41.9419231]},"n2189112794":{"id":"n2189112794","loc":[-85.6306943,41.9417835]},"n2189112795":{"id":"n2189112795","loc":[-85.6305344,41.9418474]},"n2189112796":{"id":"n2189112796","loc":[-85.6305981,41.9419355]},"n2189123410":{"id":"n2189123410","loc":[-85.6315476,41.9393801]},"n2189123412":{"id":"n2189123412","loc":[-85.6315247,41.9399188]},"n2189123415":{"id":"n2189123415","loc":[-85.6316484,41.9400433]},"n185945138":{"id":"n185945138","loc":[-85.627073,41.93319]},"n185945142":{"id":"n185945142","loc":[-85.6296891,41.9331674]},"n185945401":{"id":"n185945401","loc":[-85.6269,41.930199]},"n185945405":{"id":"n185945405","loc":[-85.6296598,41.9301676]},"n185956891":{"id":"n185956891","loc":[-85.6251617,41.9255049]},"n185959979":{"id":"n185959979","loc":[-85.626333,41.928347]},"n185959983":{"id":"n185959983","loc":[-85.6296419,41.9283288]},"n185961192":{"id":"n185961192","loc":[-85.627053,41.9352031]},"n185961200":{"id":"n185961200","loc":[-85.6297088,41.9351902]},"n185963655":{"id":"n185963655","loc":[-85.6296112,41.9273948]},"n185963665":{"id":"n185963665","loc":[-85.626047,41.92737]},"n185963688":{"id":"n185963688","loc":[-85.6296503,41.9292199]},"n185963689":{"id":"n185963689","loc":[-85.6296694,41.931157]},"n185963690":{"id":"n185963690","loc":[-85.6296791,41.9321485]},"n185963691":{"id":"n185963691","loc":[-85.6296991,41.9341973]},"n185967638":{"id":"n185967638","loc":[-85.627089,41.9361884]},"n185972917":{"id":"n185972917","loc":[-85.6293759,41.9388605]},"n185972919":{"id":"n185972919","loc":[-85.6290337,41.9380234]},"n185972921":{"id":"n185972921","loc":[-85.628424,41.936212]},"n185972923":{"id":"n185972923","loc":[-85.628367,41.936029]},"n185974511":{"id":"n185974511","loc":[-85.627064,41.932169]},"n185977728":{"id":"n185977728","loc":[-85.625605,41.925842]},"n185977729":{"id":"n185977729","loc":[-85.625685,41.926163]},"n185977731":{"id":"n185977731","loc":[-85.6257845,41.9264872]},"n185977733":{"id":"n185977733","loc":[-85.62663,41.929251]},"n185977734":{"id":"n185977734","loc":[-85.627008,41.930642]},"n185977736":{"id":"n185977736","loc":[-85.627029,41.930775]},"n185977738":{"id":"n185977738","loc":[-85.627041,41.930946]},"n185977739":{"id":"n185977739","loc":[-85.6270379,41.9311746]},"n185977742":{"id":"n185977742","loc":[-85.627055,41.934206]},"n185977744":{"id":"n185977744","loc":[-85.627084,41.936804]},"n185977746":{"id":"n185977746","loc":[-85.627104,41.936914]},"n185977748":{"id":"n185977748","loc":[-85.627156,41.937026]},"n185977750":{"id":"n185977750","loc":[-85.6272406,41.9371672]},"n185977752":{"id":"n185977752","loc":[-85.627317,41.93723]},"n185977753":{"id":"n185977753","loc":[-85.627422,41.937312]},"n185977755":{"id":"n185977755","loc":[-85.627754,41.937504]},"n185977757":{"id":"n185977757","loc":[-85.627883,41.937623]},"n185977761":{"id":"n185977761","loc":[-85.627984,41.93773]},"n1475283996":{"id":"n1475283996","loc":[-85.6270514,41.9317122],"tags":{"railway":"level_crossing"}},"n1475284004":{"id":"n1475284004","loc":[-85.6278177,41.9342117],"tags":{"railway":"level_crossing"}},"n1475284014":{"id":"n1475284014","loc":[-85.6251877,41.9255913],"tags":{"railway":"level_crossing"}},"n1475284017":{"id":"n1475284017","loc":[-85.6274992,41.9331816],"tags":{"railway":"level_crossing"}},"n1475284021":{"id":"n1475284021","loc":[-85.6297108,41.9353939],"tags":{"railway":"level_crossing"}},"n1475284027":{"id":"n1475284027","loc":[-85.62811,41.935198],"tags":{"railway":"level_crossing"}},"n1475284035":{"id":"n1475284035","loc":[-85.626888,41.9311757],"tags":{"railway":"level_crossing"}},"n1475293245":{"id":"n1475293245","loc":[-85.6286047,41.9367881]},"n1875654302":{"id":"n1875654302","loc":[-85.6296367,41.927491]},"n2189099388":{"id":"n2189099388","loc":[-85.6312007,41.9389988]},"n2189099389":{"id":"n2189099389","loc":[-85.6311003,41.9389992]},"n2189099390":{"id":"n2189099390","loc":[-85.6310988,41.9387847]},"n2189099391":{"id":"n2189099391","loc":[-85.6312165,41.9387843]},"n2189099392":{"id":"n2189099392","loc":[-85.6312152,41.9385857]},"n2189099393":{"id":"n2189099393","loc":[-85.6310877,41.9385862]},"n2189099394":{"id":"n2189099394","loc":[-85.6310858,41.9383161]},"n2189099395":{"id":"n2189099395","loc":[-85.6302002,41.9383196]},"n2189099396":{"id":"n2189099396","loc":[-85.6302011,41.9384472]},"n2189099397":{"id":"n2189099397","loc":[-85.6301018,41.9384476]},"n2189099398":{"id":"n2189099398","loc":[-85.6301025,41.9385419]},"n2189099399":{"id":"n2189099399","loc":[-85.6299275,41.9385427]},"n2189099400":{"id":"n2189099400","loc":[-85.62993,41.9388653]},"n2189099401":{"id":"n2189099401","loc":[-85.630107,41.9388645]},"n2189099402":{"id":"n2189099402","loc":[-85.6301079,41.9389908]},"n2189099403":{"id":"n2189099403","loc":[-85.6301951,41.9389904]},"n2189123382":{"id":"n2189123382","loc":[-85.6336279,41.9354365]},"n2189123384":{"id":"n2189123384","loc":[-85.6328492,41.9355177]},"n2189123387":{"id":"n2189123387","loc":[-85.6323762,41.9357396]},"n2189123388":{"id":"n2189123388","loc":[-85.6315174,41.9358966]},"n2189123389":{"id":"n2189123389","loc":[-85.6304331,41.936124]},"n2189123390":{"id":"n2189123390","loc":[-85.6302075,41.9364271]},"n2189123391":{"id":"n2189123391","loc":[-85.6303458,41.9367953]},"n2189123392":{"id":"n2189123392","loc":[-85.6299601,41.9369739]},"n2189123393":{"id":"n2189123393","loc":[-85.6299164,41.9374882]},"n2189123394":{"id":"n2189123394","loc":[-85.6299455,41.9378022]},"n2189123395":{"id":"n2189123395","loc":[-85.6299771,41.9379053]},"n2189123396":{"id":"n2189123396","loc":[-85.6301597,41.9379091]},"n2189123397":{"id":"n2189123397","loc":[-85.6308042,41.9377913]},"n2189123398":{"id":"n2189123398","loc":[-85.6316885,41.9378082]},"n2189123399":{"id":"n2189123399","loc":[-85.6316848,41.9380079]},"n2189123400":{"id":"n2189123400","loc":[-85.6318449,41.9381161]},"n2189123401":{"id":"n2189123401","loc":[-85.6320705,41.9381811]},"n2189123402":{"id":"n2189123402","loc":[-85.6321433,41.9383706]},"n2189123404":{"id":"n2189123404","loc":[-85.632056,41.9384355]},"n2189123406":{"id":"n2189123406","loc":[-85.6317867,41.9384572]},"n2189123409":{"id":"n2189123409","loc":[-85.6316572,41.9387281]},"n2189123417":{"id":"n2189123417","loc":[-85.6315946,41.93775]},"n2189123419":{"id":"n2189123419","loc":[-85.6302641,41.9378393]},"w208640158":{"id":"w208640158","tags":{"area":"yes","natural":"wetland"},"nodes":["n2189123379","n2189123382","n2189123384","n2189123387","n2189123388","n2189123389","n2189123390","n2189123391","n2189123392","n2189123393","n2189123394","n2189123395","n2189123396","n2189123419","n2189123397","n2189123417","n2189123398","n2189123399","n2189123400","n2189123401","n2189123402","n2189123404","n2189123406","n2189123409","n2189123410","n2189123412","n2189123415","n1819805722","n1819805861","n1819805887","n1819805760","n1819805641","n1819805632","n2189123379"]},"w134150787":{"id":"w134150787","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972905","n185972907","n1475293223","n185972911","n1475293241","n1475293246","n185972915","n185972917","n185972919","n1475293245","n185972921","n185972923","n1475284027","n1475284004","n1475284017","n1475283996","n1475284035","n1475284014","n185956891"]},"w208639443":{"id":"w208639443","tags":{"area":"yes","building":"yes"},"nodes":["n2189112720","n2189112721","n2189112722","n2189112723","n2189112720"]},"w17966462":{"id":"w17966462","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977728","n185977729","n185977731","n185963665","n185959979","n185977733","n185945401","n185977734","n185977736","n185977738","n185977739","n1475283996","n185974511","n185945138","n185977742","n185961192","n185967638","n185977744","n185977746","n185977748","n185977750","n185977752","n185977753","n185977754","n185977755","n185977757","n185977761","n185958030","n1475293263","n185963698","n185952745","n185947850","n185977762"]},"w203985741":{"id":"w203985741","tags":{"area":"yes","leisure":"park","name":"Conservation Park"},"nodes":["n2139982404","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n2139982404"]},"w17963676":{"id":"w17963676","tags":{"highway":"service"},"nodes":["n1475293258","n2139982428","n2139982427","n2139982426","n2139982425","n2139982424","n2139982423","n2139982422","n2139982430","n2139982421","n2139982420","n2139982429","n1475293231","n1475293258","n1475293251","n1475293223","n185952745"]},"w203985745":{"id":"w203985745","tags":{"highway":"footway"},"nodes":["n2139982430","n2139982431","n2139982432","n2139982433","n2139982434","n2139982435","n2139982436","n2139982437","n2139982438","n2139982439","n2139982440","n2139982441","n1475293231"]},"w208639451":{"id":"w208639451","tags":{"area":"yes","building":"yes"},"nodes":["n2189112754","n2189112755","n2189112756","n2189112757","n2189112758","n2189112759","n2189112754"]},"w208639452":{"id":"w208639452","tags":{"area":"yes","building":"yes"},"nodes":["n2189112760","n2189112761","n2189112766","n2189112762","n2189112763","n2189112760"]},"w206805244":{"id":"w206805244","tags":{"area":"yes","building":"yes"},"nodes":["n2168544766","n2168544767","n2168544768","n2168544769","n2168544770","n2168544771","n2168544772","n2168544773","n2168544774","n2168544775","n2168544776","n2168544777","n2168544778","n2168544779","n2168544766"]},"w208639444":{"id":"w208639444","tags":{"area":"yes","building":"yes"},"nodes":["n2189112724","n2189112725","n2189112726","n2189112727","n2189112724"]},"w208639450":{"id":"w208639450","tags":{"area":"yes","building":"yes"},"nodes":["n2189112750","n2189112751","n2189112752","n2189112753","n2189112750"]},"w208639448":{"id":"w208639448","tags":{"area":"yes","building":"yes"},"nodes":["n2189112740","n2189112741","n2189112742","n2189112743","n2189112740"]},"w208637859":{"id":"w208637859","tags":{"area":"yes","building":"yes"},"nodes":["n2189099387","n2189099388","n2189099389","n2189099390","n2189099391","n2189099392","n2189099393","n2189099394","n2189099395","n2189099396","n2189099397","n2189099398","n2189099399","n2189099400","n2189099401","n2189099402","n2189099403","n2189099404","n2189099405","n2189099406","n2189099407","n2189099408","n2189099409","n2189099410","n2189099387"]},"w208639453":{"id":"w208639453","tags":{"area":"yes","building":"yes"},"nodes":["n2189112764","n2189112765","n2189112766","n2189112761","n2189112764"]},"w208639456":{"id":"w208639456","tags":{"area":"yes","building":"yes"},"nodes":["n2189112774","n2189112778","n2189112779","n2189112775","n2189112776","n2189112777","n2189112774"]},"w208639445":{"id":"w208639445","tags":{"area":"yes","building":"yes"},"nodes":["n2189112728","n2189112729","n2189112730","n2189112731","n2189112728"]},"w17967776":{"id":"w17967776","tags":{"highway":"residential","name":"5th St"},"nodes":["n185958032","n185988963"]},"w208639461":{"id":"w208639461","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112794","n2189112795","n2189112796","n2189112793","n2189112792"]},"w206805241":{"id":"w206805241","tags":{"area":"yes","building":"yes"},"nodes":["n2168544754","n2168544755","n2168544756","n2168544757","n2168544754"]},"w208639449":{"id":"w208639449","tags":{"area":"yes","building":"yes"},"nodes":["n2189112744","n2189112745","n2189112746","n2189112747","n2189112748","n2189112749","n2189112744"]},"w208639455":{"id":"w208639455","tags":{"area":"yes","building":"yes"},"nodes":["n2189112771","n2189112772","n2189112773","n2189112768","n2189112771"]},"w208639457":{"id":"w208639457","tags":{"area":"yes","building":"yes"},"nodes":["n2189112780","n2189112781","n2189112782","n2189112783","n2189112780"]},"w208639446":{"id":"w208639446","tags":{"area":"yes","building":"yes"},"nodes":["n2189112732","n2189112733","n2189112734","n2189112735","n2189112732"]},"w208639454":{"id":"w208639454","tags":{"area":"yes","building":"yes"},"nodes":["n2189112767","n2189112768","n2189112773","n2189112769","n2189112770","n2189112767"]},"w203985743":{"id":"w203985743","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139982411","n2139982412","n2139982413","n2139982414","n2139982415","n2139982416","n2139982417","n2139982419","n2139982418","n2139982411"]},"w17965023":{"id":"w17965023","tags":{"highway":"residential","name":"4th St"},"nodes":["n185963655","n1875654302","n185959983","n185963688","n185945405","n185963689","n185963690","n185945142","n185963691","n185961200","n1475284021","n1475293246","n1875654132","n1475293263"]},"w206805242":{"id":"w206805242","tags":{"area":"yes","building":"yes"},"nodes":["n2168544758","n2168544759","n2168544760","n2168544761","n2168544758"]},"w208639460":{"id":"w208639460","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112793","n2189112789","n2189112790","n2189112792"]},"w208639447":{"id":"w208639447","tags":{"area":"yes","building":"yes"},"nodes":["n2189112736","n2189112737","n2189112738","n2189112739","n2189112736"]},"w208639458":{"id":"w208639458","tags":{"area":"yes","building":"yes"},"nodes":["n2189112784","n2189112785","n2189112786","n2189112787","n2189112784"]},"w203985744":{"id":"w203985744","tags":{"highway":"service"},"nodes":["n2139982425","n2139982400"]},"w208639459":{"id":"w208639459","tags":{"area":"yes","building":"yes"},"nodes":["n2189112788","n2189112789","n2189112790","n2189112791","n2189112788"]},"w203985742":{"id":"w203985742","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139982407","n2139982408","n2139982409","n2139982410","n2139982407"]},"w206805243":{"id":"w206805243","tags":{"area":"yes","building":"yes"},"nodes":["n2168544762","n2168544763","n2168544764","n2168544765","n2168544762"]},"n185959081":{"id":"n185959081","loc":[-85.628469,41.948674]},"n185967427":{"id":"n185967427","loc":[-85.632054,41.951174]},"n185967424":{"id":"n185967424","loc":[-85.6320391,41.9499109]},"n185968101":{"id":"n185968101","loc":[-85.6308395,41.9511969]},"n185960792":{"id":"n185960792","loc":[-85.632074,41.953707]},"n185961389":{"id":"n185961389","loc":[-85.630935,41.959037]},"n185961391":{"id":"n185961391","loc":[-85.632169,41.959025]},"n185965395":{"id":"n185965395","loc":[-85.63216,41.959859]},"n185966953":{"id":"n185966953","loc":[-85.630894,41.957428]},"n185966955":{"id":"n185966955","loc":[-85.632122,41.957427]},"n185967430":{"id":"n185967430","loc":[-85.632077,41.952453]},"n185967432":{"id":"n185967432","loc":[-85.632095,41.954685]},"n185967434":{"id":"n185967434","loc":[-85.632121,41.955914]},"n185967436":{"id":"n185967436","loc":[-85.632128,41.9583]},"n185967438":{"id":"n185967438","loc":[-85.632187,41.960681]},"n185967440":{"id":"n185967440","loc":[-85.632182,41.961493]},"n185968102":{"id":"n185968102","loc":[-85.630855,41.952452]},"n185968104":{"id":"n185968104","loc":[-85.630887,41.953714]},"n185968106":{"id":"n185968106","loc":[-85.630883,41.954692]},"n185968108":{"id":"n185968108","loc":[-85.630904,41.955913]},"n185968110":{"id":"n185968110","loc":[-85.630904,41.958058]},"n185968112":{"id":"n185968112","loc":[-85.630952,41.960667]},"n185968114":{"id":"n185968114","loc":[-85.630972,41.961495]},"n185968116":{"id":"n185968116","loc":[-85.630962,41.961967]},"n185978969":{"id":"n185978969","loc":[-85.633214,41.948618]},"n185985812":{"id":"n185985812","loc":[-85.633274,41.951159]},"n185986155":{"id":"n185986155","loc":[-85.633258,41.949893]},"n2208608826":{"id":"n2208608826","loc":[-85.6339222,41.9486225]},"w17964531":{"id":"w17964531","tags":{"highway":"residential","name":"Willow Dr"},"nodes":["n185959079","n185959081"]},"w17967386":{"id":"w17967386","tags":{"highway":"residential","name":"East Armitage Street"},"nodes":["n185982195","n185968101","n185967427","n185985812","n185974583"]},"w17965502":{"id":"w17965502","tags":{"highway":"residential","name":"Elm Street"},"nodes":["n185968100","n185968101","n185968102","n185968104","n185968106","n185968108","n185966953","n185968110","n185961389","n185968112","n185968114","n185968116"]},"w17967844":{"id":"w17967844","tags":{"highway":"residential","name":"East Bennett Street"},"nodes":["n185982193","n185967424","n185986155","n185978390"]},"w17966581":{"id":"w17966581","tags":{"highway":"residential","name":"E Kelsey St"},"nodes":["n185978967","n185978969","n2208608826","n185971578"]},"w17965402":{"id":"w17965402","tags":{"highway":"residential","name":"Walnut Street"},"nodes":["n185967422","n185967424","n185967427","n185967430","n185960792","n185967432","n185967434","n185966955","n185967436","n185961391","n185965395","n185967438","n185967440"]},"n2199093506":{"id":"n2199093506","loc":[-85.6251879,41.9478322]},"n2199093505":{"id":"n2199093505","loc":[-85.6252076,41.9477749]},"n2199093504":{"id":"n2199093504","loc":[-85.6252289,41.9477602]},"n2199093503":{"id":"n2199093503","loc":[-85.625201,41.9477492]},"n2199093502":{"id":"n2199093502","loc":[-85.6251682,41.9477066]},"n2199093501":{"id":"n2199093501","loc":[-85.6251715,41.947609]},"n2199093500":{"id":"n2199093500","loc":[-85.6252125,41.9475639]},"n2199093499":{"id":"n2199093499","loc":[-85.6252896,41.9475602]},"n2199093498":{"id":"n2199093498","loc":[-85.6253027,41.9475334]},"n2199093497":{"id":"n2199093497","loc":[-85.6253437,41.9474822]},"n2199093496":{"id":"n2199093496","loc":[-85.6254421,41.9474675]},"n2199093495":{"id":"n2199093495","loc":[-85.6256503,41.9474944]},"n2199093494":{"id":"n2199093494","loc":[-85.6257257,41.9476127]},"n2199093493":{"id":"n2199093493","loc":[-85.6257028,41.9477285]},"n2199093492":{"id":"n2199093492","loc":[-85.6255339,41.9478102]},"n2199093491":{"id":"n2199093491","loc":[-85.6253912,41.9478224]},"n2199093490":{"id":"n2199093490","loc":[-85.6253043,41.947859]},"n2199093489":{"id":"n2199093489","loc":[-85.6252027,41.9478846]},"n2199093458":{"id":"n2199093458","loc":[-85.6246876,41.9486617]},"n2199093457":{"id":"n2199093457","loc":[-85.6243127,41.9486583]},"n2199093456":{"id":"n2199093456","loc":[-85.624306,41.9490569]},"n2199093455":{"id":"n2199093455","loc":[-85.624681,41.9490603]},"n2199093514":{"id":"n2199093514","loc":[-85.6236228,41.9496059]},"n2199093513":{"id":"n2199093513","loc":[-85.6236231,41.9496997]},"n2199093512":{"id":"n2199093512","loc":[-85.623357,41.9497002]},"n2199093511":{"id":"n2199093511","loc":[-85.6233567,41.9496136]},"n2199093508":{"id":"n2199093508","loc":[-85.6239735,41.9494287]},"n2199093507":{"id":"n2199093507","loc":[-85.6239741,41.9496052]},"n2199093488":{"id":"n2199093488","loc":[-85.624497,41.9512286]},"n2199093487":{"id":"n2199093487","loc":[-85.6244966,41.9511259]},"n2199093486":{"id":"n2199093486","loc":[-85.6243151,41.9511263]},"n2199093485":{"id":"n2199093485","loc":[-85.6243154,41.951229]},"n2199093484":{"id":"n2199093484","loc":[-85.6241205,41.9508665]},"n2199093483":{"id":"n2199093483","loc":[-85.624115,41.9505249]},"n2199093482":{"id":"n2199093482","loc":[-85.6243149,41.9505231]},"n2199093481":{"id":"n2199093481","loc":[-85.6243203,41.9508648]},"n2199093480":{"id":"n2199093480","loc":[-85.624393,41.9508668]},"n2199093479":{"id":"n2199093479","loc":[-85.6243904,41.9505956]},"n2199093478":{"id":"n2199093478","loc":[-85.6246727,41.950594]},"n2199093477":{"id":"n2199093477","loc":[-85.624675,41.9508203]},"n2199093476":{"id":"n2199093476","loc":[-85.6245097,41.9508212]},"n2199093475":{"id":"n2199093475","loc":[-85.6245101,41.9508662]},"n2199093474":{"id":"n2199093474","loc":[-85.6241008,41.9493459]},"n2199093473":{"id":"n2199093473","loc":[-85.6242442,41.9493459]},"n2199093472":{"id":"n2199093472","loc":[-85.6242442,41.9493681]},"n2199093471":{"id":"n2199093471","loc":[-85.6243397,41.9493681]},"n2199093470":{"id":"n2199093470","loc":[-85.6243417,41.9493511]},"n2199093469":{"id":"n2199093469","loc":[-85.6247251,41.9493485]},"n2199093468":{"id":"n2199093468","loc":[-85.6247548,41.9504949]},"n2199093467":{"id":"n2199093467","loc":[-85.6241214,41.9505017]},"n2199093466":{"id":"n2199093466","loc":[-85.6254398,41.950174]},"n2199093465":{"id":"n2199093465","loc":[-85.6254412,41.9499872]},"n2199093464":{"id":"n2199093464","loc":[-85.6255363,41.9499876]},"n2199093463":{"id":"n2199093463","loc":[-85.6255374,41.9498439]},"n2199093462":{"id":"n2199093462","loc":[-85.6255638,41.949844]},"n2199093461":{"id":"n2199093461","loc":[-85.6255652,41.9496672]},"n2199093460":{"id":"n2199093460","loc":[-85.6251823,41.9496656]},"n2199093459":{"id":"n2199093459","loc":[-85.6251785,41.9501729]},"n2199093510":{"id":"n2199093510","loc":[-85.6229922,41.9496143]},"n2199093509":{"id":"n2199093509","loc":[-85.6229915,41.9494306]},"n185948903":{"id":"n185948903","loc":[-85.616514,41.947449]},"n185955120":{"id":"n185955120","loc":[-85.620103,41.951]},"n185955143":{"id":"n185955143","loc":[-85.619784,41.94746]},"n185960124":{"id":"n185960124","loc":[-85.615238,41.947468]},"n185961362":{"id":"n185961362","loc":[-85.617437,41.947451]},"n185961364":{"id":"n185961364","loc":[-85.61861,41.947456]},"n185961367":{"id":"n185961367","loc":[-85.620088,41.947458]},"n185965105":{"id":"n185965105","loc":[-85.620087,41.94924]},"n185970220":{"id":"n185970220","loc":[-85.62156,41.948333]},"n185974697":{"id":"n185974697","loc":[-85.6201059,41.950132]},"n2138420778":{"id":"n2138420778","loc":[-85.616948,41.9474499]},"w17967535":{"id":"w17967535","tags":{"highway":"residential","name":"10th Ave"},"nodes":["n185955120","n185986812","n185983141"]},"w209716130":{"id":"w209716130","tags":{"area":"yes","building":"yes"},"nodes":["n2199093485","n2199093486","n2199093487","n2199093488","n2199093485"]},"w17964788":{"id":"w17964788","tags":{"highway":"residential","name":"6th Ave"},"nodes":["n185960124","n185948903","n2138420778","n185961362","n185961364","n185955143","n185961367","n185961369","n185961371"]},"w17965159":{"id":"w17965159","tags":{"highway":"residential","name":"8th Ave"},"nodes":["n185965105","n185965108","n185965110"]},"w209716125":{"id":"w209716125","tags":{"area":"yes","building":"yes"},"nodes":["n2199093459","n2199093460","n2199093461","n2199093462","n2199093463","n2199093464","n2199093465","n2199093466","n2199093459"]},"w17965699":{"id":"w17965699","tags":{"highway":"residential","name":"7th Ave"},"nodes":["n185970220","n185970222","n185970224"]},"w209716132":{"id":"w209716132","tags":{"area":"yes","building":"yes"},"nodes":["n2199093507","n2199093508","n2199093509","n2199093510","n2199093511","n2199093512","n2199093513","n2199093514","n2199093507"]},"w17966129":{"id":"w17966129","tags":{"highway":"residential","name":"9th Ave"},"nodes":["n185974697","n185974699"]},"w209716127":{"id":"w209716127","tags":{"area":"yes","building":"yes"},"nodes":["n2199093475","n2199093476","n2199093477","n2199093478","n2199093479","n2199093480","n2199093475"]},"w209716131":{"id":"w209716131","tags":{"area":"yes","natural":"water","water":"pond"},"nodes":["n2199093489","n2199093490","n2199093491","n2199093492","n2199093493","n2199093494","n2199093495","n2199093496","n2199093497","n2199093498","n2199093499","n2199093500","n2199093501","n2199093502","n2199093503","n2199093504","n2199093505","n2199093506","n2199093489"]},"w209716126":{"id":"w209716126","tags":{"area":"yes","building":"yes"},"nodes":["n2199093467","n2199093468","n2199093469","n2199093470","n2199093471","n2199093472","n2199093473","n2199093474","n2199093467"]},"w209716124":{"id":"w209716124","tags":{"area":"yes","building":"yes"},"nodes":["n2199093455","n2199093456","n2199093457","n2199093458","n2199093455"]},"w209716128":{"id":"w209716128","tags":{"area":"yes","building":"yes"},"nodes":["n2199093481","n2199093482","n2199093483","n2199093484","n2199093481"]},"n185949872":{"id":"n185949872","loc":[-85.643009,41.949264]},"n185949875":{"id":"n185949875","loc":[-85.642598,41.94929]},"n185949877":{"id":"n185949877","loc":[-85.642127,41.949382]},"n185949881":{"id":"n185949881","loc":[-85.64169,41.949936]},"n185988165":{"id":"n185988165","loc":[-85.642167,41.947657]},"n185988167":{"id":"n185988167","loc":[-85.642347,41.947662]},"n185988169":{"id":"n185988169","loc":[-85.642621,41.947659]},"n185965019":{"id":"n185965019","loc":[-85.6385084,41.951127]},"n1475293248":{"id":"n1475293248","loc":[-85.6386095,41.9512214]},"n185962639":{"id":"n185962639","loc":[-85.649669,41.949161]},"n185962810":{"id":"n185962810","loc":[-85.649907,41.949157]},"n185964355":{"id":"n185964355","loc":[-85.637412,41.9511359]},"n185965021":{"id":"n185965021","loc":[-85.638661,41.952386]},"n185965023":{"id":"n185965023","loc":[-85.638654,41.953665]},"n185965025":{"id":"n185965025","loc":[-85.638694,41.954649]},"n185965027":{"id":"n185965027","loc":[-85.638724,41.955913]},"n185971415":{"id":"n185971415","loc":[-85.644466,41.949246]},"n185971417":{"id":"n185971417","loc":[-85.647021,41.949193]},"n185971420":{"id":"n185971420","loc":[-85.648476,41.949169]},"n185979975":{"id":"n185979975","loc":[-85.644429,41.947633]},"n185988171":{"id":"n185988171","loc":[-85.645377,41.947622]},"w17963211":{"id":"w17963211","tags":{"highway":"residential"},"nodes":["n185949870","n185949872","n185949875","n185949877","n185949881"]},"w17965839":{"id":"w17965839","tags":{"highway":"residential","name":"Arnold St"},"nodes":["n185949870","n185971415","n185971417","n185971420","n185962639","n185962810"]},"w17967618":{"id":"w17967618","tags":{"highway":"residential","name":"Pierson St"},"nodes":["n185967777","n185988165","n185988167","n185988169","n185985824","n185979975","n185988171"]},"w17965149":{"id":"w17965149","tags":{"highway":"residential","name":"Oak St"},"nodes":["n185965019","n1475293248","n185965021","n185965023","n185965025","n185965027"]},"w17966118":{"id":"w17966118","tags":{"highway":"residential","name":"West Armitage Street"},"nodes":["n185974583","n185974585","n185964355","n185965019"]},"n2208608800":{"id":"n2208608800","loc":[-85.6354294,41.9486201]},"n2199109806":{"id":"n2199109806","loc":[-85.6350474,41.9477884]},"n2199109804":{"id":"n2199109804","loc":[-85.6350476,41.9477962]},"n2199109802":{"id":"n2199109802","loc":[-85.635002,41.9477969]},"n2199109799":{"id":"n2199109799","loc":[-85.6350018,41.9477883]},"n2199109797":{"id":"n2199109797","loc":[-85.6349141,41.9477897]},"n2199109795":{"id":"n2199109795","loc":[-85.6349131,41.9477535]},"n2199109793":{"id":"n2199109793","loc":[-85.6349395,41.9477531]},"n2199109791":{"id":"n2199109791","loc":[-85.6349382,41.9477077]},"n2199109789":{"id":"n2199109789","loc":[-85.6351236,41.9477049]},"n2199109787":{"id":"n2199109787","loc":[-85.6351259,41.9477872]},"n2199109785":{"id":"n2199109785","loc":[-85.634972,41.9475992]},"n2199109783":{"id":"n2199109783","loc":[-85.6349206,41.9475997]},"n2199109770":{"id":"n2199109770","loc":[-85.6348499,41.9475461]},"n2199109768":{"id":"n2199109768","loc":[-85.6348499,41.9475084]},"n2199109765":{"id":"n2199109765","loc":[-85.6349241,41.9474569]},"n2199109763":{"id":"n2199109763","loc":[-85.634967,41.9474564]},"n2199109762":{"id":"n2199109762","loc":[-85.6350405,41.9475121]},"n2199109761":{"id":"n2199109761","loc":[-85.6350405,41.9475419]},"n2199109753":{"id":"n2199109753","loc":[-85.6342443,41.9478391]},"n2199109751":{"id":"n2199109751","loc":[-85.6342427,41.9477927]},"n2199109745":{"id":"n2199109745","loc":[-85.6342439,41.9476859]},"n2199109743":{"id":"n2199109743","loc":[-85.6342429,41.9476575]},"n2199109741":{"id":"n2199109741","loc":[-85.6344615,41.9476533]},"n2199109739":{"id":"n2199109739","loc":[-85.6344678,41.9478348]},"n2199109737":{"id":"n2199109737","loc":[-85.634416,41.9480059]},"n2199109735":{"id":"n2199109735","loc":[-85.6344145,41.9478983]},"n2199109733":{"id":"n2199109733","loc":[-85.6342749,41.9478993]},"n2199109731":{"id":"n2199109731","loc":[-85.6342753,41.9479272]},"n2199109729":{"id":"n2199109729","loc":[-85.6342498,41.9479274]},"n2199109727":{"id":"n2199109727","loc":[-85.6342505,41.9479762]},"n2199109725":{"id":"n2199109725","loc":[-85.6342743,41.947976]},"n2199109723":{"id":"n2199109723","loc":[-85.6342747,41.948007]},"n2199109721":{"id":"n2199109721","loc":[-85.6343415,41.9476355]},"n2199109719":{"id":"n2199109719","loc":[-85.6343391,41.9474973]},"n2199109717":{"id":"n2199109717","loc":[-85.6343133,41.9474798]},"n2199109715":{"id":"n2199109715","loc":[-85.6342874,41.9474737]},"n2199109709":{"id":"n2199109709","loc":[-85.6349804,41.94815]},"n2199109707":{"id":"n2199109707","loc":[-85.6348915,41.9481505]},"n2199109705":{"id":"n2199109705","loc":[-85.6348917,41.9481692]},"n2199109702":{"id":"n2199109702","loc":[-85.6348522,41.9481694]},"n2199109700":{"id":"n2199109700","loc":[-85.6348532,41.9482679]},"n2199109698":{"id":"n2199109698","loc":[-85.6348315,41.948268]},"n2199109696":{"id":"n2199109696","loc":[-85.6348318,41.9482955]},"n2199109694":{"id":"n2199109694","loc":[-85.6349653,41.9482946]},"n2199109692":{"id":"n2199109692","loc":[-85.6349656,41.9483211]},"n2199109690":{"id":"n2199109690","loc":[-85.634999,41.9483209]},"n2199109688":{"id":"n2199109688","loc":[-85.6349987,41.9482947]},"n2199109686":{"id":"n2199109686","loc":[-85.6351753,41.9482935]},"n2199109684":{"id":"n2199109684","loc":[-85.6351749,41.9482617]},"n2199109682":{"id":"n2199109682","loc":[-85.6351588,41.9482618]},"n2199109680":{"id":"n2199109680","loc":[-85.6351575,41.9481518]},"n2199109678":{"id":"n2199109678","loc":[-85.6350671,41.9481524]},"n2199109676":{"id":"n2199109676","loc":[-85.6350649,41.9479659]},"n2199109674":{"id":"n2199109674","loc":[-85.6349785,41.9479665]},"n2199109671":{"id":"n2199109671","loc":[-85.6343069,41.9483263]},"n2199109669":{"id":"n2199109669","loc":[-85.6343052,41.9482981]},"n2199109658":{"id":"n2199109658","loc":[-85.6343314,41.9480549]},"n2199109656":{"id":"n2199109656","loc":[-85.6343305,41.9480461]},"n2199109654":{"id":"n2199109654","loc":[-85.634435,41.9480468]},"n2199109652":{"id":"n2199109652","loc":[-85.6344342,41.9483746]},"n2199109650":{"id":"n2199109650","loc":[-85.6344629,41.9483727]},"n2199109648":{"id":"n2199109648","loc":[-85.6344637,41.9484561]},"n2199109645":{"id":"n2199109645","loc":[-85.63443,41.9484567]},"n2199109642":{"id":"n2199109642","loc":[-85.6344317,41.948505]},"n185964352":{"id":"n185964352","loc":[-85.6373958,41.9489943]},"n185964351":{"id":"n185964351","loc":[-85.637113,41.9486]},"n2208608825":{"id":"n2208608825","loc":[-85.6354483,41.9494241]},"n2208608823":{"id":"n2208608823","loc":[-85.6360418,41.949416]},"n2208608821":{"id":"n2208608821","loc":[-85.6360458,41.9495802]},"n2208608811":{"id":"n2208608811","loc":[-85.6357458,41.9495843]},"n2208608808":{"id":"n2208608808","loc":[-85.6357508,41.9497835]},"n2208608806":{"id":"n2208608806","loc":[-85.6354573,41.9497875]},"n2208608795":{"id":"n2208608795","loc":[-85.6354595,41.9498778]},"n2199109638":{"id":"n2199109638","loc":[-85.6349605,41.949749]},"n2199109636":{"id":"n2199109636","loc":[-85.6349605,41.9497639]},"n2199109634":{"id":"n2199109634","loc":[-85.6349061,41.94971]},"n2199109632":{"id":"n2199109632","loc":[-85.6349048,41.9496569]},"n2199109630":{"id":"n2199109630","loc":[-85.6348835,41.9496571]},"n2199109628":{"id":"n2199109628","loc":[-85.6348829,41.9497103]},"n2199109626":{"id":"n2199109626","loc":[-85.635227,41.9497738]},"n2199109624":{"id":"n2199109624","loc":[-85.6352184,41.9497787]},"n2199109622":{"id":"n2199109622","loc":[-85.6351181,41.9497806]},"n2199109620":{"id":"n2199109620","loc":[-85.6351181,41.9497456]},"n2199109618":{"id":"n2199109618","loc":[-85.6348842,41.9497651]},"n2199109616":{"id":"n2199109616","loc":[-85.6348827,41.9496238]},"n2199109615":{"id":"n2199109615","loc":[-85.6351268,41.9496206]},"n2199109614":{"id":"n2199109614","loc":[-85.6351261,41.9495891]},"n2199109613":{"id":"n2199109613","loc":[-85.6351957,41.9495881]},"n2199109612":{"id":"n2199109612","loc":[-85.6351924,41.9494515]},"n2199109611":{"id":"n2199109611","loc":[-85.6353997,41.9494488]},"n2199109610":{"id":"n2199109610","loc":[-85.6354074,41.9497715]},"n2189015681":{"id":"n2189015681","loc":[-85.6344229,41.9509639]},"n2189015677":{"id":"n2189015677","loc":[-85.634424,41.9507396]},"n2138493843":{"id":"n2138493843","loc":[-85.6343935,41.9502836]},"n2138493840":{"id":"n2138493840","loc":[-85.634398,41.9506264]},"n354002838":{"id":"n354002838","loc":[-85.6345197,41.9510631]},"n2114807590":{"id":"n2114807590","loc":[-85.634511,41.9499767]},"n185964353":{"id":"n185964353","loc":[-85.6374092,41.9498755]},"n1819849180":{"id":"n1819849180","loc":[-85.6348236,41.94996]},"n1819849115":{"id":"n1819849115","loc":[-85.6354372,41.9499538]},"n1819848921":{"id":"n1819848921","loc":[-85.6348439,41.951064]},"n1819848885":{"id":"n1819848885","loc":[-85.6354575,41.9510578]},"n185984281":{"id":"n185984281","loc":[-85.638075,41.949872]},"n2208608827":{"id":"n2208608827","loc":[-85.6339169,41.9473191]},"n2199109749":{"id":"n2199109749","loc":[-85.6342082,41.9477934]},"n2199109747":{"id":"n2199109747","loc":[-85.6342045,41.9476867]},"n2199109713":{"id":"n2199109713","loc":[-85.6342404,41.9474746]},"n2199109711":{"id":"n2199109711","loc":[-85.6342404,41.9476355]},"n2199109673":{"id":"n2199109673","loc":[-85.6340886,41.9483282]},"n2199109667":{"id":"n2199109667","loc":[-85.6342403,41.9482988]},"n2199109665":{"id":"n2199109665","loc":[-85.6342386,41.9482116]},"n2199109662":{"id":"n2199109662","loc":[-85.6340861,41.9482135]},"n2199109660":{"id":"n2199109660","loc":[-85.6340802,41.9480562]},"n2199109640":{"id":"n2199109640","loc":[-85.6340928,41.9485063]},"n354031366":{"id":"n354031366","loc":[-85.6341667,41.9477778],"tags":{"amenity":"place_of_worship","name":"Faith Tabernacle Church","religion":"christian"}},"n2189015686":{"id":"n2189015686","loc":[-85.6337798,41.95099]},"n2189015684":{"id":"n2189015684","loc":[-85.6337794,41.9509674]},"n2189015673":{"id":"n2189015673","loc":[-85.6337501,41.9507457]},"n2189015669":{"id":"n2189015669","loc":[-85.6337501,41.9506974]},"n2189015665":{"id":"n2189015665","loc":[-85.6339034,41.9506959]},"n2189015662":{"id":"n2189015662","loc":[-85.6339015,41.950436]},"n2189015658":{"id":"n2189015658","loc":[-85.6334916,41.9504376]},"n2189015655":{"id":"n2189015655","loc":[-85.6334939,41.9507558]},"n2189015650":{"id":"n2189015650","loc":[-85.6334543,41.950756]},"n2189015649":{"id":"n2189015649","loc":[-85.633456,41.9509915]},"n2138493842":{"id":"n2138493842","loc":[-85.6339937,41.9502836]},"n2138493841":{"id":"n2138493841","loc":[-85.6339983,41.9506281]},"n2114807579":{"id":"n2114807579","loc":[-85.6333644,41.9510682]},"n2114807573":{"id":"n2114807573","loc":[-85.6333557,41.9499819]},"n354031330":{"id":"n354031330","loc":[-85.6341667,41.9497222],"tags":{"amenity":"place_of_worship","name":"Trinity Episcopal Church","religion":"christian"}},"n185960794":{"id":"n185960794","loc":[-85.633307,41.9537]},"n185964357":{"id":"n185964357","loc":[-85.637432,41.952399]},"n185964358":{"id":"n185964358","loc":[-85.637452,41.953665]},"n185964359":{"id":"n185964359","loc":[-85.63746,41.954658]},"n185964360":{"id":"n185964360","loc":[-85.637473,41.95592]},"n185964361":{"id":"n185964361","loc":[-85.637468,41.956906]},"n185964362":{"id":"n185964362","loc":[-85.637483,41.958313]},"n185966957":{"id":"n185966957","loc":[-85.633361,41.957422]},"n185975351":{"id":"n185975351","loc":[-85.63334,41.9559]},"n185978784":{"id":"n185978784","loc":[-85.633311,41.954679]},"n185986157":{"id":"n185986157","loc":[-85.633287,41.952426]},"n185986158":{"id":"n185986158","loc":[-85.6333607,41.9582301],"tags":{"highway":"turning_circle"}},"w17965182":{"id":"w17965182","tags":{"highway":"residential","name":"W Prutzman St"},"nodes":["n185965289","n2189153241","n185965291"]},"w208627205":{"id":"w208627205","tags":{"area":"yes","building":"yes"},"nodes":["n2189015649","n2189015650","n2189015655","n2189015658","n2189015662","n2189015665","n2189015669","n2189015673","n2189015677","n2189015681","n2189015684","n2189015686","n2189015649"]},"w209717042":{"id":"w209717042","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"presbyterian","name":"First Presbyterian Church","religion":"christian"},"nodes":["n2199109610","n2199109611","n2199109612","n2199109613","n2199109614","n2199109615","n2199109616","n2199109630","n2199109632","n2199109634","n2199109628","n2199109618","n2199109636","n2199109638","n2199109620","n2199109622","n2199109624","n2199109626","n2199109610"]},"w209717045":{"id":"w209717045","tags":{"area":"yes","building":"yes"},"nodes":["n2199109711","n2199109713","n2199109715","n2199109717","n2199109719","n2199109721","n2199109711"]},"w209717047":{"id":"w209717047","tags":{"area":"yes","building":"yes"},"nodes":["n2199109739","n2199109741","n2199109743","n2199109745","n2199109747","n2199109749","n2199109751","n2199109753","n2199109739"]},"w209717044":{"id":"w209717044","tags":{"area":"yes","building":"yes"},"nodes":["n2199109674","n2199109676","n2199109678","n2199109680","n2199109682","n2199109684","n2199109686","n2199109688","n2199109690","n2199109692","n2199109694","n2199109696","n2199109698","n2199109700","n2199109702","n2199109705","n2199109707","n2199109709","n2199109674"]},"w210822776":{"id":"w210822776","tags":{"highway":"service","service":"alley","surface":"unpaved"},"nodes":["n2208608795","n2208608806","n2208608825","n2208608800","n2189153241"]},"w210822778":{"id":"w210822778","tags":{"highway":"service","service":"alley"},"nodes":["n2208608826","n2208608827"]},"w209717050":{"id":"w209717050","tags":{"area":"yes","building":"yes"},"nodes":["n2199109787","n2199109789","n2199109791","n2199109793","n2199109795","n2199109797","n2199109799","n2199109802","n2199109804","n2199109806","n2199109787"]},"w17965097":{"id":"w17965097","tags":{"highway":"residential","name":"Maple Street"},"nodes":["n185964351","n185964352","n185964353","n185964355","n185964357","n185964358","n185964359","n185964360","n185964361","n185964362"]},"w17965856":{"id":"w17965856","tags":{"highway":"residential","name":"W Kelsey St"},"nodes":["n185971578","n2208608800","n185971580","n185964351"]},"w17967444":{"id":"w17967444","tags":{"highway":"residential","name":"East Street"},"nodes":["n185966937","n185978969","n185986155","n185985812","n185986157","n185960794","n185978784","n185975351","n185966957","n185986158"]},"w17967764":{"id":"w17967764","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n185984017","n185964351"]},"w170848329":{"id":"w170848329","tags":{"leisure":"park","name":"LaFayette Park"},"nodes":["n1819849180","n1819849115","n1819848885","n1819848921","n1819849180"]},"w17967208":{"id":"w17967208","tags":{"highway":"residential","name":"West Bennett Street"},"nodes":["n185978390","n2208608795","n185984020","n185964353","n185984281"]},"w17965349":{"id":"w17965349","tags":{"highway":"residential","name":"E Prutzman St"},"nodes":["n185966937","n2208608827","n185965289"]},"w209717049":{"id":"w209717049","tags":{"area":"yes","building":"yes"},"nodes":["n2199109761","n2199109762","n2199109763","n2199109765","n2199109768","n2199109770","n2199109783","n2199109785","n2199109761"]},"w203841840":{"id":"w203841840","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493840","n2138493841","n2138493842","n2138493843","n2138493840"]},"w209717043":{"id":"w209717043","tags":{"amenity":"place_of_worship","area":"yes","building":"church","denomination":"methodist","name":"First United Methodist Church","religion":"christian"},"nodes":["n2199109640","n2199109642","n2199109645","n2199109648","n2199109650","n2199109652","n2199109654","n2199109656","n2199109658","n2199109660","n2199109662","n2199109665","n2199109667","n2199109669","n2199109671","n2199109673","n2199109640"]},"w201484341":{"id":"w201484341","tags":{"amenity":"school","name":"Hoppin School"},"nodes":["n354002838","n2114807579","n2114807573","n2114807590","n354002838"]},"w209717046":{"id":"w209717046","tags":{"area":"yes","building":"yes"},"nodes":["n2199109723","n2199109725","n2199109727","n2199109729","n2199109731","n2199109733","n2199109735","n2199109737","n2199109723"]},"w210822777":{"id":"w210822777","tags":{"amenity":"parking","area":"yes"},"nodes":["n2208608806","n2208608808","n2208608811","n2208608821","n2208608823","n2208608825","n2208608806"]},"n185954965":{"id":"n185954965","loc":[-85.6191189,41.9441922]},"n185954968":{"id":"n185954968","loc":[-85.6194384,41.9442405]},"n185954970":{"id":"n185954970","loc":[-85.6196543,41.9443252]},"n185954972":{"id":"n185954972","loc":[-85.6197862,41.9444539]},"n354002931":{"id":"n354002931","loc":[-85.6198991,41.9455269]},"n354030853":{"id":"n354030853","loc":[-85.6219444,41.9455556],"tags":{"amenity":"place_of_worship","name":"Grant Chapel","religion":"christian"}},"n367815963":{"id":"n367815963","loc":[-85.6202778,41.9461111],"tags":{"building":"yes","name":"George Washington Carver Community Center"}},"n185947331":{"id":"n185947331","loc":[-85.618779,41.943269]},"n185947333":{"id":"n185947333","loc":[-85.618795,41.943511]},"n185947336":{"id":"n185947336","loc":[-85.618711,41.94413]},"n185947338":{"id":"n185947338","loc":[-85.618704,41.944189]},"n185947339":{"id":"n185947339","loc":[-85.618597,41.944337]},"n185947340":{"id":"n185947340","loc":[-85.618485,41.944528]},"n185947343":{"id":"n185947343","loc":[-85.618442,41.944716]},"n185947345":{"id":"n185947345","loc":[-85.618457,41.945107]},"n185947347":{"id":"n185947347","loc":[-85.618296,41.945338]},"n185947374":{"id":"n185947374","loc":[-85.616748,41.944453]},"n185947375":{"id":"n185947375","loc":[-85.616813,41.944646]},"n185947376":{"id":"n185947376","loc":[-85.616859,41.945196]},"n185947377":{"id":"n185947377","loc":[-85.616941,41.945352]},"n185947406":{"id":"n185947406","loc":[-85.618184,41.944227]},"n185947409":{"id":"n185947409","loc":[-85.617911,41.943875]},"n185947410":{"id":"n185947410","loc":[-85.617579,41.943682]},"n185947411":{"id":"n185947411","loc":[-85.61713,41.943589]},"n185947412":{"id":"n185947412","loc":[-85.616549,41.943559]},"n185947414":{"id":"n185947414","loc":[-85.616482,41.943556]},"n185947464":{"id":"n185947464","loc":[-85.616526,41.943788]},"n185947466":{"id":"n185947466","loc":[-85.616504,41.944002]},"n185948863":{"id":"n185948863","loc":[-85.619017,41.943391]},"n185948865":{"id":"n185948865","loc":[-85.619059,41.943368]},"n185955022":{"id":"n185955022","loc":[-85.620088,41.945571]},"n185955025":{"id":"n185955025","loc":[-85.620051,41.945505]},"n185955028":{"id":"n185955028","loc":[-85.62001,41.94541]},"n185980371":{"id":"n185980371","loc":[-85.620982,41.944742]},"n185980398":{"id":"n185980398","loc":[-85.621305,41.944782]},"n185980401":{"id":"n185980401","loc":[-85.621174,41.944819]},"n185980403":{"id":"n185980403","loc":[-85.621029,41.944871]},"n185980405":{"id":"n185980405","loc":[-85.620741,41.945011]},"n185980407":{"id":"n185980407","loc":[-85.620616,41.945085]},"n185980409":{"id":"n185980409","loc":[-85.620506,41.945172]},"n185980411":{"id":"n185980411","loc":[-85.620394,41.945273]},"n185980413":{"id":"n185980413","loc":[-85.620316,41.94536]},"n185980415":{"id":"n185980415","loc":[-85.620257,41.945452]},"n185980417":{"id":"n185980417","loc":[-85.620212,41.945535]},"n185985910":{"id":"n185985910","loc":[-85.620101,41.945811]},"n185985912":{"id":"n185985912","loc":[-85.620081,41.945937]},"n1475283972":{"id":"n1475283972","loc":[-85.6198991,41.9437179]},"n1475283982":{"id":"n1475283982","loc":[-85.6195022,41.9433463]},"n1475284007":{"id":"n1475284007","loc":[-85.6193037,41.9433383]},"n1475284040":{"id":"n1475284040","loc":[-85.6197329,41.9434121]},"n1475284044":{"id":"n1475284044","loc":[-85.6198756,41.9435363]},"n1475284050":{"id":"n1475284050","loc":[-85.6199689,41.9432106]},"n1475284053":{"id":"n1475284053","loc":[-85.6198943,41.9432921]},"n185954974":{"id":"n185954974","loc":[-85.6198296,41.94473]},"n185954977":{"id":"n185954977","loc":[-85.6200474,41.9447384]},"n2196831365":{"id":"n2196831365","loc":[-85.6202259,41.9460883]},"n2196831366":{"id":"n2196831366","loc":[-85.6202245,41.9458642]},"n2196831367":{"id":"n2196831367","loc":[-85.6205184,41.9458631]},"n2196831368":{"id":"n2196831368","loc":[-85.6205189,41.9459437]},"n2196831369":{"id":"n2196831369","loc":[-85.6203879,41.9459441]},"n2196831370":{"id":"n2196831370","loc":[-85.6203888,41.9460878]},"n2196831371":{"id":"n2196831371","loc":[-85.6184046,41.9465663]},"n2196831372":{"id":"n2196831372","loc":[-85.6191563,41.9465618]},"n2196831373":{"id":"n2196831373","loc":[-85.6191536,41.946319]},"n2196831374":{"id":"n2196831374","loc":[-85.6187356,41.9463216]},"n2196831375":{"id":"n2196831375","loc":[-85.6187334,41.9461197]},"n2196831376":{"id":"n2196831376","loc":[-85.6193167,41.9461162]},"n2196831377":{"id":"n2196831377","loc":[-85.6193156,41.9460229]},"n2196831378":{"id":"n2196831378","loc":[-85.619622,41.946021]},"n2196831379":{"id":"n2196831379","loc":[-85.6196237,41.9461712]},"n2196831380":{"id":"n2196831380","loc":[-85.6197702,41.9461703]},"n2196831381":{"id":"n2196831381","loc":[-85.6197685,41.9460202]},"n2196831382":{"id":"n2196831382","loc":[-85.6197323,41.9460204]},"n2196831383":{"id":"n2196831383","loc":[-85.6197305,41.9458563]},"n2196831384":{"id":"n2196831384","loc":[-85.6196165,41.945857]},"n2196831385":{"id":"n2196831385","loc":[-85.6196156,41.9457764]},"n2196831386":{"id":"n2196831386","loc":[-85.6194472,41.9457775]},"n2196831387":{"id":"n2196831387","loc":[-85.6194151,41.9457777]},"n2196831388":{"id":"n2196831388","loc":[-85.6183779,41.9457883]},"n2196831389":{"id":"n2196831389","loc":[-85.6183842,41.9461317]},"n2196831390":{"id":"n2196831390","loc":[-85.6185026,41.9461304]},"n2196831391":{"id":"n2196831391","loc":[-85.6185061,41.9463194]},"n2196831392":{"id":"n2196831392","loc":[-85.6184001,41.9463205]},"n2196831393":{"id":"n2196831393","loc":[-85.6182482,41.9464163]},"n2196831394":{"id":"n2196831394","loc":[-85.6182467,41.9463193]},"n2196831395":{"id":"n2196831395","loc":[-85.6180389,41.946321]},"n2196831397":{"id":"n2196831397","loc":[-85.6180404,41.946418]},"n185947303":{"id":"n185947303","loc":[-85.611074,41.943389]},"n185947304":{"id":"n185947304","loc":[-85.611332,41.943267]},"n185947305":{"id":"n185947305","loc":[-85.611635,41.943218]},"n185947306":{"id":"n185947306","loc":[-85.612762,41.943311]},"n185947308":{"id":"n185947308","loc":[-85.613027,41.943327]},"n185947310":{"id":"n185947310","loc":[-85.615377,41.942996]},"n185947312":{"id":"n185947312","loc":[-85.615701,41.943007]},"n185947314":{"id":"n185947314","loc":[-85.61604,41.943067]},"n185947315":{"id":"n185947315","loc":[-85.61626,41.943083]},"n185947316":{"id":"n185947316","loc":[-85.616507,41.943048]},"n185947319":{"id":"n185947319","loc":[-85.616702,41.94299]},"n185947321":{"id":"n185947321","loc":[-85.617078,41.942918]},"n185947322":{"id":"n185947322","loc":[-85.617366,41.942973]},"n185947323":{"id":"n185947323","loc":[-85.617601,41.943033]},"n185947325":{"id":"n185947325","loc":[-85.617799,41.943027]},"n185947327":{"id":"n185947327","loc":[-85.618264,41.942961]},"n185947328":{"id":"n185947328","loc":[-85.618508,41.942972]},"n185947329":{"id":"n185947329","loc":[-85.618707,41.943076]},"n185947361":{"id":"n185947361","loc":[-85.615356,41.944922]},"n185947363":{"id":"n185947363","loc":[-85.61536,41.944893]},"n185947365":{"id":"n185947365","loc":[-85.615406,41.944547]},"n185947367":{"id":"n185947367","loc":[-85.61548,41.944351]},"n185947369":{"id":"n185947369","loc":[-85.615805,41.94419]},"n185947371":{"id":"n185947371","loc":[-85.616166,41.944156]},"n185947373":{"id":"n185947373","loc":[-85.616411,41.944197]},"n185947416":{"id":"n185947416","loc":[-85.616335,41.94343]},"n185947417":{"id":"n185947417","loc":[-85.616069,41.943293]},"n185947419":{"id":"n185947419","loc":[-85.615803,41.943249]},"n185947420":{"id":"n185947420","loc":[-85.615524,41.943342]},"n185947421":{"id":"n185947421","loc":[-85.615311,41.94353]},"n185947422":{"id":"n185947422","loc":[-85.614338,41.943558]},"n185947423":{"id":"n185947423","loc":[-85.61422,41.94369]},"n185947425":{"id":"n185947425","loc":[-85.614221,41.944224]},"n185947427":{"id":"n185947427","loc":[-85.614198,41.944888]},"n185947429":{"id":"n185947429","loc":[-85.614221,41.945439]},"n185947468":{"id":"n185947468","loc":[-85.615908,41.944756]},"n185947470":{"id":"n185947470","loc":[-85.615871,41.944888]},"n185947472":{"id":"n185947472","loc":[-85.615878,41.94507]},"n185955153":{"id":"n185955153","loc":[-85.620087,41.947701]},"n185960690":{"id":"n185960690","loc":[-85.620141,41.951901]},"n185978817":{"id":"n185978817","loc":[-85.617193,41.954706]},"n185985916":{"id":"n185985916","loc":[-85.620088,41.94758]},"n185985918":{"id":"n185985918","loc":[-85.620133,41.951538]},"n185985919":{"id":"n185985919","loc":[-85.62013,41.952104]},"n185985920":{"id":"n185985920","loc":[-85.620104,41.952305]},"n185985921":{"id":"n185985921","loc":[-85.620062,41.952499]},"n185985922":{"id":"n185985922","loc":[-85.619993,41.952702]},"n185985925":{"id":"n185985925","loc":[-85.619879,41.952986]},"n185985927":{"id":"n185985927","loc":[-85.619689,41.95329]},"n185985928":{"id":"n185985928","loc":[-85.619508,41.953521]},"n185985929":{"id":"n185985929","loc":[-85.619286,41.953728]},"n185985930":{"id":"n185985930","loc":[-85.618925,41.954007]},"n185985931":{"id":"n185985931","loc":[-85.618638,41.954189]},"n185985932":{"id":"n185985932","loc":[-85.61831,41.954358]},"n185985934":{"id":"n185985934","loc":[-85.618015,41.954485]},"n185985936":{"id":"n185985936","loc":[-85.617606,41.954611]},"n1475283975":{"id":"n1475283975","loc":[-85.6150935,41.9434118]},"n1475283979":{"id":"n1475283979","loc":[-85.6193367,41.9430252]},"n1475283989":{"id":"n1475283989","loc":[-85.6104771,41.9455269]},"n1475283990":{"id":"n1475283990","loc":[-85.6104771,41.9437179]},"n1475283994":{"id":"n1475283994","loc":[-85.6198042,41.9429763]},"n1475283998":{"id":"n1475283998","loc":[-85.6192101,41.9426716]},"n1475284000":{"id":"n1475284000","loc":[-85.6198622,41.942836]},"n1475284002":{"id":"n1475284002","loc":[-85.6163262,41.9427688]},"n1475284006":{"id":"n1475284006","loc":[-85.6179527,41.9429168]},"n1475284029":{"id":"n1475284029","loc":[-85.6197195,41.9427278]},"n1475284038":{"id":"n1475284038","loc":[-85.6194405,41.9427837]},"n1475284052":{"id":"n1475284052","loc":[-85.6153225,41.942841]},"n1475284055":{"id":"n1475284055","loc":[-85.6129233,41.9437179]},"n2139966627":{"id":"n2139966627","loc":[-85.61958,41.9427558]},"w17966773":{"id":"w17966773","tags":{"highway":"secondary","name":"E Michigan Ave","ref":"M 60"},"nodes":["n185980372","n185980398","n185980401","n185980403","n185980405","n185980407","n185980409","n185980411","n185980413","n185980415","n185980417","n185955019"]},"w17964043":{"id":"w17964043","tags":{"highway":"residential"},"nodes":["n185955019","n185955022","n185955025","n185955028","n185954977","n185971477","n1475284050","n1475284000","n1475284029","n2139966627","n1475284038"]},"w17962834":{"id":"w17962834","tags":{"highway":"service"},"nodes":["n185947316","n185947414","n185947464","n185947466","n185947373","n185947468","n185947470","n185947472","n185947474"]},"w209470310":{"id":"w209470310","tags":{"area":"yes","building":"yes"},"nodes":["n2196831393","n2196831394","n2196831395","n2196831397","n2196831393"]},"w17963058":{"id":"w17963058","tags":{"highway":"service"},"nodes":["n185947333","n185948863","n185948865","n1475284007","n1475283982","n1475284040","n1475284044"]},"w17962823":{"id":"w17962823","tags":{"highway":"service"},"nodes":["n185947359","n185947361","n185947363","n185947365","n185947367","n185947369","n185947371","n185947373","n185947374","n185947375","n185947376","n185947377","n185947378"]},"w17962821":{"id":"w17962821","tags":{"highway":"service"},"nodes":["n185947303","n185947304","n185947305","n185947306","n185947308","n185947310","n185947312","n185947314","n185947315","n185947316","n185947319","n185947321","n185947322","n185947323","n185947325","n185947327","n185947328","n185947329","n185947331","n185947333","n185947336","n185947338","n185947339","n185947340","n185947343","n185947345","n185947347","n185947349"]},"w134150798":{"id":"w134150798","tags":{"amenity":"grave_yard","name":"Riverside Cemetery"},"nodes":["n354002931","n1475283972","n1475284053","n1475283994","n1475283979","n1475283998","n1475284006","n1475284002","n1475284052","n1475283975","n1475284055","n1475283990","n1475283989","n354002931"]},"w17964040":{"id":"w17964040","tags":{"highway":"service"},"nodes":["n185947336","n185954965","n185954968","n185954970","n185954972","n185954974","n185954977"]},"w209470308":{"id":"w209470308","tags":{"area":"yes","building":"yes"},"nodes":["n2196831365","n2196831366","n2196831367","n2196831368","n2196831369","n2196831370","n2196831365"]},"w17962828":{"id":"w17962828","tags":{"highway":"service"},"nodes":["n185947340","n185947406","n185947409","n185947410","n185947411","n185947412","n185947414","n185947416","n185947417","n185947419","n185947420","n185947421","n185947422","n185947423","n185947425","n185947427","n185947429"]},"w209470309":{"id":"w209470309","tags":{"area":"yes","building":"yes"},"nodes":["n2196831371","n2196831372","n2196831373","n2196831374","n2196831375","n2196831376","n2196831377","n2196831378","n2196831379","n2196831380","n2196831381","n2196831382","n2196831383","n2196831384","n2196831385","n2196831386","n2196831387","n2196831388","n2196831389","n2196831390","n2196831391","n2196831392","n2196831371"]},"w17967415":{"id":"w17967415","tags":{"highway":"secondary","name":"Jefferson St","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185955019","n185985910","n185985912","n185985914","n185961367","n185985916","n185955153","n185965105","n185974697","n185955120","n185985918","n185960690","n185985919","n185985920","n185985921","n185985922","n185985925","n185985927","n185985928","n185985929","n185985930","n185985931","n185985932","n185985934","n185985936","n185978817"]},"w17966772":{"id":"w17966772","tags":{"highway":"unclassified","name":"E Michigan Ave","name_1":"State Highway 60"},"nodes":["n185954977","n185980371","n185980372"]},"n185958500":{"id":"n185958500","loc":[-85.621591,41.941075]},"n185963110":{"id":"n185963110","loc":[-85.6204416,41.9408882]},"n2139966628":{"id":"n2139966628","loc":[-85.6196431,41.9426467],"tags":{"leisure":"fishing"}},"n2139966630":{"id":"n2139966630","loc":[-85.6199354,41.9429616]},"n2199127051":{"id":"n2199127051","loc":[-85.6170556,41.939696]},"n2199127052":{"id":"n2199127052","loc":[-85.6170536,41.9392909]},"n2199127053":{"id":"n2199127053","loc":[-85.6172067,41.9392905]},"n2199127054":{"id":"n2199127054","loc":[-85.6172061,41.9391853]},"n2199127055":{"id":"n2199127055","loc":[-85.6171481,41.9391854]},"n2199127060":{"id":"n2199127060","loc":[-85.6167389,41.9392896]},"n2199127061":{"id":"n2199127061","loc":[-85.6168728,41.9392892]},"n2199127062":{"id":"n2199127062","loc":[-85.6168747,41.9396965]},"n2199127071":{"id":"n2199127071","loc":[-85.620196,41.9399446]},"n2199127072":{"id":"n2199127072","loc":[-85.620193,41.9397316]},"n2199127073":{"id":"n2199127073","loc":[-85.6200381,41.9397328]},"n2199127074":{"id":"n2199127074","loc":[-85.6200412,41.9399458]},"n2199127075":{"id":"n2199127075","loc":[-85.6203606,41.9399939]},"n2199127076":{"id":"n2199127076","loc":[-85.6205527,41.9399922]},"n2199127077":{"id":"n2199127077","loc":[-85.6205482,41.9397115]},"n2199127078":{"id":"n2199127078","loc":[-85.6204132,41.9397124]},"n2199127079":{"id":"n2199127079","loc":[-85.6204144,41.9396341]},"n2199127080":{"id":"n2199127080","loc":[-85.6205699,41.9396324]},"n2199127081":{"id":"n2199127081","loc":[-85.6205722,41.939498]},"n2199127082":{"id":"n2199127082","loc":[-85.6204064,41.9394997]},"n2199127083":{"id":"n2199127083","loc":[-85.6204087,41.939561]},"n2199127084":{"id":"n2199127084","loc":[-85.6203103,41.9395618]},"n2199127085":{"id":"n2199127085","loc":[-85.620308,41.9396069]},"n2199127086":{"id":"n2199127086","loc":[-85.6200347,41.9396086]},"n2199127087":{"id":"n2199127087","loc":[-85.6200382,41.9397141]},"n2199127088":{"id":"n2199127088","loc":[-85.6202257,41.9397149]},"n2199127089":{"id":"n2199127089","loc":[-85.6202269,41.9399182]},"n2199127090":{"id":"n2199127090","loc":[-85.6203595,41.9399199]},"n2199127091":{"id":"n2199127091","loc":[-85.6212335,41.939688]},"n2199127092":{"id":"n2199127092","loc":[-85.6212328,41.939595]},"n2199127093":{"id":"n2199127093","loc":[-85.6208807,41.9395966]},"n2199127094":{"id":"n2199127094","loc":[-85.6208815,41.9396896]},"n2199127095":{"id":"n2199127095","loc":[-85.6208676,41.9396872]},"n2199127096":{"id":"n2199127096","loc":[-85.6208583,41.9393539]},"n2199127097":{"id":"n2199127097","loc":[-85.6207006,41.9393563]},"n2199127098":{"id":"n2199127098","loc":[-85.6207099,41.9396896]},"n185967054":{"id":"n185967054","loc":[-85.6173384,41.9356126]},"n185967063":{"id":"n185967063","loc":[-85.617371,41.936243]},"n185967065":{"id":"n185967065","loc":[-85.617337,41.936299]},"n185967068":{"id":"n185967068","loc":[-85.617321,41.936373]},"n185967070":{"id":"n185967070","loc":[-85.6173562,41.9366969]},"n185967074":{"id":"n185967074","loc":[-85.6173635,41.9377414]},"n185967075":{"id":"n185967075","loc":[-85.6173696,41.9381886]},"n185967076":{"id":"n185967076","loc":[-85.617372,41.938535]},"n2199127056":{"id":"n2199127056","loc":[-85.617147,41.9389616]},"n2199127057":{"id":"n2199127057","loc":[-85.6172136,41.9389614]},"n2199127058":{"id":"n2199127058","loc":[-85.6172123,41.9386909]},"n2199127059":{"id":"n2199127059","loc":[-85.616736,41.9386922]},"n2203921041":{"id":"n2203921041","loc":[-85.6173018,41.9346369]},"w203983952":{"id":"w203983952","tags":{"highway":"service"},"nodes":["n2139966627","n1819800319"]},"w209718301":{"id":"w209718301","tags":{"area":"yes","building":"yes"},"nodes":["n2199127051","n2199127052","n2199127053","n2199127054","n2199127055","n2199127056","n2199127057","n2199127058","n2199127059","n2199127060","n2199127061","n2199127062","n2199127051"]},"w209718304":{"id":"w209718304","tags":{"area":"yes","building":"yes"},"nodes":["n2199127071","n2199127072","n2199127073","n2199127074","n2199127071"]},"w17964961":{"id":"w17964961","tags":{"highway":"residential","name":"Whipple St"},"nodes":["n185963099","n185963110"]},"w17964489":{"id":"w17964489","tags":{"highway":"residential","name":"Jackson St"},"nodes":["n185958498","n185958500"]},"w203983953":{"id":"w203983953","tags":{"area":"yes","leisure":"park","name":"Marina Park"},"nodes":["n1475283994","n1475283979","n1475283998","n2139966629","n2139966625","n1819800319","n2139966623","n2139966622","n2139966621","n2139966630","n1475283994"]},"w17965366":{"id":"w17965366","tags":{"highway":"residential","name":"14th St"},"nodes":["n2203921041","n185967054","n185967063","n185967065","n185967068","n185967070","n185967074","n185967075","n185967076","n185967077"]},"w209718306":{"id":"w209718306","tags":{"area":"yes","building":"yes"},"nodes":["n2199127091","n2199127092","n2199127093","n2199127094","n2199127091"]},"w209718307":{"id":"w209718307","tags":{"area":"yes","building":"yes"},"nodes":["n2199127095","n2199127096","n2199127097","n2199127098","n2199127095"]},"w209718305":{"id":"w209718305","tags":{"area":"yes","building":"yes"},"nodes":["n2199127075","n2199127076","n2199127077","n2199127078","n2199127079","n2199127080","n2199127081","n2199127082","n2199127083","n2199127084","n2199127085","n2199127086","n2199127087","n2199127088","n2199127089","n2199127090","n2199127075"]},"n185960199":{"id":"n185960199","loc":[-85.62965,41.95469]},"n185980737":{"id":"n185980737","loc":[-85.629083,41.953725]},"n2114807561":{"id":"n2114807561","loc":[-85.6297681,41.9524688]},"n2114807597":{"id":"n2114807597","loc":[-85.6296517,41.952563]},"n185960197":{"id":"n185960197","loc":[-85.629676,41.9537314]},"n185978791":{"id":"n185978791","loc":[-85.6244542,41.9547066]},"w17967573":{"id":"w17967573","tags":{"highway":"residential","name":"E Wheeler St"},"nodes":["n185960195","n2114807561","n185968102","n185967430","n185986157","n185978392"]},"w17966553":{"id":"w17966553","tags":{"highway":"residential","name":"East Hoffman Street"},"nodes":["n185971631","n185978784","n185967432","n185968106","n185960199","n185978787","n185978790","n185978791"]},"w17966787":{"id":"w17966787","tags":{"highway":"residential","name":"East Cushman Street"},"nodes":["n185980735","n185980737","n185960197","n185968104","n185960792"]},"w17964723":{"id":"w17964723","tags":{"highway":"residential","name":"Cushman Street"},"nodes":["n185960792","n185960794","n185960796"]},"w17964654":{"id":"w17964654","tags":{"highway":"residential","name":"Pine Street"},"nodes":["n185960195","n2114807597","n185960197","n185960199"]},"n1819848862":{"id":"n1819848862","loc":[-85.6346087,41.9545845]},"n1819848935":{"id":"n1819848935","loc":[-85.6345948,41.9537717]},"n1819848973":{"id":"n1819848973","loc":[-85.6334247,41.9537827]},"n1819848997":{"id":"n1819848997","loc":[-85.6334386,41.9545956]},"n2189015861":{"id":"n2189015861","loc":[-85.6375906,41.954836]},"n2189015865":{"id":"n2189015865","loc":[-85.6383307,41.9548291]},"n2189015867":{"id":"n2189015867","loc":[-85.6383337,41.9550072]},"n2189015868":{"id":"n2189015868","loc":[-85.6380986,41.9550094]},"n2189015869":{"id":"n2189015869","loc":[-85.6381005,41.9551226]},"n2199109808":{"id":"n2199109808","loc":[-85.6372702,41.9522894]},"n2199109810":{"id":"n2199109810","loc":[-85.6372677,41.9521583]},"n2199109812":{"id":"n2199109812","loc":[-85.6369505,41.9521617]},"n2199109814":{"id":"n2199109814","loc":[-85.636953,41.9522927]},"n185952156":{"id":"n185952156","loc":[-85.640983,41.9546557]},"n185953423":{"id":"n185953423","loc":[-85.641871,41.954652]},"n185971637":{"id":"n185971637","loc":[-85.641583,41.95465]},"n185971639":{"id":"n185971639","loc":[-85.6421344,41.9546444]},"n185971642":{"id":"n185971642","loc":[-85.6428264,41.9545612]},"n185971648":{"id":"n185971648","loc":[-85.6436023,41.9544262]},"n185975066":{"id":"n185975066","loc":[-85.640532,41.953638]},"n185975067":{"id":"n185975067","loc":[-85.64079,41.953638]},"n185982166":{"id":"n185982166","loc":[-85.6399012,41.9523817]},"n2189015858":{"id":"n2189015858","loc":[-85.6376104,41.9560138]},"n2189015870":{"id":"n2189015870","loc":[-85.6386794,41.9551172]},"n2189015871":{"id":"n2189015871","loc":[-85.6386817,41.955256]},"n2189015873":{"id":"n2189015873","loc":[-85.6385437,41.9552573]},"n2189015876":{"id":"n2189015876","loc":[-85.638555,41.9559278]},"n2189015879":{"id":"n2189015879","loc":[-85.6384954,41.9559283]},"n2189015882":{"id":"n2189015882","loc":[-85.6384965,41.9559935]},"n2189015885":{"id":"n2189015885","loc":[-85.6383533,41.9559949]},"n2189015888":{"id":"n2189015888","loc":[-85.638351,41.9558607]},"n2189015891":{"id":"n2189015891","loc":[-85.6382178,41.9558619]},"n2189015894":{"id":"n2189015894","loc":[-85.6382203,41.956008]},"w208627223":{"id":"w208627223","tags":{"area":"yes","building":"yes"},"nodes":["n2189015858","n2189015861","n2189015865","n2189015867","n2189015868","n2189015869","n2189015870","n2189015871","n2189015873","n2189015876","n2189015879","n2189015882","n2189015885","n2189015888","n2189015891","n2189015894","n2189015858"]},"w170848328":{"id":"w170848328","tags":{"leisure":"park","name":"Bowman Park"},"nodes":["n1819848935","n1819848973","n1819848997","n1819848862","n1819848935"]},"w17965866":{"id":"w17965866","tags":{"highway":"residential","name":"West Hoffman Street"},"nodes":["n185971631","n185971632","n185964359","n185965025","n1475293264","n185952156","n185971637","n185953423","n185971639","n185971642","n185971648"]},"w209717051":{"id":"w209717051","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"baptist","name":"Calvary Missionary Baptist Church","religion":"christian"},"nodes":["n2199109808","n2199109810","n2199109812","n2199109814","n2199109808"]},"w17966172":{"id":"w17966172","tags":{"highway":"residential","name":"West Cushman Street"},"nodes":["n185960796","n185975064","n185964358","n185965023","n1475293222","n185975066","n185975067"]},"w17966975":{"id":"w17966975","tags":{"highway":"residential","name":"W Wheeler St"},"nodes":["n185978392","n185982163","n185964357","n185965021","n1475293261","n185982166"]},"n185960684":{"id":"n185960684","loc":[-85.622687,41.951885]},"n185960686":{"id":"n185960686","loc":[-85.622492,41.951901]},"n185978795":{"id":"n185978795","loc":[-85.6240991,41.954708]},"n185978803":{"id":"n185978803","loc":[-85.623348,41.954547]},"n185978806":{"id":"n185978806","loc":[-85.623123,41.954502]},"n185978808":{"id":"n185978808","loc":[-85.622923,41.954469]},"n185978810":{"id":"n185978810","loc":[-85.622787,41.954457]},"n185978811":{"id":"n185978811","loc":[-85.622612,41.954458]},"n185978813":{"id":"n185978813","loc":[-85.622368,41.954472]},"n1819790545":{"id":"n1819790545","loc":[-85.6240295,41.9548949]},"n1819790621":{"id":"n1819790621","loc":[-85.6235789,41.954855]},"n1819790664":{"id":"n1819790664","loc":[-85.6238363,41.9549507]},"n1819790683":{"id":"n1819790683","loc":[-85.6224727,41.9545921]},"n1819790730":{"id":"n1819790730","loc":[-85.6227527,41.9545795]},"n1819790740":{"id":"n1819790740","loc":[-85.6240402,41.9550784]},"n1819790831":{"id":"n1819790831","loc":[-85.624126,41.9549986]},"n1819790861":{"id":"n1819790861","loc":[-85.6231712,41.9546872]},"n1819790887":{"id":"n1819790887","loc":[-85.6242762,41.955206]},"n2168544739":{"id":"n2168544739","loc":[-85.6249102,41.952801]},"n2168544740":{"id":"n2168544740","loc":[-85.6251859,41.9527564]},"n2168544741":{"id":"n2168544741","loc":[-85.6255515,41.9527921]},"n2168544742":{"id":"n2168544742","loc":[-85.626001,41.9529481]},"n2168544743":{"id":"n2168544743","loc":[-85.6265284,41.9529838]},"n2168544744":{"id":"n2168544744","loc":[-85.626942,41.9528857]},"n2168544745":{"id":"n2168544745","loc":[-85.6270918,41.9526851]},"n2168544746":{"id":"n2168544746","loc":[-85.6272117,41.95244]},"n2168544747":{"id":"n2168544747","loc":[-85.6271578,41.952226]},"n2168544748":{"id":"n2168544748","loc":[-85.6270019,41.9519719]},"n2168544749":{"id":"n2168544749","loc":[-85.6268221,41.9518382]},"n2168544750":{"id":"n2168544750","loc":[-85.6265284,41.951807]},"n2168544751":{"id":"n2168544751","loc":[-85.6256534,41.9518516]},"n2168544752":{"id":"n2168544752","loc":[-85.6253477,41.9518338]},"n2168544753":{"id":"n2168544753","loc":[-85.6251139,41.9517669]},"n185955747":{"id":"n185955747","loc":[-85.620674,41.954709]},"n185960688":{"id":"n185960688","loc":[-85.621032,41.951913]},"n185972054":{"id":"n185972054","loc":[-85.6186728,41.9547335]},"n185978814":{"id":"n185978814","loc":[-85.6201708,41.9547403]},"n1819790532":{"id":"n1819790532","loc":[-85.6244908,41.9555731]},"n1819790536":{"id":"n1819790536","loc":[-85.6217925,41.9583135]},"n1819790538":{"id":"n1819790538","loc":[-85.6233954,41.9600014]},"n1819790539":{"id":"n1819790539","loc":[-85.6204611,41.9562117]},"n1819790546":{"id":"n1819790546","loc":[-85.6210898,41.9567657]},"n1819790548":{"id":"n1819790548","loc":[-85.6202465,41.9562237]},"n1819790550":{"id":"n1819790550","loc":[-85.6250165,41.9560677]},"n1819790551":{"id":"n1819790551","loc":[-85.6227946,41.9597023]},"n1819790553":{"id":"n1819790553","loc":[-85.6215726,41.9584571]},"n1819790556":{"id":"n1819790556","loc":[-85.6196306,41.9573002]},"n1819790557":{"id":"n1819790557","loc":[-85.6209503,41.9563109]},"n1819790558":{"id":"n1819790558","loc":[-85.6196939,41.9574085]},"n1819790561":{"id":"n1819790561","loc":[-85.621079,41.957751]},"n1819790562":{"id":"n1819790562","loc":[-85.6224255,41.9611417]},"n1819790565":{"id":"n1819790565","loc":[-85.6232506,41.9604841]},"n1819790566":{"id":"n1819790566","loc":[-85.6190835,41.9562909]},"n1819790567":{"id":"n1819790567","loc":[-85.622227,41.9593028]},"n1819790569":{"id":"n1819790569","loc":[-85.620976,41.9591039]},"n1819790571":{"id":"n1819790571","loc":[-85.6212078,41.9565303]},"n1819790572":{"id":"n1819790572","loc":[-85.6235306,41.9595102]},"n1819790581":{"id":"n1819790581","loc":[-85.6235563,41.9579351]},"n1819790584":{"id":"n1819790584","loc":[-85.6230371,41.9574598]},"n1819790586":{"id":"n1819790586","loc":[-85.6211748,41.9564272]},"n1819790588":{"id":"n1819790588","loc":[-85.6226508,41.9601086]},"n1819790591":{"id":"n1819790591","loc":[-85.6218032,41.9607468]},"n1819790593":{"id":"n1819790593","loc":[-85.6207915,41.9618735]},"n1819790596":{"id":"n1819790596","loc":[-85.6252955,41.9567858]},"n1819790598":{"id":"n1819790598","loc":[-85.6196618,41.9568939]},"n1819790600":{"id":"n1819790600","loc":[-85.6224416,41.9587084]},"n1819790602":{"id":"n1819790602","loc":[-85.6217442,41.9558641]},"n1819790603":{"id":"n1819790603","loc":[-85.6213355,41.9592116]},"n1819790604":{"id":"n1819790604","loc":[-85.622801,41.9573042]},"n1819790608":{"id":"n1819790608","loc":[-85.6199729,41.9574325]},"n1819790610":{"id":"n1819790610","loc":[-85.6195555,41.9557165]},"n1819790611":{"id":"n1819790611","loc":[-85.622978,41.9586007]},"n1819790613":{"id":"n1819790613","loc":[-85.6253963,41.9562636]},"n1819790614":{"id":"n1819790614","loc":[-85.6235252,41.9580342]},"n1819790616":{"id":"n1819790616","loc":[-85.6232988,41.9596305]},"n1819790617":{"id":"n1819790617","loc":[-85.6226776,41.9598732]},"n1819790619":{"id":"n1819790619","loc":[-85.625553,41.9561794]},"n1819790620":{"id":"n1819790620","loc":[-85.6235574,41.959231]},"n1819790624":{"id":"n1819790624","loc":[-85.6228429,41.9573726]},"n1819790626":{"id":"n1819790626","loc":[-85.6193785,41.9556766]},"n1819790628":{"id":"n1819790628","loc":[-85.620092,41.9554253]},"n1819790630":{"id":"n1819790630","loc":[-85.6226658,41.9604402]},"n1819790638":{"id":"n1819790638","loc":[-85.6219964,41.9602561]},"n1819790640":{"id":"n1819790640","loc":[-85.6232731,41.9599969]},"n1819790643":{"id":"n1819790643","loc":[-85.6247698,41.9568895]},"n1819790650":{"id":"n1819790650","loc":[-85.6216412,41.9550149]},"n1819790652":{"id":"n1819790652","loc":[-85.6224952,41.9603918]},"n1819790656":{"id":"n1819790656","loc":[-85.61918,41.9555649]},"n1819790661":{"id":"n1819790661","loc":[-85.6200169,41.955505]},"n1819790662":{"id":"n1819790662","loc":[-85.6217389,41.9563149]},"n1819790666":{"id":"n1819790666","loc":[-85.6229566,41.9598373]},"n1819790667":{"id":"n1819790667","loc":[-85.6209117,41.9609189]},"n1819790669":{"id":"n1819790669","loc":[-85.6252311,41.9562353]},"n1819790670":{"id":"n1819790670","loc":[-85.6209758,41.961868]},"n1819790672":{"id":"n1819790672","loc":[-85.6209557,41.9589078]},"n1819790673":{"id":"n1819790673","loc":[-85.6190352,41.9561393]},"n1819790675":{"id":"n1819790675","loc":[-85.6236432,41.9586685]},"n1819790676":{"id":"n1819790676","loc":[-85.6194901,41.9565389]},"n1819790678":{"id":"n1819790678","loc":[-85.6219266,41.9582417]},"n1819790680":{"id":"n1819790680","loc":[-85.6208258,41.9557211]},"n1819790681":{"id":"n1819790681","loc":[-85.6212024,41.9613212]},"n1819790682":{"id":"n1819790682","loc":[-85.624877,41.9559401]},"n1819790684":{"id":"n1819790684","loc":[-85.6206499,41.9583693]},"n1819790699":{"id":"n1819790699","loc":[-85.6215243,41.956279]},"n1819790701":{"id":"n1819790701","loc":[-85.6246625,41.9559321]},"n1819790703":{"id":"n1819790703","loc":[-85.6230478,41.9585089]},"n1819790708":{"id":"n1819790708","loc":[-85.6211102,41.9575402]},"n1819790710":{"id":"n1819790710","loc":[-85.6215082,41.9548468]},"n1819790711":{"id":"n1819790711","loc":[-85.6206552,41.9586007]},"n1819790713":{"id":"n1819790713","loc":[-85.6215404,41.9549705]},"n1819790715":{"id":"n1819790715","loc":[-85.6216906,41.955521]},"n1819790717":{"id":"n1819790717","loc":[-85.6215404,41.9547391]},"n1819790722":{"id":"n1819790722","loc":[-85.6219964,41.9599131]},"n1819790723":{"id":"n1819790723","loc":[-85.622286,41.9606989]},"n1819790725":{"id":"n1819790725","loc":[-85.6228439,41.9572005]},"n1819790727":{"id":"n1819790727","loc":[-85.6202518,41.9554458]},"n1819790728":{"id":"n1819790728","loc":[-85.623434,41.9575276]},"n1819790729":{"id":"n1819790729","loc":[-85.6234287,41.9568576]},"n1819790732":{"id":"n1819790732","loc":[-85.6229566,41.9571369]},"n1819790733":{"id":"n1819790733","loc":[-85.6225543,41.9590275]},"n1819790734":{"id":"n1819790734","loc":[-85.6232892,41.9583135]},"n1819790736":{"id":"n1819790736","loc":[-85.622977,41.9608551]},"n1819790737":{"id":"n1819790737","loc":[-85.624008,41.9569533]},"n1819790741":{"id":"n1819790741","loc":[-85.6212775,41.9608545]},"n1819790742":{"id":"n1819790742","loc":[-85.6231282,41.9569932]},"n1819790743":{"id":"n1819790743","loc":[-85.6224523,41.9591831]},"n1819790744":{"id":"n1819790744","loc":[-85.6210951,41.9610819]},"n1819790745":{"id":"n1819790745","loc":[-85.6220114,41.960544]},"n1819790755":{"id":"n1819790755","loc":[-85.6216369,41.9553854]},"n1819790757":{"id":"n1819790757","loc":[-85.6209986,41.9592709]},"n1819790758":{"id":"n1819790758","loc":[-85.6200437,41.9563468]},"n1819790764":{"id":"n1819790764","loc":[-85.6219363,41.9596823]},"n1819790765":{"id":"n1819790765","loc":[-85.6237612,41.9568496]},"n1819790769":{"id":"n1819790769","loc":[-85.6212389,41.9593433]},"n1819790771":{"id":"n1819790771","loc":[-85.6210726,41.9560123]},"n1819790772":{"id":"n1819790772","loc":[-85.6212711,41.9561838]},"n1819790776":{"id":"n1819790776","loc":[-85.6234437,41.9577795]},"n1819790777":{"id":"n1819790777","loc":[-85.6212502,41.9618599]},"n1819790783":{"id":"n1819790783","loc":[-85.6216895,41.9610585]},"n1819790784":{"id":"n1819790784","loc":[-85.6200115,41.9556367]},"n1819790785":{"id":"n1819790785","loc":[-85.6210576,41.9573002]},"n1819790786":{"id":"n1819790786","loc":[-85.621138,41.9576632]},"n1819790788":{"id":"n1819790788","loc":[-85.6207733,41.9578946]},"n1819790789":{"id":"n1819790789","loc":[-85.6200705,41.9571566]},"n1819790790":{"id":"n1819790790","loc":[-85.6245337,41.9558443]},"n1819790792":{"id":"n1819790792","loc":[-85.621932,41.9608066]},"n1819790793":{"id":"n1819790793","loc":[-85.6233578,41.9581385]},"n1819790794":{"id":"n1819790794","loc":[-85.6204557,41.9555136]},"n1819790797":{"id":"n1819790797","loc":[-85.6235038,41.9576074]},"n1819790800":{"id":"n1819790800","loc":[-85.6214438,41.9607508]},"n1819790801":{"id":"n1819790801","loc":[-85.623492,41.9602129]},"n1819790802":{"id":"n1819790802","loc":[-85.6216691,41.9546553]},"n1819790803":{"id":"n1819790803","loc":[-85.6231057,41.9586851]},"n1819790804":{"id":"n1819790804","loc":[-85.6209224,41.9578673]},"n1819790813":{"id":"n1819790813","loc":[-85.620092,41.9572962]},"n1819790814":{"id":"n1819790814","loc":[-85.6216691,41.9552218]},"n1819790816":{"id":"n1819790816","loc":[-85.6216144,41.9609668]},"n1819790818":{"id":"n1819790818","loc":[-85.6216906,41.9557324]},"n1819790820":{"id":"n1819790820","loc":[-85.6192069,41.9564186]},"n1819790823":{"id":"n1819790823","loc":[-85.6211155,41.9566027]},"n1819790825":{"id":"n1819790825","loc":[-85.6233106,41.9569294]},"n1819790839":{"id":"n1819790839","loc":[-85.625671,41.9564986]},"n1819790842":{"id":"n1819790842","loc":[-85.6235252,41.9567379]},"n1819790844":{"id":"n1819790844","loc":[-85.6253813,41.9566342]},"n1819790847":{"id":"n1819790847","loc":[-85.6200963,41.9567702]},"n1819790849":{"id":"n1819790849","loc":[-85.6238031,41.9587449]},"n1819790851":{"id":"n1819790851","loc":[-85.6234984,41.9584571]},"n1819790856":{"id":"n1819790856","loc":[-85.6242226,41.9570092]},"n1819790865":{"id":"n1819790865","loc":[-85.6200265,41.9569458]},"n1819790869":{"id":"n1819790869","loc":[-85.6230049,41.9601245]},"n1819790871":{"id":"n1819790871","loc":[-85.6190727,41.9558322]},"n1819790873":{"id":"n1819790873","loc":[-85.6217442,41.9550104]},"n1819790875":{"id":"n1819790875","loc":[-85.6208044,41.9587808]},"n1819790879":{"id":"n1819790879","loc":[-85.6198444,41.9574484]},"n1819790883":{"id":"n1819790883","loc":[-85.623713,41.9588719]},"n1819790885":{"id":"n1819790885","loc":[-85.6223289,41.9605075]},"n1819790889":{"id":"n1819790889","loc":[-85.6208044,41.9562437]},"n1819790893":{"id":"n1819790893","loc":[-85.6218183,41.9559684]},"n1819790906":{"id":"n1819790906","loc":[-85.6214052,41.958697]},"n1819790913":{"id":"n1819790913","loc":[-85.6209981,41.9609957]},"n1819790917":{"id":"n1819790917","loc":[-85.6216208,41.9604436]},"n1819790919":{"id":"n1819790919","loc":[-85.6209406,41.9616373]},"n1819790920":{"id":"n1819790920","loc":[-85.6221948,41.9583334]},"n1819790922":{"id":"n1819790922","loc":[-85.6216681,41.9615292]},"n1819790924":{"id":"n1819790924","loc":[-85.6210147,41.9570489]},"n1819790929":{"id":"n1819790929","loc":[-85.6193678,41.955521]},"w17964707":{"id":"w17964707","tags":{"highway":"residential","name":"11th Ave"},"nodes":["n185960682","n185960684","n185960686","n185960688","n185960690"]},"w201484345":{"id":"w201484345","tags":{"bridge":"yes","highway":"residential","name":"E Hoffman St"},"nodes":["n185978791","n185978795"]},"w201484348":{"id":"w201484348","tags":{"highway":"residential","name":"E Hoffman St"},"nodes":["n185978795","n185978800","n185978803","n185978806","n185978808","n185978810","n185978811","n185978813","n185955747","n185978814","n185972054","n185978817"]},"w170843845":{"id":"w170843845","tags":{"landuse":"reservoir","name":"Hoffman Pond","natural":"water"},"nodes":["n1819790732","n1819790742","n1819790825","n1819790729","n1819790842","n1819790765","n1819790737","n1819790856","n1819790643","n1819790596","n1819790844","n1819790839","n1819849190","n1819790619","n1819790613","n1819790669","n1819790550","n1819790682","n1819790701","n1819790790","n1819790532","n1819790887","n1819790740","n1819790831","n1819790545","n1819790664","n1819790621","n1819790861","n1819790730","n1819790683","n1819790802","n1819790717","n1819790710","n1819790713","n1819790650","n1819790873","n1819790814","n1819790755","n1819790715","n1819790818","n1819790602","n1819790893","n1819790662","n1819790699","n1819790772","n1819790771","n1819790680","n1819790794","n1819790727","n1819790628","n1819790661","n1819790784","n1819790610","n1819790626","n1819790929","n1819790656","n1819790871","n1819790673","n1819790566","n1819790820","n1819790676","n1819790598","n1819790556","n1819790558","n1819790879","n1819790608","n1819790813","n1819790789","n1819790865","n1819790847","n1819790758","n1819790548","n1819790539","n1819790889","n1819790557","n1819790586","n1819790571","n1819790823","n1819790546","n1819790924","n1819790785","n1819790708","n1819790786","n1819790561","n1819790804","n1819790788","n1819790684","n1819790711","n1819790875","n1819790672","n1819790569","n1819790757","n1819790769","n1819790603","n1819790906","n1819790553","n1819790536","n1819790678","n1819790920","n1819790600","n1819790733","n1819790743","n1819790567","n1819790764","n1819790722","n1819790638","n1819790917","n1819790800","n1819790741","n1819790667","n1819790913","n1819790744","n1819790816","n1819790591","n1819790745","n1819790885","n1819790652","n1819790588","n1819790617","n1819790551","n1819790666","n1819790869","n1819790630","n1819790723","n1819790792","n1819790783","n1819790681","n1819790919","n1819790593","n1819790670","n1819790777","n1819790922","n1819790562","n1819790736","n1819790565","n1819790801","n1819790538","n1819790640","n1819790616","n1819790572","n1819790620","n1819790883","n1819790849","n1819790675","n1819790851","n1819790803","n1819790611","n1819790703","n1819790734","n1819790793","n1819790614","n1819790581","n1819790776","n1819790797","n1819790728","n1819790584","n1819790624","n1819790604","n1819790725","n1819790732"]},"w206805240":{"id":"w206805240","tags":{"waterway":"river"},"nodes":["n2168544738","n2168544739","n2168544740","n2168544741","n2168544742","n2168544743","n2168544744","n2168544745","n2168544746","n2168544747","n2168544748","n2168544749","n2168544750","n2168544751","n2168544752","n2168544753","n1819848944"]},"n394490429":{"id":"n394490429","loc":[-85.643883,41.954365]},"n185953421":{"id":"n185953421","loc":[-85.641876,41.954946]},"n185953417":{"id":"n185953417","loc":[-85.6418306,41.9551597]},"n185977233":{"id":"n185977233","loc":[-85.642987,41.95486]},"n185977232":{"id":"n185977232","loc":[-85.642894,41.9547842]},"n1475293244":{"id":"n1475293244","loc":[-85.63974,41.9521543]},"n1819848890":{"id":"n1819848890","loc":[-85.6410004,41.9552822]},"n1819848965":{"id":"n1819848965","loc":[-85.6409795,41.9553892]},"n2189015846":{"id":"n2189015846","loc":[-85.6420457,41.9549528]},"n2189015849":{"id":"n2189015849","loc":[-85.6425867,41.9551392]},"n2189015852":{"id":"n2189015852","loc":[-85.6426877,41.9549771]},"n2199109816":{"id":"n2199109816","loc":[-85.6399215,41.9540925]},"n2199109818":{"id":"n2199109818","loc":[-85.6399182,41.9538236]},"n2199109820":{"id":"n2199109820","loc":[-85.6402201,41.9538216]},"n2199109822":{"id":"n2199109822","loc":[-85.640222,41.9539771]},"n2199109825":{"id":"n2199109825","loc":[-85.6402904,41.9539766]},"n2199109827":{"id":"n2199109827","loc":[-85.6402918,41.95409]},"n2199109829":{"id":"n2199109829","loc":[-85.6395845,41.9544626]},"n2199109831":{"id":"n2199109831","loc":[-85.6395792,41.9540671]},"n2199109833":{"id":"n2199109833","loc":[-85.6397173,41.9540661]},"n2199109835":{"id":"n2199109835","loc":[-85.6397226,41.9544616]},"n2199109837":{"id":"n2199109837","loc":[-85.6399641,41.9545058]},"n2199109839":{"id":"n2199109839","loc":[-85.6399637,41.9541859]},"n2199109841":{"id":"n2199109841","loc":[-85.6401098,41.9541858]},"n2199109843":{"id":"n2199109843","loc":[-85.64011,41.9543272]},"n2199109845":{"id":"n2199109845","loc":[-85.6400783,41.9543273]},"n2199109847":{"id":"n2199109847","loc":[-85.6400785,41.9545058]},"n2199109853":{"id":"n2199109853","loc":[-85.6396184,41.9554049]},"n2199109855":{"id":"n2199109855","loc":[-85.6396825,41.9553713]},"n185949745":{"id":"n185949745","loc":[-85.6442727,41.9553112]},"n185949748":{"id":"n185949748","loc":[-85.6448804,41.9555238]},"n185949755":{"id":"n185949755","loc":[-85.6420011,41.9603536]},"n185949763":{"id":"n185949763","loc":[-85.6408843,41.9555822]},"n185949765":{"id":"n185949765","loc":[-85.6414548,41.9557751]},"n185952158":{"id":"n185952158","loc":[-85.640066,41.956854]},"n185952160":{"id":"n185952160","loc":[-85.639848,41.957229]},"n185952161":{"id":"n185952161","loc":[-85.6396089,41.9576192]},"n185952163":{"id":"n185952163","loc":[-85.63892,41.957957]},"n185953413":{"id":"n185953413","loc":[-85.64162,41.955475]},"n185971651":{"id":"n185971651","loc":[-85.6440766,41.9543462]},"n185977234":{"id":"n185977234","loc":[-85.645044,41.955581]},"n394490395":{"id":"n394490395","loc":[-85.657336,41.936762]},"n394490396":{"id":"n394490396","loc":[-85.653896,41.936978]},"n394490397":{"id":"n394490397","loc":[-85.653732,41.937386]},"n394490398":{"id":"n394490398","loc":[-85.65182,41.937378]},"n394490399":{"id":"n394490399","loc":[-85.651843,41.938445]},"n394490400":{"id":"n394490400","loc":[-85.652536,41.938447]},"n394490401":{"id":"n394490401","loc":[-85.652533,41.938901]},"n394490402":{"id":"n394490402","loc":[-85.652084,41.9389]},"n394490403":{"id":"n394490403","loc":[-85.6521,41.939627]},"n394490404":{"id":"n394490404","loc":[-85.652301,41.939628]},"n394490405":{"id":"n394490405","loc":[-85.652302,41.939755]},"n394490406":{"id":"n394490406","loc":[-85.652783,41.939747]},"n394490407":{"id":"n394490407","loc":[-85.652835,41.94112]},"n394490408":{"id":"n394490408","loc":[-85.651968,41.941123]},"n394490409":{"id":"n394490409","loc":[-85.651983,41.941969]},"n394490410":{"id":"n394490410","loc":[-85.652908,41.941961]},"n394490411":{"id":"n394490411","loc":[-85.65292,41.94278]},"n394490412":{"id":"n394490412","loc":[-85.651698,41.942816]},"n394490413":{"id":"n394490413","loc":[-85.651509,41.942823]},"n394490414":{"id":"n394490414","loc":[-85.651272,41.942837]},"n394490415":{"id":"n394490415","loc":[-85.651272,41.943325]},"n394490416":{"id":"n394490416","loc":[-85.65122,41.944053]},"n394490417":{"id":"n394490417","loc":[-85.651193,41.944449]},"n394490418":{"id":"n394490418","loc":[-85.651088,41.944969]},"n394490419":{"id":"n394490419","loc":[-85.650949,41.945554]},"n394490420":{"id":"n394490420","loc":[-85.650907,41.945719]},"n394490421":{"id":"n394490421","loc":[-85.650808,41.946016]},"n394490422":{"id":"n394490422","loc":[-85.650712,41.946516]},"n394490423":{"id":"n394490423","loc":[-85.650493,41.947166]},"n394490424":{"id":"n394490424","loc":[-85.650626,41.947213]},"n394490425":{"id":"n394490425","loc":[-85.650201,41.948109]},"n394490426":{"id":"n394490426","loc":[-85.649868,41.948797]},"n394490427":{"id":"n394490427","loc":[-85.649669,41.949161]},"n394490428":{"id":"n394490428","loc":[-85.64659,41.954067]},"n394490430":{"id":"n394490430","loc":[-85.644034,41.95444]},"n394490431":{"id":"n394490431","loc":[-85.644248,41.954507]},"n394490432":{"id":"n394490432","loc":[-85.64491,41.954481]},"n394490433":{"id":"n394490433","loc":[-85.645213,41.954433]},"n394490434":{"id":"n394490434","loc":[-85.645426,41.954477]},"n394490435":{"id":"n394490435","loc":[-85.6458,41.954704]},"n394490436":{"id":"n394490436","loc":[-85.64605,41.954804]},"n394490437":{"id":"n394490437","loc":[-85.646125,41.954817]},"n394490438":{"id":"n394490438","loc":[-85.646002,41.954997]},"n394490439":{"id":"n394490439","loc":[-85.645764,41.955366]},"n394490440":{"id":"n394490440","loc":[-85.645525,41.955734]},"n394490441":{"id":"n394490441","loc":[-85.64443,41.957424]},"n394490442":{"id":"n394490442","loc":[-85.641712,41.961723]},"n394490443":{"id":"n394490443","loc":[-85.640747,41.963246]},"n394490444":{"id":"n394490444","loc":[-85.637803,41.967894]},"n394490445":{"id":"n394490445","loc":[-85.637673,41.967861]},"n394490446":{"id":"n394490446","loc":[-85.636637,41.969275]},"n394490447":{"id":"n394490447","loc":[-85.634923,41.969269]},"n394490448":{"id":"n394490448","loc":[-85.634893,41.968537]},"n394490449":{"id":"n394490449","loc":[-85.634544,41.96927]},"n394490450":{"id":"n394490450","loc":[-85.630835,41.969274]},"n394490451":{"id":"n394490451","loc":[-85.630834,41.968348]},"n394490452":{"id":"n394490452","loc":[-85.630857,41.968179]},"n394490453":{"id":"n394490453","loc":[-85.630924,41.968044]},"n394490454":{"id":"n394490454","loc":[-85.631004,41.967925]},"n394490455":{"id":"n394490455","loc":[-85.631143,41.967811]},"n394490456":{"id":"n394490456","loc":[-85.631311,41.967736]},"n394490457":{"id":"n394490457","loc":[-85.631595,41.967693]},"n394490458":{"id":"n394490458","loc":[-85.63325,41.967702]},"n394490459":{"id":"n394490459","loc":[-85.633247,41.967021]},"n394490460":{"id":"n394490460","loc":[-85.634858,41.967021]},"n394490461":{"id":"n394490461","loc":[-85.634865,41.967711]},"n394490462":{"id":"n394490462","loc":[-85.634884,41.968231]},"n394490463":{"id":"n394490463","loc":[-85.636559,41.963867]},"n394490464":{"id":"n394490464","loc":[-85.634832,41.963866]},"n394490465":{"id":"n394490465","loc":[-85.63481,41.961899]},"n394490466":{"id":"n394490466","loc":[-85.637219,41.961842]},"n394490467":{"id":"n394490467","loc":[-85.637837,41.960019]},"n394490468":{"id":"n394490468","loc":[-85.637459,41.960022]},"n394490469":{"id":"n394490469","loc":[-85.635295,41.959987]},"n394490470":{"id":"n394490470","loc":[-85.634783,41.959979]},"n394490471":{"id":"n394490471","loc":[-85.634776,41.959834]},"n394490472":{"id":"n394490472","loc":[-85.634767,41.959009]},"n394490473":{"id":"n394490473","loc":[-85.634763,41.958292]},"n394490474":{"id":"n394490474","loc":[-85.633346,41.958287]},"n394490475":{"id":"n394490475","loc":[-85.632128,41.9583]},"n394490476":{"id":"n394490476","loc":[-85.631414,41.958318]},"n394490477":{"id":"n394490477","loc":[-85.63137,41.959033]},"n394490478":{"id":"n394490478","loc":[-85.631325,41.959753]},"n394490479":{"id":"n394490479","loc":[-85.631494,41.95977]},"n394490480":{"id":"n394490480","loc":[-85.631456,41.960673]},"n394490481":{"id":"n394490481","loc":[-85.631421,41.961494]},"n394490482":{"id":"n394490482","loc":[-85.631404,41.961887]},"n394490483":{"id":"n394490483","loc":[-85.631401,41.961968]},"n394490484":{"id":"n394490484","loc":[-85.630962,41.961967]},"n394490485":{"id":"n394490485","loc":[-85.6299,41.961973]},"n394490486":{"id":"n394490486","loc":[-85.624929,41.962002]},"n394490487":{"id":"n394490487","loc":[-85.623333,41.961987]},"n394490488":{"id":"n394490488","loc":[-85.621894,41.963956]},"n394490489":{"id":"n394490489","loc":[-85.62131,41.963727]},"n394490490":{"id":"n394490490","loc":[-85.621216,41.963868]},"n394490491":{"id":"n394490491","loc":[-85.620356,41.965119]},"n394490492":{"id":"n394490492","loc":[-85.620848,41.965341]},"n394490493":{"id":"n394490493","loc":[-85.620684,41.965558]},"n394490494":{"id":"n394490494","loc":[-85.620621,41.965658]},"n394490495":{"id":"n394490495","loc":[-85.618165,41.965759]},"n394490496":{"id":"n394490496","loc":[-85.618071,41.965759]},"n394490497":{"id":"n394490497","loc":[-85.617986,41.965759]},"n394490498":{"id":"n394490498","loc":[-85.605673,41.965764]},"n394490499":{"id":"n394490499","loc":[-85.605668,41.963548]},"n394490500":{"id":"n394490500","loc":[-85.605664,41.962094]},"n394490501":{"id":"n394490501","loc":[-85.595828,41.962159]},"n394490502":{"id":"n394490502","loc":[-85.587869,41.962169]},"n394490503":{"id":"n394490503","loc":[-85.586289,41.962179]},"n394490504":{"id":"n394490504","loc":[-85.583774,41.962178]},"n394490505":{"id":"n394490505","loc":[-85.583774,41.961789]},"n394490506":{"id":"n394490506","loc":[-85.581303,41.961783]},"n394490507":{"id":"n394490507","loc":[-85.581304,41.961616]},"n394490508":{"id":"n394490508","loc":[-85.581292,41.961616]},"n394490509":{"id":"n394490509","loc":[-85.581247,41.959244]},"n394490510":{"id":"n394490510","loc":[-85.581245,41.958394]},"n394490511":{"id":"n394490511","loc":[-85.581276,41.958372]},"n394490512":{"id":"n394490512","loc":[-85.581302,41.958353]},"n394490513":{"id":"n394490513","loc":[-85.581376,41.9583]},"n394490514":{"id":"n394490514","loc":[-85.582256,41.957663]},"n394490515":{"id":"n394490515","loc":[-85.585299,41.955483]},"n394490516":{"id":"n394490516","loc":[-85.585588,41.955331]},"n394490517":{"id":"n394490517","loc":[-85.586053,41.955163]},"n394490518":{"id":"n394490518","loc":[-85.58632,41.955076]},"n394490519":{"id":"n394490519","loc":[-85.586478,41.955025]},"n394490520":{"id":"n394490520","loc":[-85.58692,41.954947]},"n394490521":{"id":"n394490521","loc":[-85.587327,41.954914]},"n394490522":{"id":"n394490522","loc":[-85.587345,41.954913]},"n394490523":{"id":"n394490523","loc":[-85.587358,41.954913]},"n394490524":{"id":"n394490524","loc":[-85.58963,41.954877]},"n394490525":{"id":"n394490525","loc":[-85.591077,41.954865]},"n394490526":{"id":"n394490526","loc":[-85.594824,41.954843]},"n394490527":{"id":"n394490527","loc":[-85.594804,41.95331]},"n394490528":{"id":"n394490528","loc":[-85.599336,41.95331]},"n394490529":{"id":"n394490529","loc":[-85.599336,41.954825]},"n394490530":{"id":"n394490530","loc":[-85.597828,41.954839]},"n394490531":{"id":"n394490531","loc":[-85.597833,41.95614]},"n394490532":{"id":"n394490532","loc":[-85.596586,41.956151]},"n394490533":{"id":"n394490533","loc":[-85.596586,41.956394]},"n394490534":{"id":"n394490534","loc":[-85.595933,41.956394]},"n394490535":{"id":"n394490535","loc":[-85.595933,41.958176]},"n394490536":{"id":"n394490536","loc":[-85.597635,41.958179]},"n394490537":{"id":"n394490537","loc":[-85.597717,41.958177]},"n394490538":{"id":"n394490538","loc":[-85.601671,41.958194]},"n394490539":{"id":"n394490539","loc":[-85.605619,41.958194]},"n394490540":{"id":"n394490540","loc":[-85.608054,41.958187]},"n394490542":{"id":"n394490542","loc":[-85.6080762,41.9547864]},"n394490545":{"id":"n394490545","loc":[-85.6104354,41.9548263]},"n394490546":{"id":"n394490546","loc":[-85.610274,41.951106]},"n394490547":{"id":"n394490547","loc":[-85.610278,41.950829]},"n394490548":{"id":"n394490548","loc":[-85.610309,41.948377]},"n394490549":{"id":"n394490549","loc":[-85.610314,41.947986]},"n394490550":{"id":"n394490550","loc":[-85.610464,41.947985]},"n394490551":{"id":"n394490551","loc":[-85.610447,41.947468]},"n394490552":{"id":"n394490552","loc":[-85.612469,41.947471]},"n394490553":{"id":"n394490553","loc":[-85.612494,41.945576]},"n394490554":{"id":"n394490554","loc":[-85.610292,41.94558]},"n394490555":{"id":"n394490555","loc":[-85.608412,41.945625]},"n394490556":{"id":"n394490556","loc":[-85.608412,41.943036]},"n394490557":{"id":"n394490557","loc":[-85.608702,41.943087]},"n394490558":{"id":"n394490558","loc":[-85.609196,41.943224]},"n394490559":{"id":"n394490559","loc":[-85.609571,41.943263]},"n394490560":{"id":"n394490560","loc":[-85.610116,41.943295]},"n394490561":{"id":"n394490561","loc":[-85.610273,41.943275]},"n394490562":{"id":"n394490562","loc":[-85.611339,41.943075]},"n394490563":{"id":"n394490563","loc":[-85.611575,41.942997]},"n394490564":{"id":"n394490564","loc":[-85.611847,41.942849]},"n394490565":{"id":"n394490565","loc":[-85.612164,41.942568]},"n394490566":{"id":"n394490566","loc":[-85.612341,41.942529]},"n394490567":{"id":"n394490567","loc":[-85.612562,41.942524]},"n394490568":{"id":"n394490568","loc":[-85.612768,41.942546]},"n394490569":{"id":"n394490569","loc":[-85.612938,41.942633]},"n394490570":{"id":"n394490570","loc":[-85.6131,41.942782]},"n394490571":{"id":"n394490571","loc":[-85.613299,41.942919]},"n394490572":{"id":"n394490572","loc":[-85.613498,41.942996]},"n394490573":{"id":"n394490573","loc":[-85.614698,41.942842]},"n394490574":{"id":"n394490574","loc":[-85.615288,41.942698]},"n394490575":{"id":"n394490575","loc":[-85.616054,41.942693]},"n394490576":{"id":"n394490576","loc":[-85.61603,41.942175]},"n394490577":{"id":"n394490577","loc":[-85.616004,41.941741]},"n394490578":{"id":"n394490578","loc":[-85.615994,41.940156]},"n394490579":{"id":"n394490579","loc":[-85.615144,41.940159]},"n394490580":{"id":"n394490580","loc":[-85.614915,41.940161]},"n394490582":{"id":"n394490582","loc":[-85.614875,41.938532]},"n394490583":{"id":"n394490583","loc":[-85.616167,41.938787]},"n394490585":{"id":"n394490585","loc":[-85.616176,41.938589]},"n394490586":{"id":"n394490586","loc":[-85.614537,41.938282]},"n394490588":{"id":"n394490588","loc":[-85.610141,41.937459]},"n394490589":{"id":"n394490589","loc":[-85.610172,41.937298]},"n394490590":{"id":"n394490590","loc":[-85.609918,41.935495]},"n394490592":{"id":"n394490592","loc":[-85.610092,41.935451]},"n394490594":{"id":"n394490594","loc":[-85.610681,41.935247]},"n394490595":{"id":"n394490595","loc":[-85.611446,41.934955]},"n394490596":{"id":"n394490596","loc":[-85.612057,41.934696]},"n394490598":{"id":"n394490598","loc":[-85.613256,41.934084]},"n394490599":{"id":"n394490599","loc":[-85.613948,41.933682]},"n394490601":{"id":"n394490601","loc":[-85.61436,41.933417]},"n394490602":{"id":"n394490602","loc":[-85.614638,41.933212]},"n394490604":{"id":"n394490604","loc":[-85.615249,41.9332]},"n394490605":{"id":"n394490605","loc":[-85.618218,41.933223]},"n394490607":{"id":"n394490607","loc":[-85.618241,41.933479]},"n394490608":{"id":"n394490608","loc":[-85.618257,41.93365]},"n394490609":{"id":"n394490609","loc":[-85.618298,41.935067]},"n394490611":{"id":"n394490611","loc":[-85.619791,41.935067]},"n394490612":{"id":"n394490612","loc":[-85.619794,41.933301]},"n394490613":{"id":"n394490613","loc":[-85.619795,41.932692]},"n394490614":{"id":"n394490614","loc":[-85.619729,41.929517]},"n394490615":{"id":"n394490615","loc":[-85.619801,41.929305]},"n394490616":{"id":"n394490616","loc":[-85.619809,41.927391]},"n394490617":{"id":"n394490617","loc":[-85.620883,41.927378]},"n394490618":{"id":"n394490618","loc":[-85.620988,41.927368]},"n394490619":{"id":"n394490619","loc":[-85.621076,41.927368]},"n394490620":{"id":"n394490620","loc":[-85.621156,41.927376]},"n394490621":{"id":"n394490621","loc":[-85.621685,41.92737]},"n394490622":{"id":"n394490622","loc":[-85.624716,41.927359]},"n394490623":{"id":"n394490623","loc":[-85.625308,41.92737]},"n394490624":{"id":"n394490624","loc":[-85.625655,41.927377]},"n394490625":{"id":"n394490625","loc":[-85.625093,41.925591]},"n394490626":{"id":"n394490626","loc":[-85.625174,41.92559]},"n394490627":{"id":"n394490627","loc":[-85.625249,41.925597]},"n394490628":{"id":"n394490628","loc":[-85.625532,41.925604]},"n394490629":{"id":"n394490629","loc":[-85.625761,41.925597]},"n394490630":{"id":"n394490630","loc":[-85.625955,41.926153]},"n394490631":{"id":"n394490631","loc":[-85.626209,41.926155]},"n394490632":{"id":"n394490632","loc":[-85.627757,41.926151]},"n394490633":{"id":"n394490633","loc":[-85.627825,41.926298]},"n394490634":{"id":"n394490634","loc":[-85.627994,41.926315]},"n394490635":{"id":"n394490635","loc":[-85.628049,41.927196]},"n394490636":{"id":"n394490636","loc":[-85.62949,41.927221]},"n394490637":{"id":"n394490637","loc":[-85.629602,41.927277]},"n394490638":{"id":"n394490638","loc":[-85.6297102,41.9273279]},"n394490639":{"id":"n394490639","loc":[-85.630958,41.927398]},"n394490699":{"id":"n394490699","loc":[-85.632741,41.927388]},"n394490700":{"id":"n394490700","loc":[-85.632997,41.927391]},"n394490701":{"id":"n394490701","loc":[-85.633149,41.927393]},"n394490702":{"id":"n394490702","loc":[-85.633334,41.927393]},"n394490703":{"id":"n394490703","loc":[-85.633468,41.927561]},"n394490704":{"id":"n394490704","loc":[-85.633563,41.927755]},"n394490705":{"id":"n394490705","loc":[-85.633662,41.928192]},"n394490706":{"id":"n394490706","loc":[-85.633679,41.928807]},"n394490707":{"id":"n394490707","loc":[-85.633687,41.929107]},"n394490708":{"id":"n394490708","loc":[-85.633927,41.929109]},"n394490709":{"id":"n394490709","loc":[-85.634126,41.929111]},"n394490710":{"id":"n394490710","loc":[-85.634207,41.92911]},"n394490711":{"id":"n394490711","loc":[-85.634323,41.929111]},"n394490712":{"id":"n394490712","loc":[-85.636712,41.929128]},"n394490713":{"id":"n394490713","loc":[-85.63808,41.9291]},"n394490714":{"id":"n394490714","loc":[-85.639213,41.929088]},"n394490715":{"id":"n394490715","loc":[-85.639189,41.92852]},"n394490716":{"id":"n394490716","loc":[-85.639204,41.925488]},"n394490717":{"id":"n394490717","loc":[-85.644204,41.925452]},"n394490718":{"id":"n394490718","loc":[-85.651425,41.925406]},"n394490719":{"id":"n394490719","loc":[-85.651449,41.926321]},"n394490720":{"id":"n394490720","loc":[-85.651451,41.926969]},"n394490721":{"id":"n394490721","loc":[-85.651458,41.928052]},"n394490722":{"id":"n394490722","loc":[-85.651446,41.928892]},"n394490723":{"id":"n394490723","loc":[-85.651456,41.929447]},"n394490724":{"id":"n394490724","loc":[-85.651707,41.929454]},"n394490725":{"id":"n394490725","loc":[-85.652369,41.929473]},"n394490726":{"id":"n394490726","loc":[-85.6525,41.929452]},"n394490727":{"id":"n394490727","loc":[-85.654066,41.92946]},"n394490728":{"id":"n394490728","loc":[-85.654816,41.92946]},"n394490729":{"id":"n394490729","loc":[-85.654816,41.930337]},"n394490730":{"id":"n394490730","loc":[-85.654587,41.930337]},"n394490731":{"id":"n394490731","loc":[-85.654548,41.931072]},"n394490732":{"id":"n394490732","loc":[-85.654538,41.931701]},"n394490733":{"id":"n394490733","loc":[-85.654898,41.931689]},"n394490734":{"id":"n394490734","loc":[-85.654898,41.932505]},"n394490735":{"id":"n394490735","loc":[-85.654854,41.932514]},"n394490736":{"id":"n394490736","loc":[-85.655497,41.932499]},"n394490737":{"id":"n394490737","loc":[-85.656405,41.932493]},"n394490738":{"id":"n394490738","loc":[-85.656422,41.933416]},"n394490739":{"id":"n394490739","loc":[-85.657322,41.933438]},"n1475293233":{"id":"n1475293233","loc":[-85.6385522,41.9585167]},"n1475293242":{"id":"n1475293242","loc":[-85.64609,41.9540815]},"n1475293249":{"id":"n1475293249","loc":[-85.6358079,41.9692721]},"n1475293256":{"id":"n1475293256","loc":[-85.6387369,41.9581583]},"n1475293259":{"id":"n1475293259","loc":[-85.6455882,41.9541138]},"n1475293266":{"id":"n1475293266","loc":[-85.6451008,41.9541821]},"n1819800253":{"id":"n1819800253","loc":[-85.6134286,41.9429692]},"n2114807558":{"id":"n2114807558","loc":[-85.6365609,41.963866],"tags":{"railway":"level_crossing"}},"n2189015728":{"id":"n2189015728","loc":[-85.6383956,41.9590576]},"n2189015838":{"id":"n2189015838","loc":[-85.6435144,41.9563705]},"n2189015842":{"id":"n2189015842","loc":[-85.6415782,41.9557035]},"n2189015855":{"id":"n2189015855","loc":[-85.6440829,41.9554577]},"n2199109849":{"id":"n2199109849","loc":[-85.6393434,41.9565591]},"n2199109851":{"id":"n2199109851","loc":[-85.6393208,41.9565002]},"n2199109857":{"id":"n2199109857","loc":[-85.6401986,41.955545]},"n2199109859":{"id":"n2199109859","loc":[-85.6402362,41.955587]},"n2199109861":{"id":"n2199109861","loc":[-85.6395958,41.9565675]},"n2199109863":{"id":"n2199109863","loc":[-85.639528,41.9566011]},"w209717053":{"id":"w209717053","tags":{"area":"yes","building":"yes"},"nodes":["n2199109829","n2199109831","n2199109833","n2199109835","n2199109829"]},"w17966415":{"id":"w17966415","tags":{"access":"private","highway":"service","name":"Manufacturing Way"},"nodes":["n185971642","n185977232","n185977233","n185949745","n185949748","n185977234"]},"w209717054":{"id":"w209717054","tags":{"area":"yes","building":"yes"},"nodes":["n2199109837","n2199109839","n2199109841","n2199109843","n2199109845","n2199109847","n2199109837"]},"w208627214":{"id":"w208627214","tags":{"highway":"service"},"nodes":["n185949755","n2189015728","n1475293233","n1475293256","n185952163","n185952161","n185952160","n185952158","n185949763","n1819848965","n1819848890","n185952156"]},"w17963817":{"id":"w17963817","tags":{"access":"private","highway":"service"},"nodes":["n185949765","n185953413","n185953417","n185953421","n185953423"]},"w34369809":{"id":"w34369809","tags":{"admin_level":"8","boundary":"administrative","landuse":"residential"},"nodes":["n394490395","n394490396","n394490397","n394490398","n394490399","n394490400","n394490401","n394490402","n394490403","n394490404","n394490405","n394490406","n394490407","n394490408","n394490409","n394490410","n394490411","n394490412","n394490413","n394490414","n394490415","n394490416","n394490417","n394490418","n394490419","n394490420","n394490421","n394490422","n394490423","n394490424","n394490425","n394490426","n394490427","n394490428","n1475293242","n1475293259","n1475293266","n394490429","n394490430","n394490431","n394490432","n394490433","n394490434","n394490435","n394490436","n394490437","n394490438","n394490439","n394490440","n394490441","n394490442","n394490443","n394490444","n394490445","n394490446","n1475293249","n394490447","n394490448","n394490449","n394490450","n394490451","n394490452","n394490453","n394490454","n394490455","n394490456","n394490457","n394490458","n394490459","n394490460","n394490461","n394490462","n2114807558","n394490463","n1475293226","n394490464","n394490465","n394490466","n394490467","n394490468","n394490469","n394490470","n394490471","n394490472","n394490473","n394490474","n394490475","n394490476","n394490477","n394490478","n394490479","n394490480","n394490481","n394490482","n394490483","n394490484","n394490485","n394490486","n394490487","n394490488","n394490489","n394490490","n394490491","n394490492","n394490493","n394490494","n394490495","n394490496","n394490497","n394490498","n394490499","n394490500","n394490501","n394490502","n394490503","n394490504","n394490505","n394490506","n394490507","n394490508","n394490509","n394490510","n394490511","n394490512","n394490513","n394490514","n394490515","n394490516","n394490517","n394490518","n394490519","n394490520","n394490521","n394490522","n394490523","n394490524","n394490525","n394490526","n394490527","n394490528","n394490529","n394490530","n394490531","n394490532","n394490533","n394490534","n394490535","n394490536","n394490537","n394490538","n394490539","n394490540","n394490542","n394490545","n394490546","n394490547","n394490548","n394490549","n394490550","n394490551","n394490552","n394490553","n394490554","n394490555","n394490556","n394490557","n394490558","n394490559","n394490560","n394490561","n394490562","n394490563","n394490564","n394490565","n394490566","n394490567","n394490568","n394490569","n394490570","n394490571","n1819800253","n394490572","n394490573","n394490574","n394490575","n394490576","n394490577","n394490578","n394490579","n394490580","n394490582","n394490583","n394490585","n394490586","n394490588","n394490589","n394490590","n394490592","n394490594","n394490595","n394490596","n394490598","n394490599","n394490601","n394490602","n394490604","n394490605","n394490607","n394490608","n394490609","n394490611","n394490612","n394490613","n394490614","n394490615","n394490616","n394490617","n394490618","n394490619","n394490620","n394490621","n394490622","n394490623","n394490624","n394490625","n394490626","n394490627","n394490628","n394490629","n394490630","n394490631","n394490632","n394490633","n394490634","n394490635","n394490636","n394490637","n394490638","n394490639","n394490699","n394490700","n394490701","n394490702","n394490703","n394490704","n394490705","n394490706","n394490707","n394490708","n394490709","n394490710","n394490711","n394490712","n394490713","n394490714","n394490715","n394490716","n394490717","n394490718","n394490719","n394490720","n394490721","n394490722","n394490723","n394490724","n394490725","n394490726","n394490727","n394490728","n394490729","n394490730","n394490731","n394490732","n394490733","n394490734","n394490735","n394490736","n394490737","n394490738","n394490739","n394490395"]},"w208627221":{"id":"w208627221","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015838","n2189015842","n2189015846","n2189015849","n2189015852","n2189015855","n2189015838"]},"w209717052":{"id":"w209717052","tags":{"area":"yes","building":"yes"},"nodes":["n2199109816","n2199109818","n2199109820","n2199109822","n2199109825","n2199109827","n2199109816"]},"w134151784":{"id":"w134151784","tags":{"bridge":"yes","highway":"residential","name":"W Hoffman St"},"nodes":["n185971648","n185971651"]},"w209717055":{"id":"w209717055","tags":{"area":"yes","landuse":"basin"},"nodes":["n2199109849","n2199109851","n2199109853","n2199109855","n2199109857","n2199109859","n2199109861","n2199109863","n2199109849"]},"w17967763":{"id":"w17967763","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n1475293244","n185982166","n185975067","n185971637"]},"r134949":{"id":"r134949","tags":{"admin_level":"8","border_type":"city","boundary":"administrative","name":"Three Rivers","place":"city","type":"boundary"},"members":[{"id":"w34369809","type":"way","role":"outer"},{"id":"w34369821","type":"way","role":"outer"},{"id":"w34369822","type":"way","role":"outer"},{"id":"w34369823","type":"way","role":"outer"},{"id":"w34369824","type":"way","role":"outer"},{"id":"w34369825","type":"way","role":"outer"},{"id":"w34369826","type":"way","role":"outer"},{"id":"w34369810","type":"way","role":"inner"},{"id":"w34369811","type":"way","role":"inner"},{"id":"w34369812","type":"way","role":"inner"},{"id":"w34367079","type":"way","role":"inner"},{"id":"w34369814","type":"way","role":"inner"},{"id":"w34367080","type":"way","role":"inner"},{"id":"w34369815","type":"way","role":"inner"},{"id":"w34369820","type":"way","role":"inner"}]},"n1819848881":{"id":"n1819848881","loc":[-85.638562,41.9569965]},"n1819848947":{"id":"n1819848947","loc":[-85.6384348,41.9576565]},"n1819849044":{"id":"n1819849044","loc":[-85.6385749,41.9573345]},"n2114807547":{"id":"n2114807547","loc":[-85.6384626,41.9583756]},"n2114807564":{"id":"n2114807564","loc":[-85.638535,41.9581283]},"n2189015691":{"id":"n2189015691","loc":[-85.6435584,41.9565243]},"n2189015696":{"id":"n2189015696","loc":[-85.6435805,41.9566049]},"n2189015722":{"id":"n2189015722","loc":[-85.6435035,41.9567438]},"n2189015744":{"id":"n2189015744","loc":[-85.6437991,41.9569582]},"n2189015747":{"id":"n2189015747","loc":[-85.6433042,41.9567742]},"n2189015750":{"id":"n2189015750","loc":[-85.6433827,41.9566844]},"n2189015753":{"id":"n2189015753","loc":[-85.6430447,41.9565588]},"n2189015756":{"id":"n2189015756","loc":[-85.6431111,41.956451]},"n2189015759":{"id":"n2189015759","loc":[-85.6420247,41.956083]},"n2189015760":{"id":"n2189015760","loc":[-85.6419945,41.9561369]},"n2189015764":{"id":"n2189015764","loc":[-85.6413729,41.9558945]},"n2189015766":{"id":"n2189015766","loc":[-85.6412884,41.9560606]},"n2189015770":{"id":"n2189015770","loc":[-85.6411798,41.9560112]},"n2189015771":{"id":"n2189015771","loc":[-85.6410651,41.9562132]},"n2189015774":{"id":"n2189015774","loc":[-85.6409504,41.9561728]},"n2189015778":{"id":"n2189015778","loc":[-85.6407996,41.9564241]},"n2189015781":{"id":"n2189015781","loc":[-85.6406889,41.9563892]},"n2189015785":{"id":"n2189015785","loc":[-85.6404857,41.9567024]},"n2189015789":{"id":"n2189015789","loc":[-85.6406909,41.9567877]},"n2189015793":{"id":"n2189015793","loc":[-85.6405642,41.9570165]},"n2189015796":{"id":"n2189015796","loc":[-85.6415359,41.9573711]},"n2189015800":{"id":"n2189015800","loc":[-85.6411738,41.9579501]},"n2189015804":{"id":"n2189015804","loc":[-85.6411119,41.957921]},"n2189015808":{"id":"n2189015808","loc":[-85.6403186,41.9591751]},"n2189015909":{"id":"n2189015909","loc":[-85.6389293,41.9564636]},"n2189015926":{"id":"n2189015926","loc":[-85.6385431,41.9564617]},"n2189015929":{"id":"n2189015929","loc":[-85.6385457,41.9561823]},"n2189015932":{"id":"n2189015932","loc":[-85.6389319,41.9561843]},"n2199109865":{"id":"n2199109865","loc":[-85.6400768,41.956776]},"n2199109867":{"id":"n2199109867","loc":[-85.639902,41.9567153]},"n2199109869":{"id":"n2199109869","loc":[-85.640004,41.956553]},"n2199109871":{"id":"n2199109871","loc":[-85.6401788,41.9566137]},"n2199109873":{"id":"n2199109873","loc":[-85.6399316,41.9564506],"tags":{"man_made":"water_tower"}},"n2199109876":{"id":"n2199109876","loc":[-85.6397689,41.9572354]},"n2199109878":{"id":"n2199109878","loc":[-85.6399229,41.9569826]},"n2199109880":{"id":"n2199109880","loc":[-85.639706,41.9569095]},"n2199109882":{"id":"n2199109882","loc":[-85.639552,41.9571623]},"n2199109884":{"id":"n2199109884","loc":[-85.6391028,41.9569517]},"n2199109886":{"id":"n2199109886","loc":[-85.6392876,41.956646]},"n2199109888":{"id":"n2199109888","loc":[-85.639484,41.9567117]},"n2199109889":{"id":"n2199109889","loc":[-85.6394322,41.9567973]},"n2199109890":{"id":"n2199109890","loc":[-85.6393718,41.9567771]},"n2199109891":{"id":"n2199109891","loc":[-85.6392387,41.9569972]},"n1819848900":{"id":"n1819848900","loc":[-85.638281,41.9576578]},"n1819848978":{"id":"n1819848978","loc":[-85.6377186,41.9580867]},"n1819849039":{"id":"n1819849039","loc":[-85.6384217,41.9573405]},"n1819849050":{"id":"n1819849050","loc":[-85.6377011,41.9570042]},"n1819849088":{"id":"n1819849088","loc":[-85.6382879,41.9580817]},"n2114807549":{"id":"n2114807549","loc":[-85.6362551,41.96473]},"n2114807587":{"id":"n2114807587","loc":[-85.6368694,41.9629829]},"n2189015725":{"id":"n2189015725","loc":[-85.644156,41.9569753]},"n2189015741":{"id":"n2189015741","loc":[-85.6419825,41.9597632]},"w208627217":{"id":"w208627217","tags":{"area":"yes","building":"yes"},"nodes":["n2189015741","n2189015744","n2189015747","n2189015750","n2189015753","n2189015756","n2189015759","n2189015760","n2189015764","n2189015766","n2189015770","n2189015771","n2189015774","n2189015778","n2189015781","n2189015785","n2189015789","n2189015793","n2189015796","n2189015800","n2189015804","n2189015808","n2189015741"]},"w208627212":{"id":"w208627212","tags":{"highway":"service"},"nodes":["n2189015691","n2189015696","n2189015722","n2189015725"]},"w209717057":{"id":"w209717057","tags":{"area":"yes","building":"yes"},"nodes":["n2199109876","n2199109878","n2199109880","n2199109882","n2199109876"]},"w209717056":{"id":"w209717056","tags":{"area":"yes","building":"yes"},"nodes":["n2199109865","n2199109867","n2199109869","n2199109871","n2199109865"]},"w208627231":{"id":"w208627231","tags":{"area":"yes","building":"yes"},"nodes":["n2189015909","n2189015926","n2189015929","n2189015932","n2189015909"]},"w170848326":{"id":"w170848326","tags":{"building":"yes"},"nodes":["n1819848881","n1819849050","n1819848978","n1819849088","n1819848900","n1819848947","n1819849039","n1819849044","n1819848881"]},"w17963182":{"id":"w17963182","tags":{"highway":"service"},"nodes":["n185949763","n185949765","n2189015691","n185949745"]},"w201484340":{"id":"w201484340","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807565","n2114807564","n2114807547","n2114807587","n2114807558","n2114807549","n2114807593"]},"w209717058":{"id":"w209717058","tags":{"area":"yes","building":"yes"},"nodes":["n2199109884","n2199109886","n2199109888","n2199109889","n2199109890","n2199109891","n2199109884"]},"n185954650":{"id":"n185954650","loc":[-85.627331,41.957439]},"n185966949":{"id":"n185966949","loc":[-85.626868,41.957314]},"n185989335":{"id":"n185989335","loc":[-85.62529,41.958568]},"n185989337":{"id":"n185989337","loc":[-85.624962,41.958453]},"n185989339":{"id":"n185989339","loc":[-85.624832,41.958399]},"n185989340":{"id":"n185989340","loc":[-85.624707,41.958325]},"n185989342":{"id":"n185989342","loc":[-85.624636,41.958251]},"n185989345":{"id":"n185989345","loc":[-85.624578,41.95818]},"n185989347":{"id":"n185989347","loc":[-85.624533,41.958099]},"n185989349":{"id":"n185989349","loc":[-85.624507,41.957985]},"n185989351":{"id":"n185989351","loc":[-85.624495,41.957807]},"n185989353":{"id":"n185989353","loc":[-85.624514,41.957663]},"n185989354":{"id":"n185989354","loc":[-85.624577,41.957593]},"n185989356":{"id":"n185989356","loc":[-85.624685,41.95754]},"n185989357":{"id":"n185989357","loc":[-85.624802,41.957523]},"n185989359":{"id":"n185989359","loc":[-85.624996,41.957524]},"n185989361":{"id":"n185989361","loc":[-85.625409,41.957515]},"n185989364":{"id":"n185989364","loc":[-85.625634,41.957496]},"n185989367":{"id":"n185989367","loc":[-85.625832,41.957453]},"n185989368":{"id":"n185989368","loc":[-85.626044,41.957394]},"n354031352":{"id":"n354031352","loc":[-85.6252778,41.9586111],"tags":{"amenity":"place_of_worship","denomination":"baptist","name":"First Baptist Church","religion":"christian"}},"n2199109892":{"id":"n2199109892","loc":[-85.6261578,41.9589963]},"n2199109893":{"id":"n2199109893","loc":[-85.6263191,41.9586865]},"n2199109894":{"id":"n2199109894","loc":[-85.6261186,41.9586288]},"n2199109895":{"id":"n2199109895","loc":[-85.6260644,41.9587329]},"n2199109896":{"id":"n2199109896","loc":[-85.6261547,41.9587589]},"n2199109898":{"id":"n2199109898","loc":[-85.6260476,41.9589646]},"n185966951":{"id":"n185966951","loc":[-85.628404,41.957438]},"w17965351":{"id":"w17965351","tags":{"highway":"residential","name":"Flower Street"},"nodes":["n185966948","n185966949","n185954650","n185966951","n185966953","n185966955","n185966957"]},"w17967809":{"id":"w17967809","tags":{"highway":"residential","name":"Azaleamum Drive"},"nodes":["n185982197","n185989335","n185989337","n185989339","n185989340","n185989342","n185989345","n185989347","n185989349","n185989351","n185989353","n185989354","n185989356","n185989357","n185989359","n185989361","n185989364","n185989367","n185989368","n185982196"]},"w209717059":{"id":"w209717059","tags":{"area":"yes","building":"yes"},"nodes":["n2199109892","n2199109893","n2199109894","n2199109895","n2199109896","n2199109898","n2199109892"]},"n185961390":{"id":"n185961390","loc":[-85.63137,41.959033]},"n185961393":{"id":"n185961393","loc":[-85.634315,41.959017]},"w17966214":{"id":"w17966214","tags":{"highway":"residential","name":"East Adams Street"},"nodes":["n185975351","n185967434","n185968108"]},"w17964793":{"id":"w17964793","tags":{"highway":"residential","name":"Morris Ave"},"nodes":["n185961389","n185961390","n185961391","n185961393","n185961396"]},"n185952166":{"id":"n185952166","loc":[-85.638174,41.95831]},"n2114807552":{"id":"n2114807552","loc":[-85.6383526,41.9593788]},"n2114807591":{"id":"n2114807591","loc":[-85.6383741,41.9593968]},"n2189015731":{"id":"n2189015731","loc":[-85.6368404,41.9592785]},"n2189015734":{"id":"n2189015734","loc":[-85.6368404,41.9585918]},"n2189015737":{"id":"n2189015737","loc":[-85.6376009,41.9585918]},"n2189015738":{"id":"n2189015738","loc":[-85.6376009,41.9592785]},"n2189015897":{"id":"n2189015897","loc":[-85.6376839,41.9566137]},"n2189015900":{"id":"n2189015900","loc":[-85.6376831,41.9564865]},"n2189015903":{"id":"n2189015903","loc":[-85.6381161,41.9564851]},"n2189015906":{"id":"n2189015906","loc":[-85.6381168,41.9566122]},"n2189015937":{"id":"n2189015937","loc":[-85.6364789,41.9590634]},"n2189015940":{"id":"n2189015940","loc":[-85.6361137,41.9590672]},"n2189015943":{"id":"n2189015943","loc":[-85.6361169,41.9594033]},"n2189015945":{"id":"n2189015945","loc":[-85.6363456,41.9594021]},"n2189015952":{"id":"n2189015952","loc":[-85.636112,41.958892]},"n2189015955":{"id":"n2189015955","loc":[-85.6364757,41.9588894]},"n2189015957":{"id":"n2189015957","loc":[-85.6364729,41.9586747]},"n2189015958":{"id":"n2189015958","loc":[-85.6361103,41.9586765]},"n2189015959":{"id":"n2189015959","loc":[-85.6364719,41.9585562]},"n2189015960":{"id":"n2189015960","loc":[-85.6361093,41.958558]},"n2189015961":{"id":"n2189015961","loc":[-85.6355494,41.9586403]},"n2189015962":{"id":"n2189015962","loc":[-85.635549,41.9584711]},"n2189015963":{"id":"n2189015963","loc":[-85.6351831,41.9584715]},"n2189015964":{"id":"n2189015964","loc":[-85.6351834,41.9586408]},"n2189015966":{"id":"n2189015966","loc":[-85.6359579,41.9586359]},"n2189015968":{"id":"n2189015968","loc":[-85.6359561,41.9585465]},"n2189015971":{"id":"n2189015971","loc":[-85.6355476,41.9585509]},"n2189015974":{"id":"n2189015974","loc":[-85.6359516,41.9592934]},"n2189015977":{"id":"n2189015977","loc":[-85.635949,41.9586697]},"n2189015980":{"id":"n2189015980","loc":[-85.6351329,41.9586716]},"n2189015983":{"id":"n2189015983","loc":[-85.6351318,41.9583949]},"n2189015986":{"id":"n2189015986","loc":[-85.6349148,41.9583954]},"n2189015989":{"id":"n2189015989","loc":[-85.6349186,41.9592958]},"n2189015995":{"id":"n2189015995","loc":[-85.6360173,41.9593286]},"n2189015998":{"id":"n2189015998","loc":[-85.6360278,41.9583079]},"n2114807550":{"id":"n2114807550","loc":[-85.6383392,41.9595404]},"n2114807551":{"id":"n2114807551","loc":[-85.6375855,41.9616107]},"n2114807559":{"id":"n2114807559","loc":[-85.6373978,41.9621273]},"n2114807562":{"id":"n2114807562","loc":[-85.6373361,41.9622609]},"n2114807563":{"id":"n2114807563","loc":[-85.6376472,41.9613953]},"n2114807574":{"id":"n2114807574","loc":[-85.636974,41.9627695]},"n2114807589":{"id":"n2114807589","loc":[-85.6383017,41.9595005]},"n2114807592":{"id":"n2114807592","loc":[-85.6377169,41.9613494]},"n2114807595":{"id":"n2114807595","loc":[-85.6371081,41.962574]},"n2189015934":{"id":"n2189015934","loc":[-85.6364855,41.9595098]},"n2189015949":{"id":"n2189015949","loc":[-85.6363466,41.9595105]},"w208627244":{"id":"w208627244","tags":{"highway":"service"},"nodes":["n2189015992","n2189015995","n2189015998"]},"w208627240":{"id":"w208627240","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015971","n2189015962","n2189015963","n2189015964","n2189015961"]},"w17967437":{"id":"w17967437","tags":{"highway":"residential","name":"Lyman St"},"nodes":["n185964361","n185984024"]},"w208627237":{"id":"w208627237","tags":{"area":"yes","building":"yes"},"nodes":["n2189015955","n2189015957","n2189015958","n2189015952","n2189015955"]},"w17967465":{"id":"w17967465","tags":{"highway":"residential","name":"W Adams St"},"nodes":["n185978394","n185984022","n185964360"]},"w208627228":{"id":"w208627228","tags":{"area":"yes","building":"yes"},"nodes":["n2189015897","n2189015900","n2189015903","n2189015906","n2189015897"]},"w201484351":{"id":"w201484351","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807587","n2114807574","n2114807595","n2114807562","n2114807559","n2114807551","n2114807563","n2114807589","n2114807552"]},"w208627239":{"id":"w208627239","tags":{"area":"yes","building":"yes"},"nodes":["n2189015957","n2189015959","n2189015960","n2189015958","n2189015957"]},"w208627233":{"id":"w208627233","tags":{"area":"yes","building":"yes"},"nodes":["n2189015934","n2189015937","n2189015940","n2189015943","n2189015945","n2189015949","n2189015934"]},"w208627241":{"id":"w208627241","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015966","n2189015968","n2189015971","n2189015961"]},"w17967970":{"id":"w17967970","tags":{"highway":"residential","name":"Adams St"},"nodes":["n185975351","n185978394"]},"w208627235":{"id":"w208627235","tags":{"area":"yes","building":"yes"},"nodes":["n2189015940","n2189015952","n2189015955","n2189015937","n2189015940"]},"w17965468":{"id":"w17965468","tags":{"highway":"residential","name":"Armstrong Blvd"},"nodes":["n185967917","n2189015998","n185967918","n185964362","n185952166"]},"w201484346":{"id":"w201484346","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807551","n2114807592","n2114807550","n2114807591"]},"w208627242":{"id":"w208627242","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015974","n2189015977","n2189015980","n2189015983","n2189015986","n2189015989","n2189015974"]},"w208627216":{"id":"w208627216","tags":{"area":"yes","building":"yes"},"nodes":["n2189015731","n2189015734","n2189015737","n2189015738","n2189015731"]},"n185984309":{"id":"n185984309","loc":[-85.631421,41.961494]},"n185987987":{"id":"n185987987","loc":[-85.631456,41.960673]},"n185965397":{"id":"n185965397","loc":[-85.634603,41.959838]},"w17965196":{"id":"w17965196","tags":{"highway":"residential","name":"Burke Ave"},"nodes":["n185965395","n185965397","n185965399"]},"w17967215":{"id":"w17967215","tags":{"highway":"residential","name":"Kellogg Ave"},"nodes":["n185968114","n185984309","n185967440","n185978402"]},"w17967597":{"id":"w17967597","tags":{"highway":"residential","name":"Barnard Ave"},"nodes":["n185968112","n185987987","n185967438","n185978399"]},"n394490857":{"id":"n394490857","loc":[-85.633952,41.960664]},"n394490858":{"id":"n394490858","loc":[-85.633938,41.960227]},"n394490859":{"id":"n394490859","loc":[-85.634794,41.960212]},"n394490860":{"id":"n394490860","loc":[-85.634815,41.960662]},"n394490861":{"id":"n394490861","loc":[-85.634103,41.961268]},"n394490862":{"id":"n394490862","loc":[-85.634103,41.961001]},"n394490863":{"id":"n394490863","loc":[-85.634504,41.961003]},"n394490864":{"id":"n394490864","loc":[-85.634561,41.961269]},"n1057629869":{"id":"n1057629869","loc":[-85.6382599,41.9612134]},"n1057629937":{"id":"n1057629937","loc":[-85.6380035,41.9616137]},"n2189016014":{"id":"n2189016014","loc":[-85.6360365,41.9626496]},"n2189016017":{"id":"n2189016017","loc":[-85.6360374,41.9623228]},"n2189016020":{"id":"n2189016020","loc":[-85.6367557,41.9623239]},"n2189016022":{"id":"n2189016022","loc":[-85.6367566,41.9619919]},"n2189016025":{"id":"n2189016025","loc":[-85.6351794,41.9619893]},"n2189016028":{"id":"n2189016028","loc":[-85.6351788,41.9622011]},"n2189016031":{"id":"n2189016031","loc":[-85.6350855,41.9622009]},"n2189016034":{"id":"n2189016034","loc":[-85.6350845,41.962527]},"n2189016037":{"id":"n2189016037","loc":[-85.6352732,41.9625273]},"n2189016039":{"id":"n2189016039","loc":[-85.6352738,41.9623178]},"n2189016042":{"id":"n2189016042","loc":[-85.6357712,41.9623186]},"n2189016044":{"id":"n2189016044","loc":[-85.6357702,41.9626492]},"n1057629880":{"id":"n1057629880","loc":[-85.638817,41.9619017]},"n1057629923":{"id":"n1057629923","loc":[-85.6390733,41.9615014]},"w91092312":{"id":"w91092312","tags":{"power":"station"},"nodes":["n1057629923","n1057629869","n1057629937","n1057629880","n1057629923"]},"w34369826":{"id":"w34369826","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490861","n394490862","n394490863","n394490864","n394490861"]},"w34369825":{"id":"w34369825","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490857","n394490858","n394490859","n394490860","n394490857"]},"w208627248":{"id":"w208627248","tags":{"area":"yes","building":"yes"},"nodes":["n2189016014","n2189016017","n2189016020","n2189016022","n2189016025","n2189016028","n2189016031","n2189016034","n2189016037","n2189016039","n2189016042","n2189016044","n2189016014"]},"n394490766":{"id":"n394490766","loc":[-85.616777,41.955642]},"n394490768":{"id":"n394490768","loc":[-85.617239,41.955644]},"n394490792":{"id":"n394490792","loc":[-85.619034,41.95543]},"n185972055":{"id":"n185972055","loc":[-85.6185905,41.9568211]},"n185972057":{"id":"n185972057","loc":[-85.6186688,41.9570086]},"n185972059":{"id":"n185972059","loc":[-85.6186924,41.9581453]},"n185972060":{"id":"n185972060","loc":[-85.6187082,41.9588211],"tags":{"highway":"turning_circle"}},"n1819790724":{"id":"n1819790724","loc":[-85.6182155,41.9555703]},"n1819790735":{"id":"n1819790735","loc":[-85.6184059,41.9566188]},"n1819790799":{"id":"n1819790799","loc":[-85.6182372,41.9563771]},"n1819790896":{"id":"n1819790896","loc":[-85.6181431,41.9557227]},"n185971405":{"id":"n185971405","loc":[-85.6186766,41.9577468]},"n185971565":{"id":"n185971565","loc":[-85.6181613,41.9560879]},"n185967985":{"id":"n185967985","loc":[-85.6186798,41.9585791]},"n185955753":{"id":"n185955753","loc":[-85.620773,41.9555854]},"n185955755":{"id":"n185955755","loc":[-85.6212652,41.9559891]},"n185955748":{"id":"n185955748","loc":[-85.620722,41.954858]},"n185955751":{"id":"n185955751","loc":[-85.6206912,41.955367]},"n185967987":{"id":"n185967987","loc":[-85.6159351,41.9585809]},"n185971407":{"id":"n185971407","loc":[-85.6159142,41.9577578]},"n185971570":{"id":"n185971570","loc":[-85.6162248,41.95603]},"n185971572":{"id":"n185971572","loc":[-85.6160402,41.9560749]},"n185971574":{"id":"n185971574","loc":[-85.61593,41.956201]},"n185981301":{"id":"n185981301","loc":[-85.6158973,41.9581601]},"n394490762":{"id":"n394490762","loc":[-85.617193,41.954706]},"n394490764":{"id":"n394490764","loc":[-85.616773,41.954737]},"n394490787":{"id":"n394490787","loc":[-85.618972,41.954737]},"n394490790":{"id":"n394490790","loc":[-85.619046,41.954929]},"n394490794":{"id":"n394490794","loc":[-85.619922,41.955296]},"n394490796":{"id":"n394490796","loc":[-85.61991,41.95501]},"n394490798":{"id":"n394490798","loc":[-85.619974,41.954751]},"n1819790677":{"id":"n1819790677","loc":[-85.6187031,41.9550522]},"n1819790787":{"id":"n1819790787","loc":[-85.6186436,41.9552022]},"n1819790828":{"id":"n1819790828","loc":[-85.6185127,41.9553393]},"w17966857":{"id":"w17966857","tags":{"access":"private","highway":"service","name":"Sable River Rd"},"nodes":["n185972059","n185981301"]},"w34369814":{"id":"w34369814","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490787","n394490790","n394490792","n394490794","n394490796","n394490798","n394490787"]},"w17964176":{"id":"w17964176","tags":{"highway":"residential"},"nodes":["n185955747","n185955748","n185955751","n185955753","n185955755"]},"w17965838":{"id":"w17965838","tags":{"access":"private","highway":"service","name":"Pine River Rd"},"nodes":["n185971405","n185971407"]},"w17965476":{"id":"w17965476","tags":{"access":"private","highway":"service","name":"Raisin River Rd"},"nodes":["n185967985","n185967987"]},"w17965913":{"id":"w17965913","tags":{"access":"private","highway":"service","name":"Shiawassee River Rd"},"nodes":["n185972054","n1819790677","n1819790787","n1819790828","n1819790724","n1819790896","n185971565","n1819790799","n1819790735","n185972055","n185972057","n185971405","n185972059","n185967985","n185972060"]},"w34369811":{"id":"w34369811","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490762","n394490764","n394490766","n394490768","n394490762"]},"w17965854":{"id":"w17965854","tags":{"access":"private","highway":"service","name":"Sturgeon River Rd"},"nodes":["n185971565","n185971570","n185971572","n185971574"]},"n2139795769":{"id":"n2139795769","loc":[-85.6250804,41.9608796]},"n2139795770":{"id":"n2139795770","loc":[-85.6250315,41.9613684]},"n2139795771":{"id":"n2139795771","loc":[-85.6249671,41.9614362]},"n2139795772":{"id":"n2139795772","loc":[-85.6249698,41.961522]},"n2139795773":{"id":"n2139795773","loc":[-85.6250798,41.9615838]},"n2139795774":{"id":"n2139795774","loc":[-85.6252273,41.9615639]},"n2139795775":{"id":"n2139795775","loc":[-85.6252863,41.9614622]},"n2139795776":{"id":"n2139795776","loc":[-85.6252273,41.9613764]},"n2139795777":{"id":"n2139795777","loc":[-85.6251227,41.9613525]},"n2139795778":{"id":"n2139795778","loc":[-85.6249564,41.9612527]},"n2139795779":{"id":"n2139795779","loc":[-85.6249846,41.9610254]},"n2139795780":{"id":"n2139795780","loc":[-85.6266725,41.9599647]},"n2139795781":{"id":"n2139795781","loc":[-85.6259162,41.9599711]},"n2139795782":{"id":"n2139795782","loc":[-85.6257185,41.960019]},"n2139795783":{"id":"n2139795783","loc":[-85.6255509,41.9601213]},"n185963539":{"id":"n185963539","loc":[-85.615718,41.983893]},"n185964418":{"id":"n185964418","loc":[-85.616626,42.049512]},"n185966614":{"id":"n185966614","loc":[-85.615514,41.976603]},"n185966635":{"id":"n185966635","loc":[-85.616118,42.013017]},"n185969040":{"id":"n185969040","loc":[-85.615632,41.972357]},"n185969070":{"id":"n185969070","loc":[-85.619145,41.967648]},"n185972156":{"id":"n185972156","loc":[-85.621894,41.963956]},"n185972157":{"id":"n185972157","loc":[-85.621806,41.964077]},"n185972158":{"id":"n185972158","loc":[-85.620848,41.965341]},"n185972159":{"id":"n185972159","loc":[-85.620684,41.965558]},"n185972160":{"id":"n185972160","loc":[-85.620621,41.965658]},"n185972161":{"id":"n185972161","loc":[-85.617844,41.969359]},"n185972162":{"id":"n185972162","loc":[-85.616843,41.97068]},"n185972164":{"id":"n185972164","loc":[-85.616714,41.970839]},"n185972166":{"id":"n185972166","loc":[-85.615879,41.971969]},"n185972168":{"id":"n185972168","loc":[-85.615748,41.972159]},"n185972170":{"id":"n185972170","loc":[-85.615589,41.972502]},"n185972172":{"id":"n185972172","loc":[-85.615542,41.972733]},"n185972175":{"id":"n185972175","loc":[-85.615524,41.972947]},"n185972177":{"id":"n185972177","loc":[-85.615512,41.973715]},"n185972179":{"id":"n185972179","loc":[-85.615513,41.976496]},"n185972180":{"id":"n185972180","loc":[-85.615538,41.977246]},"n185972181":{"id":"n185972181","loc":[-85.61558,41.982139]},"n185972184":{"id":"n185972184","loc":[-85.61557,41.983317]},"n185972186":{"id":"n185972186","loc":[-85.615591,41.983463]},"n185972188":{"id":"n185972188","loc":[-85.615763,41.984146]},"n185972190":{"id":"n185972190","loc":[-85.615814,41.98435]},"n185972192":{"id":"n185972192","loc":[-85.615965,41.998453]},"n185972194":{"id":"n185972194","loc":[-85.615982,42.001237]},"n185972195":{"id":"n185972195","loc":[-85.616055,42.00555]},"n185972197":{"id":"n185972197","loc":[-85.616134,42.014887]},"n185972199":{"id":"n185972199","loc":[-85.616177,42.018465]},"n185972201":{"id":"n185972201","loc":[-85.616298,42.027627]},"n185972203":{"id":"n185972203","loc":[-85.616513,42.042212]},"w203968015":{"id":"w203968015","tags":{"highway":"residential"},"nodes":["n2139795768","n2139795769"]},"w17965932":{"id":"w17965932","tags":{"highway":"residential","name":"Buckhorn Road","name_1":"County Highway 122"},"nodes":["n185972155","n185972156","n185972157","n185972158","n185972159","n185972160","n185969070","n185972161","n185972162","n185972164","n185972166","n185972168","n185969040","n185972170","n185972172","n185972175","n185972177","n185972179","n185966614","n185972180","n185972181","n185972184","n185972186","n185963539","n185972188","n185972190","n185972192","n185972194","n185972195","n185966635","n185972197","n185972199","n185972201","n185972203","n185964418"]},"w203968016":{"id":"w203968016","tags":{"highway":"residential","name":"New Jersey Court"},"nodes":["n2139795770","n2139795771","n2139795772","n2139795773","n2139795774","n2139795775","n2139795776","n2139795777","n2139795770","n2139795778","n2139795779","n2139795769"]},"w203968017":{"id":"w203968017","tags":{"highway":"residential","name":"Oklahoma Drive"},"nodes":["n2139795780","n2139795781","n2139795782","n2139795783","n2139795769"]},"n1819790528":{"id":"n1819790528","loc":[-85.6184827,41.960025]},"n1819790530":{"id":"n1819790530","loc":[-85.6168626,41.9605834]},"n1819790534":{"id":"n1819790534","loc":[-85.6197379,41.9617163]},"n1819790541":{"id":"n1819790541","loc":[-85.6198881,41.9620833]},"n1819790543":{"id":"n1819790543","loc":[-85.619695,41.9619397]},"n1819790547":{"id":"n1819790547","loc":[-85.6190298,41.9609504]},"n1819790555":{"id":"n1819790555","loc":[-85.6180471,41.9609788]},"n1819790559":{"id":"n1819790559","loc":[-85.6203817,41.9605436]},"n1819790583":{"id":"n1819790583","loc":[-85.6201564,41.9603282]},"n1819790590":{"id":"n1819790590","loc":[-85.617045,41.9598894]},"n1819790609":{"id":"n1819790609","loc":[-85.6177638,41.9598495]},"n1819790618":{"id":"n1819790618","loc":[-85.6195234,41.9610143]},"n1819790642":{"id":"n1819790642","loc":[-85.6181179,41.9627933]},"n1819790659":{"id":"n1819790659","loc":[-85.6174634,41.962897]},"n1819790665":{"id":"n1819790665","loc":[-85.6170343,41.9630885]},"n1819790674":{"id":"n1819790674","loc":[-85.6194697,41.9601925]},"n1819790685":{"id":"n1819790685","loc":[-85.6207722,41.9610665]},"n1819790687":{"id":"n1819790687","loc":[-85.6202315,41.9622109]},"n1819790697":{"id":"n1819790697","loc":[-85.6184505,41.9624662]},"n1819790726":{"id":"n1819790726","loc":[-85.6178926,41.9628492]},"n1819790738":{"id":"n1819790738","loc":[-85.6173347,41.9598016]},"n1819790762":{"id":"n1819790762","loc":[-85.6186221,41.9609105]},"n1819790774":{"id":"n1819790774","loc":[-85.6175922,41.9608308]},"n1819790781":{"id":"n1819790781","loc":[-85.6167768,41.9633198]},"n1819790796":{"id":"n1819790796","loc":[-85.619856,41.961461]},"n1819790811":{"id":"n1819790811","loc":[-85.6208215,41.9620195]},"n1819790833":{"id":"n1819790833","loc":[-85.618311,41.9612536]},"n1819790854":{"id":"n1819790854","loc":[-85.6183646,41.9626417]},"n1819790863":{"id":"n1819790863","loc":[-85.6204997,41.9608547]},"n1819790867":{"id":"n1819790867","loc":[-85.6184934,41.9621391]},"n1819790877":{"id":"n1819790877","loc":[-85.6206928,41.9621152]},"n1819790881":{"id":"n1819790881","loc":[-85.6170879,41.960735]},"n1819790891":{"id":"n1819790891","loc":[-85.6168304,41.9601207]},"n1819790898":{"id":"n1819790898","loc":[-85.619813,41.9612297]},"n1819790909":{"id":"n1819790909","loc":[-85.6167982,41.960376]},"n1819790912":{"id":"n1819790912","loc":[-85.6205855,41.9610462]},"n1819790544":{"id":"n1819790544","loc":[-85.612968,41.9707781]},"n1819790549":{"id":"n1819790549","loc":[-85.614395,41.9697172]},"n1819790552":{"id":"n1819790552","loc":[-85.6180535,41.9655536]},"n1819790554":{"id":"n1819790554","loc":[-85.6111227,41.9703713]},"n1819790560":{"id":"n1819790560","loc":[-85.6112729,41.9701958]},"n1819790563":{"id":"n1819790563","loc":[-85.6137512,41.9689917]},"n1819790564":{"id":"n1819790564","loc":[-85.6181072,41.9659205]},"n1819790595":{"id":"n1819790595","loc":[-85.6170021,41.9666863]},"n1819790605":{"id":"n1819790605","loc":[-85.6168948,41.9644527]},"n1819790606":{"id":"n1819790606","loc":[-85.6128071,41.9701081]},"n1819790607":{"id":"n1819790607","loc":[-85.6129251,41.9704032]},"n1819790612":{"id":"n1819790612","loc":[-85.6177638,41.9663912]},"n1819790615":{"id":"n1819790615","loc":[-85.6152533,41.9670373]},"n1819790622":{"id":"n1819790622","loc":[-85.6146739,41.9673804]},"n1819790623":{"id":"n1819790623","loc":[-85.6180428,41.9661838]},"n1819790625":{"id":"n1819790625","loc":[-85.6172918,41.9646202]},"n1819790645":{"id":"n1819790645","loc":[-85.6178067,41.965043]},"n1819790647":{"id":"n1819790647","loc":[-85.6143306,41.9712488]},"n1819790649":{"id":"n1819790649","loc":[-85.6147383,41.9707702]},"n1819790654":{"id":"n1819790654","loc":[-85.6157361,41.9668459]},"n1819790657":{"id":"n1819790657","loc":[-85.6145666,41.9710733]},"n1819790668":{"id":"n1819790668","loc":[-85.6166909,41.9642692]},"n1819790671":{"id":"n1819790671","loc":[-85.6141482,41.9696538]},"n1819790679":{"id":"n1819790679","loc":[-85.6148349,41.9705388]},"n1819790686":{"id":"n1819790686","loc":[-85.6139551,41.9695501]},"n1819790696":{"id":"n1819790696","loc":[-85.6119703,41.9699087]},"n1819790704":{"id":"n1819790704","loc":[-85.6140731,41.9684174]},"n1819790706":{"id":"n1819790706","loc":[-85.6124745,41.9699246]},"n1819790718":{"id":"n1819790718","loc":[-85.6165407,41.9636868]},"n1819790720":{"id":"n1819790720","loc":[-85.61388,41.9687365]},"n1819790731":{"id":"n1819790731","loc":[-85.6165193,41.9639421]},"n1819790739":{"id":"n1819790739","loc":[-85.6146739,41.9699964]},"n1819790753":{"id":"n1819790753","loc":[-85.6173883,41.9665747]},"n1819790760":{"id":"n1819790760","loc":[-85.6133221,41.9712089]},"n1819790767":{"id":"n1819790767","loc":[-85.6116698,41.9699246]},"n1819790779":{"id":"n1819790779","loc":[-85.6130753,41.9710573]},"n1819790791":{"id":"n1819790791","loc":[-85.6137083,41.9692869]},"n1819790795":{"id":"n1819790795","loc":[-85.6141482,41.9679627]},"n1819790798":{"id":"n1819790798","loc":[-85.6137727,41.9694305]},"n1819790836":{"id":"n1819790836","loc":[-85.6143842,41.9676037]},"n1819790915":{"id":"n1819790915","loc":[-85.6148456,41.9702756]},"n1819790926":{"id":"n1819790926","loc":[-85.6138371,41.9713525]},"n1819790927":{"id":"n1819790927","loc":[-85.6141053,41.9713525]},"n1819790931":{"id":"n1819790931","loc":[-85.6162832,41.966814]},"n1821014625":{"id":"n1821014625","loc":[-85.5960611,41.9808498]},"n1821014627":{"id":"n1821014627","loc":[-85.5565843,42.010982]},"n1821014629":{"id":"n1821014629","loc":[-85.5971541,41.9805808]},"n1821014632":{"id":"n1821014632","loc":[-85.6061837,41.9725907]},"n1821014633":{"id":"n1821014633","loc":[-85.5247773,42.025766]},"n1821014635":{"id":"n1821014635","loc":[-85.5908938,41.9902384]},"n1821014636":{"id":"n1821014636","loc":[-85.5917682,41.9860637]},"n1821014637":{"id":"n1821014637","loc":[-85.5456556,42.0166797]},"n1821014638":{"id":"n1821014638","loc":[-85.5795749,42.0032352]},"n1821014639":{"id":"n1821014639","loc":[-85.6103988,41.9723456]},"n1821014642":{"id":"n1821014642","loc":[-85.5818816,42.0022466]},"n1821014643":{"id":"n1821014643","loc":[-85.5570604,42.0091586]},"n1821014644":{"id":"n1821014644","loc":[-85.5952886,41.9803792]},"n1821014645":{"id":"n1821014645","loc":[-85.5780366,42.0040343]},"n1821014646":{"id":"n1821014646","loc":[-85.6050505,41.9751971]},"n1821014647":{"id":"n1821014647","loc":[-85.5854435,41.9946162]},"n1821014648":{"id":"n1821014648","loc":[-85.5452278,42.0168768]},"n1821014649":{"id":"n1821014649","loc":[-85.6023254,41.9780166]},"n1821014651":{"id":"n1821014651","loc":[-85.5761899,42.0046783]},"n1821014653":{"id":"n1821014653","loc":[-85.5897351,41.9876707]},"n1821014657":{"id":"n1821014657","loc":[-85.5963601,41.9808998]},"n1821014658":{"id":"n1821014658","loc":[-85.5892952,41.9951983]},"n1821014660":{"id":"n1821014660","loc":[-85.5778328,42.0037194]},"n1821014661":{"id":"n1821014661","loc":[-85.5541475,42.0125705]},"n1821014663":{"id":"n1821014663","loc":[-85.5914047,41.9856469]},"n1821014664":{"id":"n1821014664","loc":[-85.6101681,41.9727723]},"n1821014665":{"id":"n1821014665","loc":[-85.5910172,41.9854696]},"n1821014666":{"id":"n1821014666","loc":[-85.5398688,42.0187699]},"n1821014667":{"id":"n1821014667","loc":[-85.5218752,42.0282884]},"n1821014668":{"id":"n1821014668","loc":[-85.5159582,42.0329384]},"n1821014669":{"id":"n1821014669","loc":[-85.5898102,41.9847319]},"n1821014670":{"id":"n1821014670","loc":[-85.5734809,42.0066235]},"n1821014671":{"id":"n1821014671","loc":[-85.5922939,41.980852]},"n1821014672":{"id":"n1821014672","loc":[-85.6023629,41.9781163]},"n1821014674":{"id":"n1821014674","loc":[-85.5409953,42.0191724]},"n1821014676":{"id":"n1821014676","loc":[-85.584435,41.9949909]},"n1821014677":{"id":"n1821014677","loc":[-85.5972399,41.9783835]},"n1821014678":{"id":"n1821014678","loc":[-85.5616738,42.0071337]},"n1821014681":{"id":"n1821014681","loc":[-85.5202994,42.0310755]},"n1821014682":{"id":"n1821014682","loc":[-85.5915912,41.9857767]},"n1821014684":{"id":"n1821014684","loc":[-85.6022288,41.977897]},"n1821014687":{"id":"n1821014687","loc":[-85.5933024,41.9846362]},"n1821014688":{"id":"n1821014688","loc":[-85.5846871,41.9956169]},"n1821014689":{"id":"n1821014689","loc":[-85.5898209,41.99037]},"n1821014691":{"id":"n1821014691","loc":[-85.5448939,42.0149261]},"n1821014692":{"id":"n1821014692","loc":[-85.5977763,41.9786348]},"n1821014694":{"id":"n1821014694","loc":[-85.5767706,42.0034523]},"n1821014695":{"id":"n1821014695","loc":[-85.6103559,41.9726766]},"n1821014697":{"id":"n1821014697","loc":[-85.5922134,41.9809876]},"n1821014698":{"id":"n1821014698","loc":[-85.5935277,41.9831728]},"n1821014700":{"id":"n1821014700","loc":[-85.5674674,42.0078273]},"n1821014703":{"id":"n1821014703","loc":[-85.6021,41.9778053]},"n1821014704":{"id":"n1821014704","loc":[-85.5756763,42.0053737]},"n1821014705":{"id":"n1821014705","loc":[-85.5887695,41.9895207]},"n1821014707":{"id":"n1821014707","loc":[-85.6061073,41.9746866]},"n1821014708":{"id":"n1821014708","loc":[-85.6033446,41.9751692]},"n1821014710":{"id":"n1821014710","loc":[-85.5180986,42.0322332]},"n1821014711":{"id":"n1821014711","loc":[-85.543365,42.0163569]},"n1821014712":{"id":"n1821014712","loc":[-85.6030656,41.9753646]},"n1821014713":{"id":"n1821014713","loc":[-85.6104417,41.9704792]},"n1821014714":{"id":"n1821014714","loc":[-85.5205716,42.030998]},"n1821014716":{"id":"n1821014716","loc":[-85.516382,42.032536]},"n1821014717":{"id":"n1821014717","loc":[-85.5932863,41.9820882]},"n1821014718":{"id":"n1821014718","loc":[-85.5361928,42.0194974]},"n1821014720":{"id":"n1821014720","loc":[-85.6011613,41.9773586]},"n1821014721":{"id":"n1821014721","loc":[-85.554287,42.0109124]},"n1821014722":{"id":"n1821014722","loc":[-85.5577524,42.0103425]},"n1821014725":{"id":"n1821014725","loc":[-85.5867256,41.9921004]},"n1821014726":{"id":"n1821014726","loc":[-85.5856045,41.9968807]},"n1821014727":{"id":"n1821014727","loc":[-85.5545445,42.0106454]},"n1821014728":{"id":"n1821014728","loc":[-85.5923797,41.9842534]},"n1821014729":{"id":"n1821014729","loc":[-85.5696346,42.0081462]},"n1821014730":{"id":"n1821014730","loc":[-85.5998322,41.9786884]},"n1821014735":{"id":"n1821014735","loc":[-85.5337426,42.0218266]},"n1821014736":{"id":"n1821014736","loc":[-85.5847944,41.994672]},"n1821014740":{"id":"n1821014740","loc":[-85.5315271,42.0238669]},"n1821014741":{"id":"n1821014741","loc":[-85.5248846,42.027085]},"n1821014742":{"id":"n1821014742","loc":[-85.5853376,41.997018]},"n1821014743":{"id":"n1821014743","loc":[-85.5894883,41.988811]},"n1821014745":{"id":"n1821014745","loc":[-85.6095311,41.9726226]},"n1821014746":{"id":"n1821014746","loc":[-85.5531511,42.0133416]},"n1821014747":{"id":"n1821014747","loc":[-85.5735882,42.007058]},"n1821014749":{"id":"n1821014749","loc":[-85.5428554,42.0164366]},"n1821014751":{"id":"n1821014751","loc":[-85.5395255,42.0186304]},"n1821014752":{"id":"n1821014752","loc":[-85.571378,42.0083176]},"n1821014754":{"id":"n1821014754","loc":[-85.5541918,42.0113925]},"n1821014755":{"id":"n1821014755","loc":[-85.5278029,42.0250806]},"n1821014756":{"id":"n1821014756","loc":[-85.5936725,41.9827102]},"n1821014757":{"id":"n1821014757","loc":[-85.5176266,42.0346677]},"n1821014758":{"id":"n1821014758","loc":[-85.6096692,41.9714245]},"n1821014759":{"id":"n1821014759","loc":[-85.5770321,42.0034266]},"n1821014761":{"id":"n1821014761","loc":[-85.5988921,41.9779369]},"n1821014762":{"id":"n1821014762","loc":[-85.5811788,42.0024499]},"n1821014763":{"id":"n1821014763","loc":[-85.5154003,42.0381101]},"n1821014764":{"id":"n1821014764","loc":[-85.5155827,42.0374089]},"n1821014765":{"id":"n1821014765","loc":[-85.5891249,41.9884978]},"n1821014766":{"id":"n1821014766","loc":[-85.5313863,42.0238293]},"n1821014768":{"id":"n1821014768","loc":[-85.593297,41.9833363]},"n1821014769":{"id":"n1821014769","loc":[-85.5849446,41.9957245]},"n1821014770":{"id":"n1821014770","loc":[-85.5537774,42.0130847]},"n1821014771":{"id":"n1821014771","loc":[-85.6111766,41.9706069]},"n1821014772":{"id":"n1821014772","loc":[-85.5585477,42.008989]},"n1821014774":{"id":"n1821014774","loc":[-85.5928142,41.9852623]},"n1821014777":{"id":"n1821014777","loc":[-85.5891933,41.9882608]},"n1821014778":{"id":"n1821014778","loc":[-85.5926909,41.9817532]},"n1821014779":{"id":"n1821014779","loc":[-85.5260272,42.0252201]},"n1821014781":{"id":"n1821014781","loc":[-85.5894615,41.9950468]},"n1821014782":{"id":"n1821014782","loc":[-85.5461063,42.0143242]},"n1821014783":{"id":"n1821014783","loc":[-85.5711527,42.0085886]},"n1821014784":{"id":"n1821014784","loc":[-85.5329379,42.0218624]},"n1821014786":{"id":"n1821014786","loc":[-85.583047,42.0020252]},"n1821014787":{"id":"n1821014787","loc":[-85.5758962,42.0054095]},"n1821014788":{"id":"n1821014788","loc":[-85.5626354,42.0077733]},"n1821014789":{"id":"n1821014789","loc":[-85.6029852,41.9755999]},"n1821014790":{"id":"n1821014790","loc":[-85.5892362,41.9886755]},"n1821014791":{"id":"n1821014791","loc":[-85.5157597,42.0372017]},"n1821014793":{"id":"n1821014793","loc":[-85.6054582,41.9751094]},"n1821014794":{"id":"n1821014794","loc":[-85.5986936,41.9778412]},"n1821014795":{"id":"n1821014795","loc":[-85.5880775,41.98976]},"n1821014796":{"id":"n1821014796","loc":[-85.5858727,41.9963624]},"n1821014798":{"id":"n1821014798","loc":[-85.5792543,42.0035958]},"n1821014799":{"id":"n1821014799","loc":[-85.5921665,41.9838326]},"n1821014801":{"id":"n1821014801","loc":[-85.599214,41.9782599]},"n1821014802":{"id":"n1821014802","loc":[-85.5571905,42.0090967]},"n1821014803":{"id":"n1821014803","loc":[-85.5426891,42.0173612]},"n1821014804":{"id":"n1821014804","loc":[-85.5889626,41.9896404]},"n1821014805":{"id":"n1821014805","loc":[-85.5491264,42.0141648]},"n1821014806":{"id":"n1821014806","loc":[-85.5618897,42.0072631]},"n1821014808":{"id":"n1821014808","loc":[-85.5573501,42.0109802]},"n1821014809":{"id":"n1821014809","loc":[-85.5983463,41.9778031]},"n1821014810":{"id":"n1821014810","loc":[-85.5885173,41.9895128]},"n1821014811":{"id":"n1821014811","loc":[-85.6084998,41.9721143]},"n1821014812":{"id":"n1821014812","loc":[-85.5737598,42.0056389]},"n1821014814":{"id":"n1821014814","loc":[-85.5542173,42.0118132]},"n1821014816":{"id":"n1821014816","loc":[-85.5277868,42.024451]},"n1821014817":{"id":"n1821014817","loc":[-85.5403999,42.0191724]},"n1821014819":{"id":"n1821014819","loc":[-85.5983879,41.9791452]},"n1821014820":{"id":"n1821014820","loc":[-85.5891302,41.9897578]},"n1821014822":{"id":"n1821014822","loc":[-85.5930731,41.9805108]},"n1821014824":{"id":"n1821014824","loc":[-85.515395,42.0378471]},"n1821014825":{"id":"n1821014825","loc":[-85.5352755,42.0205136]},"n1821014826":{"id":"n1821014826","loc":[-85.5502744,42.0133398]},"n1821014828":{"id":"n1821014828","loc":[-85.5701295,42.0088256]},"n1821014830":{"id":"n1821014830","loc":[-85.5888929,41.9953099]},"n1821014832":{"id":"n1821014832","loc":[-85.5880077,41.9901547]},"n1821014833":{"id":"n1821014833","loc":[-85.5451192,42.0157072]},"n1821014834":{"id":"n1821014834","loc":[-85.6096478,41.9711932]},"n1821014835":{"id":"n1821014835","loc":[-85.5806424,42.0026532]},"n1821014836":{"id":"n1821014836","loc":[-85.5911674,41.9868732]},"n1821014838":{"id":"n1821014838","loc":[-85.5930302,41.9836571]},"n1821014839":{"id":"n1821014839","loc":[-85.588925,41.9938148]},"n1821014840":{"id":"n1821014840","loc":[-85.6111874,41.9705311]},"n1821014841":{"id":"n1821014841","loc":[-85.5680843,42.0075842]},"n1821014842":{"id":"n1821014842","loc":[-85.6012793,41.9775062]},"n1821014843":{"id":"n1821014843","loc":[-85.5855562,41.9989777]},"n1821014844":{"id":"n1821014844","loc":[-85.5506137,42.0131662]},"n1821014845":{"id":"n1821014845","loc":[-85.5270049,42.025457]},"n1821014846":{"id":"n1821014846","loc":[-85.5257054,42.025244]},"n1821014847":{"id":"n1821014847","loc":[-85.6011184,41.9771832]},"n1821014848":{"id":"n1821014848","loc":[-85.515534,42.0389234]},"n1821014850":{"id":"n1821014850","loc":[-85.5847032,42.0010347]},"n1821014853":{"id":"n1821014853","loc":[-85.5361499,42.019063]},"n1821014854":{"id":"n1821014854","loc":[-85.5439176,42.0165721]},"n1821014855":{"id":"n1821014855","loc":[-85.5838825,42.0017284]},"n1821014857":{"id":"n1821014857","loc":[-85.5542173,42.0122317]},"n1821014859":{"id":"n1821014859","loc":[-85.5708201,42.0089195]},"n1821014860":{"id":"n1821014860","loc":[-85.5844833,41.9954415]},"n1821014862":{"id":"n1821014862","loc":[-85.5223204,42.0295396]},"n1821014863":{"id":"n1821014863","loc":[-85.5777898,42.0035918]},"n1821014864":{"id":"n1821014864","loc":[-85.591044,41.9898078]},"n1821014865":{"id":"n1821014865","loc":[-85.5973204,41.980182]},"n1821014866":{"id":"n1821014866","loc":[-85.5699578,42.0085825]},"n1821014867":{"id":"n1821014867","loc":[-85.5210598,42.0305278]},"n1821014868":{"id":"n1821014868","loc":[-85.5929108,41.9819008]},"n1821014869":{"id":"n1821014869","loc":[-85.5279799,42.0242995]},"n1821014870":{"id":"n1821014870","loc":[-85.5196114,42.0320539]},"n1821014871":{"id":"n1821014871","loc":[-85.5785449,42.0040883]},"n1821014872":{"id":"n1821014872","loc":[-85.588292,41.9895766]},"n1821014873":{"id":"n1821014873","loc":[-85.5160172,42.0331775]},"n1821014874":{"id":"n1821014874","loc":[-85.5688849,42.0077016]},"n1821014876":{"id":"n1821014876","loc":[-85.5857976,41.9996036]},"n1821014879":{"id":"n1821014879","loc":[-85.5990906,41.9780765]},"n1821014881":{"id":"n1821014881","loc":[-85.5483647,42.0144279]},"n1821014883":{"id":"n1821014883","loc":[-85.5691209,42.0077972]},"n1821014885":{"id":"n1821014885","loc":[-85.6076844,41.9721103]},"n1821014886":{"id":"n1821014886","loc":[-85.6015489,41.9766147]},"n1821014887":{"id":"n1821014887","loc":[-85.574822,42.0052802]},"n1821014888":{"id":"n1821014888","loc":[-85.5880024,41.9899593]},"n1821014890":{"id":"n1821014890","loc":[-85.5909421,41.9893772]},"n1821014892":{"id":"n1821014892","loc":[-85.5497326,42.0138141]},"n1821014893":{"id":"n1821014893","loc":[-85.5167106,42.0357811]},"n1821014895":{"id":"n1821014895","loc":[-85.5844404,41.9952501]},"n1821014896":{"id":"n1821014896","loc":[-85.5362465,42.0192662]},"n1821014898":{"id":"n1821014898","loc":[-85.5906095,41.9889147]},"n1821014899":{"id":"n1821014899","loc":[-85.5590667,42.0089354]},"n1821014900":{"id":"n1821014900","loc":[-85.5921598,41.9844209]},"n1821014902":{"id":"n1821014902","loc":[-85.5778971,42.0039266]},"n1821014903":{"id":"n1821014903","loc":[-85.603012,41.9761981]},"n1821014904":{"id":"n1821014904","loc":[-85.6108977,41.9706787]},"n1821014905":{"id":"n1821014905","loc":[-85.5685738,42.0076139]},"n1821014906":{"id":"n1821014906","loc":[-85.5392787,42.0186304]},"n1821014907":{"id":"n1821014907","loc":[-85.5227885,42.0274972]},"n1821014908":{"id":"n1821014908","loc":[-85.5857547,41.9961431]},"n1821014910":{"id":"n1821014910","loc":[-85.5610354,42.0072812]},"n1821014911":{"id":"n1821014911","loc":[-85.5209632,42.0308705]},"n1821014912":{"id":"n1821014912","loc":[-85.5709757,42.0087959]},"n1821014913":{"id":"n1821014913","loc":[-85.59231,41.9839344]},"n1821014914":{"id":"n1821014914","loc":[-85.5375245,42.0185865]},"n1821014916":{"id":"n1821014916","loc":[-85.5901548,41.9839841]},"n1821014917":{"id":"n1821014917","loc":[-85.5611213,42.0086405]},"n1821014918":{"id":"n1821014918","loc":[-85.5360426,42.0198122]},"n1821014919":{"id":"n1821014919","loc":[-85.5862817,41.9948691]},"n1821014921":{"id":"n1821014921","loc":[-85.5469807,42.0144438]},"n1821014922":{"id":"n1821014922","loc":[-85.5761309,42.0053838]},"n1821014924":{"id":"n1821014924","loc":[-85.516264,42.0332971]},"n1821014925":{"id":"n1821014925","loc":[-85.5277224,42.0246661]},"n1821014926":{"id":"n1821014926","loc":[-85.5980016,41.9798231]},"n1821014928":{"id":"n1821014928","loc":[-85.5924548,41.9806965]},"n1821014930":{"id":"n1821014930","loc":[-85.5899121,41.985023]},"n1821014931":{"id":"n1821014931","loc":[-85.5706015,42.0089492]},"n1821014932":{"id":"n1821014932","loc":[-85.515926,42.033046]},"n1821014933":{"id":"n1821014933","loc":[-85.5982377,41.9796796]},"n1821014936":{"id":"n1821014936","loc":[-85.5475721,42.0145253]},"n1821014938":{"id":"n1821014938","loc":[-85.5895701,41.9902323]},"n1821014939":{"id":"n1821014939","loc":[-85.6030495,41.9759947]},"n1821014942":{"id":"n1821014942","loc":[-85.6094721,41.9724989]},"n1821014944":{"id":"n1821014944","loc":[-85.5921973,41.9811112]},"n1821014945":{"id":"n1821014945","loc":[-85.5223526,42.0291332]},"n1821014946":{"id":"n1821014946","loc":[-85.5965103,41.9808998]},"n1821014948":{"id":"n1821014948","loc":[-85.517766,42.0349227]},"n1821014950":{"id":"n1821014950","loc":[-85.5889894,41.990996]},"n1821014951":{"id":"n1821014951","loc":[-85.5601932,42.0092902]},"n1821014954":{"id":"n1821014954","loc":[-85.6028135,41.9764055]},"n1821014955":{"id":"n1821014955","loc":[-85.5520621,42.0130666]},"n1821014956":{"id":"n1821014956","loc":[-85.593002,41.9839344]},"n1821014957":{"id":"n1821014957","loc":[-85.515926,42.0369666]},"n1821014960":{"id":"n1821014960","loc":[-85.5761255,42.003877]},"n1821014961":{"id":"n1821014961","loc":[-85.5716355,42.007911]},"n1821014962":{"id":"n1821014962","loc":[-85.5575378,42.0109045]},"n1821014963":{"id":"n1821014963","loc":[-85.5735667,42.0068188]},"n1821014964":{"id":"n1821014964","loc":[-85.5915214,41.9865861]},"n1821014965":{"id":"n1821014965","loc":[-85.5866344,41.9923157]},"n1821014967":{"id":"n1821014967","loc":[-85.5283138,42.0242256]},"n1821014968":{"id":"n1821014968","loc":[-85.5177875,42.0355801]},"n1821014969":{"id":"n1821014969","loc":[-85.548071,42.0144934]},"n1821014972":{"id":"n1821014972","loc":[-85.5611159,42.0088557]},"n1821014973":{"id":"n1821014973","loc":[-85.541686,42.0188757]},"n1821014974":{"id":"n1821014974","loc":[-85.5917628,41.9862631]},"n1821014975":{"id":"n1821014975","loc":[-85.5854864,41.9959478]},"n1821014977":{"id":"n1821014977","loc":[-85.609102,41.9722317]},"n1821014980":{"id":"n1821014980","loc":[-85.5761202,42.0042438]},"n1821014982":{"id":"n1821014982","loc":[-85.5465944,42.0143601]},"n1821014983":{"id":"n1821014983","loc":[-85.5173261,42.0342732]},"n1821014984":{"id":"n1821014984","loc":[-85.5897297,41.9888509]},"n1821014985":{"id":"n1821014985","loc":[-85.5856688,41.999181]},"n1821014986":{"id":"n1821014986","loc":[-85.5344011,42.0217251]},"n1821014987":{"id":"n1821014987","loc":[-85.601467,41.9768203]},"n1821014988":{"id":"n1821014988","loc":[-85.5457254,42.0165123]},"n1821014989":{"id":"n1821014989","loc":[-85.6023482,41.9784332]},"n1821014991":{"id":"n1821014991","loc":[-85.5361606,42.01823]},"n1821014992":{"id":"n1821014992","loc":[-85.5178465,42.0351139]},"n1821014995":{"id":"n1821014995","loc":[-85.5634293,42.0078092]},"n1821014996":{"id":"n1821014996","loc":[-85.573497,42.0072015]},"n1821014997":{"id":"n1821014997","loc":[-85.5976328,41.9799725]},"n1821014998":{"id":"n1821014998","loc":[-85.5210651,42.0303166]},"n1821015003":{"id":"n1821015003","loc":[-85.5222131,42.0288064]},"n1821015004":{"id":"n1821015004","loc":[-85.5897941,41.984405]},"n1821015005":{"id":"n1821015005","loc":[-85.5975725,41.9776099]},"n1821015006":{"id":"n1821015006","loc":[-85.5765708,42.0034903]},"n1821015007":{"id":"n1821015007","loc":[-85.5250187,42.026559]},"n1821015009":{"id":"n1821015009","loc":[-85.5426998,42.0166279]},"n1821015010":{"id":"n1821015010","loc":[-85.5957606,41.9806584]},"n1821015011":{"id":"n1821015011","loc":[-85.5262753,42.0252497]},"n1821015012":{"id":"n1821015012","loc":[-85.5266455,42.0253374]},"n1821015014":{"id":"n1821015014","loc":[-85.5515632,42.0130187]},"n1821015015":{"id":"n1821015015","loc":[-85.6024058,41.9765212]},"n1821015017":{"id":"n1821015017","loc":[-85.5175032,42.0357156]},"n1821015018":{"id":"n1821015018","loc":[-85.5302718,42.0236039]},"n1821015019":{"id":"n1821015019","loc":[-85.6024005,41.9782759]},"n1821015020":{"id":"n1821015020","loc":[-85.5907758,41.9890821]},"n1821015021":{"id":"n1821015021","loc":[-85.6019445,41.9777215]},"n1821015022":{"id":"n1821015022","loc":[-85.5942854,41.9800881]},"n1821015024":{"id":"n1821015024","loc":[-85.5325826,42.0222711]},"n1821015029":{"id":"n1821015029","loc":[-85.555093,42.0105316]},"n1821015033":{"id":"n1821015033","loc":[-85.5249704,42.0270372]},"n1821015034":{"id":"n1821015034","loc":[-85.5243965,42.0272205]},"n1821015038":{"id":"n1821015038","loc":[-85.5413426,42.0190749]},"n1821015039":{"id":"n1821015039","loc":[-85.5920431,41.9848175]},"n1821015041":{"id":"n1821015041","loc":[-85.5577685,42.0106015]},"n1821015042":{"id":"n1821015042","loc":[-85.5453606,42.0158866]},"n1821015045":{"id":"n1821015045","loc":[-85.5333228,42.0217889]},"n1821015046":{"id":"n1821015046","loc":[-85.5426891,42.0175924]},"n1821015048":{"id":"n1821015048","loc":[-85.5886836,41.9936474]},"n1821015050":{"id":"n1821015050","loc":[-85.6001152,41.9786467]},"n1821015051":{"id":"n1821015051","loc":[-85.6094064,41.9723655]},"n1821015053":{"id":"n1821015053","loc":[-85.605721,41.9749738]},"n1821015055":{"id":"n1821015055","loc":[-85.6106791,41.9705048]},"n1821015057":{"id":"n1821015057","loc":[-85.5210437,42.0307071]},"n1821015059":{"id":"n1821015059","loc":[-85.5995694,41.9786725]},"n1821015060":{"id":"n1821015060","loc":[-85.5371638,42.0182938]},"n1821015062":{"id":"n1821015062","loc":[-85.6111766,41.9704593]},"n1821015065":{"id":"n1821015065","loc":[-85.577704,42.0034921]},"n1821015067":{"id":"n1821015067","loc":[-85.5570067,42.0093699]},"n1821015068":{"id":"n1821015068","loc":[-85.5920364,41.9845525]},"n1821015069":{"id":"n1821015069","loc":[-85.5252065,42.0253954]},"n1821015072":{"id":"n1821015072","loc":[-85.5664159,42.0088517]},"n1821015073":{"id":"n1821015073","loc":[-85.5880399,41.991905]},"n1821015075":{"id":"n1821015075","loc":[-85.6099871,41.9727861]},"n1821015076":{"id":"n1821015076","loc":[-85.5319603,42.0231478]},"n1821015078":{"id":"n1821015078","loc":[-85.6036088,41.9751112]},"n1821015080":{"id":"n1821015080","loc":[-85.5983128,41.9789179]},"n1821015082":{"id":"n1821015082","loc":[-85.5614069,42.0071395]},"n1821015083":{"id":"n1821015083","loc":[-85.60968,41.9709738]},"n1821015086":{"id":"n1821015086","loc":[-85.5914195,41.9837351]},"n1821015087":{"id":"n1821015087","loc":[-85.5895473,41.9948036]},"n1821015090":{"id":"n1821015090","loc":[-85.5929913,41.9851905]},"n1821015093":{"id":"n1821015093","loc":[-85.5907396,41.9838485]},"n1821015095":{"id":"n1821015095","loc":[-85.5893864,41.9880176]},"n1821015096":{"id":"n1821015096","loc":[-85.5788024,42.0039807]},"n1821015097":{"id":"n1821015097","loc":[-85.5630592,42.0078411]},"n1821015098":{"id":"n1821015098","loc":[-85.5350609,42.0211274]},"n1821015099":{"id":"n1821015099","loc":[-85.5967195,41.9808679]},"n1821015100":{"id":"n1821015100","loc":[-85.5666734,42.0088119]},"n1821015101":{"id":"n1821015101","loc":[-85.564694,42.0077675]},"n1821015103":{"id":"n1821015103","loc":[-85.6066544,41.9726527]},"n1821015104":{"id":"n1821015104","loc":[-85.6011827,41.9769838]},"n1821015105":{"id":"n1821015105","loc":[-85.5972131,41.9776697]},"n1821015106":{"id":"n1821015106","loc":[-85.5880828,41.9903341]},"n1821015107":{"id":"n1821015107","loc":[-85.5510268,42.0130626]},"n1821015108":{"id":"n1821015108","loc":[-85.6102164,41.970543]},"n1821015109":{"id":"n1821015109","loc":[-85.5905344,41.9853899]},"n1821015111":{"id":"n1821015111","loc":[-85.5888821,41.9913429]},"n1821015112":{"id":"n1821015112","loc":[-85.606295,41.9741921]},"n1821015114":{"id":"n1821015114","loc":[-85.5969556,41.9807443]},"n1821015115":{"id":"n1821015115","loc":[-85.5882223,41.9934081]},"n1821015116":{"id":"n1821015116","loc":[-85.6104471,41.9724971]},"n1821015118":{"id":"n1821015118","loc":[-85.5406091,42.0192162]},"n1821015120":{"id":"n1821015120","loc":[-85.589955,41.9888429]},"n1821015121":{"id":"n1821015121","loc":[-85.5598821,42.0092304]},"n1821015122":{"id":"n1821015122","loc":[-85.545598,42.0144097]},"n1821015123":{"id":"n1821015123","loc":[-85.5649528,42.0079965]},"n1821015125":{"id":"n1821015125","loc":[-85.5883993,41.9917814]},"n1821015126":{"id":"n1821015126","loc":[-85.5295785,42.0239967]},"n1821015129":{"id":"n1821015129","loc":[-85.5648723,42.0078809]},"n1821015132":{"id":"n1821015132","loc":[-85.564989,42.0081103]},"n1821015133":{"id":"n1821015133","loc":[-85.5946127,41.9800841]},"n1821015134":{"id":"n1821015134","loc":[-85.583448,42.0019078]},"n1821015135":{"id":"n1821015135","loc":[-85.5905934,41.9871842]},"n1821015137":{"id":"n1821015137","loc":[-85.610608,41.9704752]},"n1821015138":{"id":"n1821015138","loc":[-85.5752257,42.0052939]},"n1821015139":{"id":"n1821015139","loc":[-85.5893864,41.9943491]},"n1821015140":{"id":"n1821015140","loc":[-85.5426247,42.0169866]},"n1821015141":{"id":"n1821015141","loc":[-85.562001,42.0074526]},"n1821015142":{"id":"n1821015142","loc":[-85.5212046,42.0301094]},"n1821015143":{"id":"n1821015143","loc":[-85.602214,41.9784531]},"n1821015144":{"id":"n1821015144","loc":[-85.5858687,41.9948293]},"n1821015145":{"id":"n1821015145","loc":[-85.5608477,42.0074805]},"n1821015146":{"id":"n1821015146","loc":[-85.5651607,42.0083614]},"n1821015147":{"id":"n1821015147","loc":[-85.5288288,42.0242495]},"n1821015149":{"id":"n1821015149","loc":[-85.5450334,42.0146989]},"n1821015151":{"id":"n1821015151","loc":[-85.5578275,42.0092304]},"n1821015154":{"id":"n1821015154","loc":[-85.6056634,41.9724511]},"n1821015155":{"id":"n1821015155","loc":[-85.5902179,41.9852742]},"n1821015156":{"id":"n1821015156","loc":[-85.5156256,42.0387157]},"n1821015157":{"id":"n1821015157","loc":[-85.5734433,42.0059459]},"n1821015158":{"id":"n1821015158","loc":[-85.6050773,41.9731273]},"n1821015160":{"id":"n1821015160","loc":[-85.5223419,42.0275233]},"n1821015163":{"id":"n1821015163","loc":[-85.6053562,41.972525]},"n1821015164":{"id":"n1821015164","loc":[-85.5850412,41.9946082]},"n1821015165":{"id":"n1821015165","loc":[-85.5359031,42.0186326]},"n1821015166":{"id":"n1821015166","loc":[-85.5608745,42.0077635]},"n1821015169":{"id":"n1821015169","loc":[-85.572876,42.0073189]},"n1821015171":{"id":"n1821015171","loc":[-85.5875424,41.9919188]},"n1821015172":{"id":"n1821015172","loc":[-85.5240116,42.0272581]},"n1821015173":{"id":"n1821015173","loc":[-85.5318369,42.0236818]},"n1821015174":{"id":"n1821015174","loc":[-85.566888,42.0086923]},"n1821015175":{"id":"n1821015175","loc":[-85.5931522,41.9850669]},"n1821015176":{"id":"n1821015176","loc":[-85.5604842,42.0093199]},"n1821015177":{"id":"n1821015177","loc":[-85.5868168,41.9927543]},"n1821015178":{"id":"n1821015178","loc":[-85.6052275,41.9732549]},"n1821015179":{"id":"n1821015179","loc":[-85.5910118,41.9900431]},"n1821015182":{"id":"n1821015182","loc":[-85.5610032,42.0082897]},"n1821015183":{"id":"n1821015183","loc":[-85.5425443,42.0179431]},"n1821015184":{"id":"n1821015184","loc":[-85.5843277,42.0014055]},"n1821015186":{"id":"n1821015186","loc":[-85.5733307,42.0063564]},"n1821015188":{"id":"n1821015188","loc":[-85.5277385,42.0248694]},"n1821015189":{"id":"n1821015189","loc":[-85.5558427,42.0108168]},"n1821015190":{"id":"n1821015190","loc":[-85.5650587,42.0082618]},"n1821015191":{"id":"n1821015191","loc":[-85.5660351,42.0088278]},"n1821015192":{"id":"n1821015192","loc":[-85.5849768,41.9980049]},"n1821015194":{"id":"n1821015194","loc":[-85.5359139,42.0188199]},"n1821015195":{"id":"n1821015195","loc":[-85.593238,41.9849194]},"n1821015197":{"id":"n1821015197","loc":[-85.5850841,41.9983239]},"n1821015199":{"id":"n1821015199","loc":[-85.5983396,41.9794283]},"n1821015204":{"id":"n1821015204","loc":[-85.5452801,42.0145355]},"n1821015205":{"id":"n1821015205","loc":[-85.5340685,42.0218407]},"n1821015207":{"id":"n1821015207","loc":[-85.5773272,42.0034186]},"n1821015209":{"id":"n1821015209","loc":[-85.5535212,42.0132419]},"n1821015211":{"id":"n1821015211","loc":[-85.6107703,41.9706045]},"n1821015212":{"id":"n1821015212","loc":[-85.6030066,41.9758193]},"n1821015213":{"id":"n1821015213","loc":[-85.5359943,42.0184213]},"n1821015214":{"id":"n1821015214","loc":[-85.5922993,41.9813305]},"n1821015215":{"id":"n1821015215","loc":[-85.5672689,42.0080465]},"n1821015217":{"id":"n1821015217","loc":[-85.5160494,42.0365682]},"n1821015218":{"id":"n1821015218","loc":[-85.5401142,42.0190351]},"n1821015219":{"id":"n1821015219","loc":[-85.5607632,42.0092282]},"n1821015220":{"id":"n1821015220","loc":[-85.5866197,41.9947894]},"n1821015221":{"id":"n1821015221","loc":[-85.6017889,41.9765132]},"n1821015222":{"id":"n1821015222","loc":[-85.5595978,42.009059]},"n1821015226":{"id":"n1821015226","loc":[-85.5871494,41.9929018]},"n1821015227":{"id":"n1821015227","loc":[-85.5857708,41.9998866]},"n1821015228":{"id":"n1821015228","loc":[-85.5317135,42.0238094]},"n1821015231":{"id":"n1821015231","loc":[-85.5733521,42.0061372]},"n1821015233":{"id":"n1821015233","loc":[-85.5855991,42.0001936]},"n1821015234":{"id":"n1821015234","loc":[-85.5213924,42.029962]},"n1821015235":{"id":"n1821015235","loc":[-85.6052221,41.9726567]},"n1821015236":{"id":"n1821015236","loc":[-85.5763723,42.0035422]},"n1821015237":{"id":"n1821015237","loc":[-85.5858512,41.9966215]},"n1821015238":{"id":"n1821015238","loc":[-85.567061,42.008439]},"n1821015239":{"id":"n1821015239","loc":[-85.5250563,42.0269057]},"n1821015240":{"id":"n1821015240","loc":[-85.5347551,42.0214263]},"n1821015241":{"id":"n1821015241","loc":[-85.6098463,41.9707066]},"n1821015242":{"id":"n1821015242","loc":[-85.5676927,42.0076519]},"n1821015243":{"id":"n1821015243","loc":[-85.516775,42.0322669]},"n1821015244":{"id":"n1821015244","loc":[-85.5762275,42.0036538]},"n1821015245":{"id":"n1821015245","loc":[-85.5583639,42.0090949]},"n1821015246":{"id":"n1821015246","loc":[-85.5554041,42.0106432]},"n1821015247":{"id":"n1821015247","loc":[-85.5973364,41.9776099]},"n1821015248":{"id":"n1821015248","loc":[-85.6098945,41.9717513]},"n1821015249":{"id":"n1821015249","loc":[-85.6045315,41.9751511]},"n1821015250":{"id":"n1821015250","loc":[-85.5579938,42.0092264]},"n1821015253":{"id":"n1821015253","loc":[-85.6058873,41.9724652]},"n1821015254":{"id":"n1821015254","loc":[-85.5869456,41.9947517]},"n1821015255":{"id":"n1821015255","loc":[-85.5936565,41.9823713]},"n1821015256":{"id":"n1821015256","loc":[-85.5218269,42.0278102]},"n1821015258":{"id":"n1821015258","loc":[-85.5887802,41.9905534]},"n1821015259":{"id":"n1821015259","loc":[-85.5901924,41.9904515]},"n1821015263":{"id":"n1821015263","loc":[-85.5249222,42.0255787]},"n1821015265":{"id":"n1821015265","loc":[-85.5175206,42.0321672]},"n1821015266":{"id":"n1821015266","loc":[-85.5275722,42.0254034]},"n1821015267":{"id":"n1821015267","loc":[-85.6016226,41.9765451]},"n1821015269":{"id":"n1821015269","loc":[-85.5569316,42.011032]},"n1821015271":{"id":"n1821015271","loc":[-85.6010714,41.9785209]},"n1821015272":{"id":"n1821015272","loc":[-85.6050666,41.9729917]},"n1821015273":{"id":"n1821015273","loc":[-85.5891235,41.99529]},"n1821015274":{"id":"n1821015274","loc":[-85.515454,42.0376439]},"n1821015276":{"id":"n1821015276","loc":[-85.5776021,42.0034443]},"n1821015277":{"id":"n1821015277","loc":[-85.6041707,41.9751453]},"n1821015278":{"id":"n1821015278","loc":[-85.5444701,42.0167435]},"n1821015280":{"id":"n1821015280","loc":[-85.5923274,41.9852202]},"n1821015283":{"id":"n1821015283","loc":[-85.5893649,41.9900271]},"n1821015284":{"id":"n1821015284","loc":[-85.5933453,41.9804412]},"n1821015285":{"id":"n1821015285","loc":[-85.5247237,42.026017]},"n1821015286":{"id":"n1821015286","loc":[-85.5286182,42.0242477]},"n1821015287":{"id":"n1821015287","loc":[-85.5904003,41.9888549]},"n1821015288":{"id":"n1821015288","loc":[-85.6062146,41.9739369]},"n1821015290":{"id":"n1821015290","loc":[-85.5762596,42.0052602]},"n1821015292":{"id":"n1821015292","loc":[-85.5849715,41.9975465]},"n1821015293":{"id":"n1821015293","loc":[-85.585229,42.0006241]},"n1821015294":{"id":"n1821015294","loc":[-85.5926922,41.9805946]},"n1821015295":{"id":"n1821015295","loc":[-85.5703387,42.0089133]},"n1821015299":{"id":"n1821015299","loc":[-85.5789955,42.0038611]},"n1821015301":{"id":"n1821015301","loc":[-85.6072888,41.9721918]},"n1821015302":{"id":"n1821015302","loc":[-85.5356349,42.0200992]},"n1821015304":{"id":"n1821015304","loc":[-85.5891772,41.994066]},"n1821015306":{"id":"n1821015306","loc":[-85.606295,41.9744952]},"n1821015307":{"id":"n1821015307","loc":[-85.538871,42.0186583]},"n1821015308":{"id":"n1821015308","loc":[-85.587997,41.994971]},"n1821015311":{"id":"n1821015311","loc":[-85.606869,41.9725809]},"n1821015312":{"id":"n1821015312","loc":[-85.5171974,42.0339943]},"n1821015314":{"id":"n1821015314","loc":[-85.5327435,42.0220479]},"n1821015315":{"id":"n1821015315","loc":[-85.5383439,42.0187282]},"n1821015316":{"id":"n1821015316","loc":[-85.5248095,42.0263119]},"n1821015318":{"id":"n1821015318","loc":[-85.5732502,42.0073051]},"n1821015319":{"id":"n1821015319","loc":[-85.5924226,41.9852663]},"n1821015321":{"id":"n1821015321","loc":[-85.5179001,42.0353052]},"n1821015322":{"id":"n1821015322","loc":[-85.5456771,42.0162413]},"n1821015323":{"id":"n1821015323","loc":[-85.5936618,41.9829096]},"n1821015325":{"id":"n1821015325","loc":[-85.5656931,42.0086582]},"n1821015326":{"id":"n1821015326","loc":[-85.5448456,42.0150975]},"n1821015327":{"id":"n1821015327","loc":[-85.5220039,42.027615]},"n1821015329":{"id":"n1821015329","loc":[-85.517884,42.0354885]},"n1821015330":{"id":"n1821015330","loc":[-85.5576666,42.0101671]},"n1821015332":{"id":"n1821015332","loc":[-85.5368754,42.0181402]},"n1821015333":{"id":"n1821015333","loc":[-85.5367078,42.0181145]},"n1821015334":{"id":"n1821015334","loc":[-85.5903909,41.9904316]},"n1821015335":{"id":"n1821015335","loc":[-85.5430767,42.0163587]},"n1821015336":{"id":"n1821015336","loc":[-85.5277492,42.0252878]},"n1821015337":{"id":"n1821015337","loc":[-85.5312146,42.0236898]},"n1821015338":{"id":"n1821015338","loc":[-85.5886568,41.991614]},"n1821015339":{"id":"n1821015339","loc":[-85.5782498,42.0040883]},"n1821015341":{"id":"n1821015341","loc":[-85.562233,42.0076457]},"n1821015342":{"id":"n1821015342","loc":[-85.588626,41.9952479]},"n1821015343":{"id":"n1821015343","loc":[-85.5762865,42.005033]},"n1821015344":{"id":"n1821015344","loc":[-85.5850841,41.9971478]},"n1821015346":{"id":"n1821015346","loc":[-85.5643144,42.0076936]},"n1821015347":{"id":"n1821015347","loc":[-85.5164893,42.0359467]},"n1821015348":{"id":"n1821015348","loc":[-85.5906846,41.9903541]},"n1821015349":{"id":"n1821015349","loc":[-85.557688,42.0107769]},"n1821015350":{"id":"n1821015350","loc":[-85.5363698,42.0181424]},"n1821015351":{"id":"n1821015351","loc":[-85.5939636,41.9801918]},"n1821015352":{"id":"n1821015352","loc":[-85.5524041,42.0131644]},"n1821015354":{"id":"n1821015354","loc":[-85.5308606,42.0236221]},"n1821015355":{"id":"n1821015355","loc":[-85.5877449,41.9932367]},"n1821015356":{"id":"n1821015356","loc":[-85.519885,42.0318586]},"n1821015357":{"id":"n1821015357","loc":[-85.5454035,42.0168431]},"n1821015358":{"id":"n1821015358","loc":[-85.5970629,41.9781881]},"n1821015359":{"id":"n1821015359","loc":[-85.5932541,41.9844767]},"n1821015360":{"id":"n1821015360","loc":[-85.5970736,41.9778252]},"n1821015361":{"id":"n1821015361","loc":[-85.537031,42.0181601]},"n1821015362":{"id":"n1821015362","loc":[-85.5548355,42.0105156]},"n1821015363":{"id":"n1821015363","loc":[-85.5168648,42.0336158]},"n1821015365":{"id":"n1821015365","loc":[-85.5870435,41.9919507]},"n1821015366":{"id":"n1821015366","loc":[-85.5719681,42.0075443]},"n1821015367":{"id":"n1821015367","loc":[-85.5969985,41.9780446]},"n1821015368":{"id":"n1821015368","loc":[-85.5926761,41.98528]},"n1821015369":{"id":"n1821015369","loc":[-85.5224009,42.0293444]},"n1821015371":{"id":"n1821015371","loc":[-85.518737,42.0322651]},"n1821015372":{"id":"n1821015372","loc":[-85.6064573,41.9726465]},"n1821015373":{"id":"n1821015373","loc":[-85.5201103,42.0313088]},"n1821015375":{"id":"n1821015375","loc":[-85.5378182,42.0186844]},"n1821015376":{"id":"n1821015376","loc":[-85.6109741,41.9706882]},"n1821015377":{"id":"n1821015377","loc":[-85.5993333,41.9785488]},"n1821015378":{"id":"n1821015378","loc":[-85.5889787,41.9907368]},"n1821015380":{"id":"n1821015380","loc":[-85.6060161,41.9737375]},"n1821015381":{"id":"n1821015381","loc":[-85.5743016,42.0053679]},"n1821015382":{"id":"n1821015382","loc":[-85.6014724,41.9776099]},"n1821015383":{"id":"n1821015383","loc":[-85.5574426,42.0091644]},"n1821015385":{"id":"n1821015385","loc":[-85.5208613,42.0309302]},"n1821015386":{"id":"n1821015386","loc":[-85.5919023,41.9837789]},"n1821015387":{"id":"n1821015387","loc":[-85.5455484,42.0160221]},"n1821015392":{"id":"n1821015392","loc":[-85.5801757,42.0028964]},"n1821015395":{"id":"n1821015395","loc":[-85.5493785,42.0139974]},"n1821015396":{"id":"n1821015396","loc":[-85.5449475,42.015488]},"n1821015398":{"id":"n1821015398","loc":[-85.611123,41.9706627]},"n1821015400":{"id":"n1821015400","loc":[-85.5935706,41.9822477]},"n1821015401":{"id":"n1821015401","loc":[-85.5724254,42.0073508]},"n1821015403":{"id":"n1821015403","loc":[-85.5486812,42.0143442]},"n1821015404":{"id":"n1821015404","loc":[-85.5161835,42.0327711]},"n1821015406":{"id":"n1821015406","loc":[-85.5921705,41.9851107]},"n1821015407":{"id":"n1821015407","loc":[-85.531912,42.0234069]},"n1821015410":{"id":"n1821015410","loc":[-85.5292566,42.024176]},"n1821015411":{"id":"n1821015411","loc":[-85.5845316,41.9948315]},"n1821015413":{"id":"n1821015413","loc":[-85.5217947,42.0280413]},"n1821015414":{"id":"n1821015414","loc":[-85.5527367,42.013272]},"n1821015415":{"id":"n1821015415","loc":[-85.5191179,42.0321973]},"n1821015416":{"id":"n1821015416","loc":[-85.5540241,42.0128655]},"n1821015418":{"id":"n1821015418","loc":[-85.5272892,42.0254849]},"n1821015419":{"id":"n1821015419","loc":[-85.5449744,42.016867]},"n1821015420":{"id":"n1821015420","loc":[-85.5852665,41.9986787]},"n1821015421":{"id":"n1821015421","loc":[-85.6102701,41.972186]},"n1821015423":{"id":"n1821015423","loc":[-85.6026365,41.9764972]},"n1821015427":{"id":"n1821015427","loc":[-85.5898692,41.9841498]},"n1821015429":{"id":"n1821015429","loc":[-85.5422546,42.0183855]},"n1821015430":{"id":"n1821015430","loc":[-85.5866505,41.9925549]},"n1821015431":{"id":"n1821015431","loc":[-85.5234376,42.0273577]},"n1821015432":{"id":"n1821015432","loc":[-85.6096746,41.9727284]},"n1821015433":{"id":"n1821015433","loc":[-85.5824891,42.0021567]},"n1821015434":{"id":"n1821015434","loc":[-85.5923905,41.9841139]},"n1821015435":{"id":"n1821015435","loc":[-85.5874565,41.9948014]},"n1821015437":{"id":"n1821015437","loc":[-85.6055279,41.9734423]},"n1821015438":{"id":"n1821015438","loc":[-85.5299379,42.0237376]},"n1821015439":{"id":"n1821015439","loc":[-85.5155022,42.0383651]},"n1821015442":{"id":"n1821015442","loc":[-85.527422,42.0254711]},"n1821015443":{"id":"n1821015443","loc":[-85.5920699,41.9849291]},"n1821015444":{"id":"n1821015444","loc":[-85.5639711,42.0077494]},"n1821015445":{"id":"n1821015445","loc":[-85.5162586,42.0361777]},"n1821015446":{"id":"n1821015446","loc":[-85.5220039,42.029695]},"n1821015448":{"id":"n1821015448","loc":[-85.5176641,42.0356956]},"n1821015449":{"id":"n1821015449","loc":[-85.5930556,41.9841577]},"n1821015451":{"id":"n1821015451","loc":[-85.5320783,42.0228848]},"n1821015452":{"id":"n1821015452","loc":[-85.5170096,42.0357235]},"n1821015453":{"id":"n1821015453","loc":[-85.5571355,42.009613]},"n1821015454":{"id":"n1821015454","loc":[-85.5609979,42.009059]},"n1821015455":{"id":"n1821015455","loc":[-85.6097336,41.9708342]},"n1821015456":{"id":"n1821015456","loc":[-85.5884476,41.9904218]},"w170843846":{"id":"w170843846","tags":{"waterway":"river"},"nodes":["n1819790555","n1819790762","n1819790547","n1819790618","n1819790898","n1819790796","n1819790534","n1819790543","n1819790541","n1819790687","n1819790877","n1819790811","n1819790670"]},"w209083541":{"id":"w209083541","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1821014848","n1821015156","n1821015439","n1821014763","n1821014824","n1821015274","n1821014764","n1821014791","n1821014957","n1821015217","n1821015445","n1821015347","n1821014893","n1821015452","n1821015017","n1821015448","n1821014968","n1821015329","n1821015321","n1821014992","n1821014948","n1821014757","n1821014983","n1821015312","n1821015363","n1821014924","n1821014873","n1821014932","n1821014668","n1821015404","n1821014716","n1821015243","n1821015265","n1821014710","n1821015371","n1821015415","n1821014870","n1821015356","n1821015373","n1821014681","n1821014714","n1821015385","n1821014911","n1821015057","n1821014867","n1821014998","n1821015142","n1821015234","n1821015446","n1821014862","n1821015369","n1821014945","n1821015003","n1821014667","n1821015413","n1821015256","n1821015327","n1821015160","n1821014907","n1821015431","n1821015172","n1821015034","n1821014741","n1821015033","n1821015239","n1821015007","n1821015316","n1821015285","n1821014633","n1821015263","n1821015069","n1821014846","n1821014779","n1821015011","n1821015012","n1821014845","n1821015418","n1821015442","n1821015266","n1821015336","n1821014755","n1821015188","n1821014925","n1821014816","n1821014869","n1821014967","n1821015286","n1821015147","n1821015410","n1821015126","n1821015438","n1821015018","n1821015354","n1821015337","n1821014766","n1821014740","n1821015228","n1821015173","n1821015407","n1821015076","n1821015451","n1821015024","n1821015314","n1821014784","n1821015045","n1821014735","n1821015205","n1821014986","n1821015240","n1821015098","n1821014825","n1821015302","n1821014918","n1821014718","n1821014896","n1821014853","n1821015194","n1821015165","n1821015213","n1821014991","n1821015350","n1821015333","n1821015332","n1821015361","n1821015060","n1821014914","n1821015375","n1821015315","n1821015307","n1821014906","n1821014751","n1821014666","n1821015218","n1821014817","n1821015118","n1821014674","n1821015038","n1821014973","n1821015429","n1821015183","n1821015046","n1821014803","n1821015140","n1821015009","n1821014749","n1821015335","n1821014711","n1821014854","n1821015278","n1821015419","n1821014648","n1821015357","n1821014637","n1821014988","n1821015322","n1821015387","n1821015042","n1821014833","n1821015396","n1821015326","n1821014691","n1821015149","n1821015204","n1821015122","n1821014782","n1821014982","n1821014921","n1821014936","n1821014969","n1821014881","n1821015403","n1821014805","n1821015395","n1821014892","n1821014826","n1821014844","n1821015107","n1821015014","n1821014955","n1821015352","n1821015414","n1821014746","n1821015209","n1821014770","n1821015416","n1821014661","n1821014857","n1821014814","n1821014754","n1821014721","n1821014727","n1821015362","n1821015029","n1821015246","n1821015189","n1821014627","n1821015269","n1821014808","n1821014962","n1821015349","n1821015041","n1821014722","n1821015330","n1821015453","n1821015067","n1821014643","n1821014802","n1821015383","n1821015151","n1821015250","n1821015245","n1821014772","n1821014899","n1821015222","n1821015121","n1821014951","n1821015176","n1821015219","n1821015454","n1821014972","n1821014917","n1821015182","n1821015166","n1821015145","n1821014910","n1821015082","n1821014678","n1821014806","n1821015141","n1821015341","n1821014788","n1821015097","n1821014995","n1821015444","n1821015346","n1821015101","n1821015129","n1821015123","n1821015132","n1821015190","n1821015146","n1821015325","n1821015191","n1821015072","n1821015100","n1821015174","n1821015238","n1821015215","n1821014700","n1821015242","n1821014841","n1821014905","n1821014874","n1821014883","n1821014729","n1821014866","n1821014828","n1821015295","n1821014931","n1821014859","n1821014912","n1821014783","n1821014752","n1821014961","n1821015366","n1821015401","n1821015169","n1821015318","n1821014996","n1821014747","n1821014963","n1821014670","n1821015186","n1821015231","n1821015157","n1821014812","n1821015381","n1821014887","n1821015138","n1821014704","n1821014787","n1821014922","n1821015290","n1821015343","n1821014651","n1821014980","n1821014960","n1821015244","n1821015236","n1821015006","n1821014694","n1821014759","n1821015207","n1821015276","n1821015065","n1821014863","n1821014660","n1821014902","n1821014645","n1821015339","n1821014871","n1821015096","n1821015299","n1821014798","n1821014638","n1821015392","n1821014835","n1821014762","n1821014642","n1821015433","n1821014786","n1821015134","n1821014855","n1821015184","n1821014850","n1821015293","n1821015233","n1821015227","n1821014876","n1821014985","n1821014843","n1821015420","n1821015197","n1821015192","n1821015292","n1821015344","n1821014742","n1821014726","n1821015237","n1821014796","n1821014908","n1821014975","n1821014769","n1821014688","n1821014860","n1821014895","n1821014676","n1821015411","n1821014736","n1821015164","n1821014647","n1821015144","n1821014919","n1821015220","n1821015254","n1821015435","n1821015308","n1821015342","n1821014830","n1821015273","n1821014658","n1821014781","n1821015087","n1821015139","n1821015304","n1821014839","n1821015048","n1821015115","n1821015355","n1821015226","n1821015177","n1821015430","n1821014965","n1821014725","n1821015365","n1821015171","n1821015073","n1821015125","n1821015338","n1821015111","n1821014950","n1821015378","n1821015258","n1821015456","n1821015106","n1821014832","n1821014888","n1821014795","n1821014872","n1821014810","n1821014705","n1821014804","n1821014820","n1821015283","n1821014938","n1821014689","n1821015259","n1821015334","n1821015348","n1821014635","n1821015179","n1821014864","n1821014890","n1821015020","n1821014898","n1821015287","n1821015120","n1821014984","n1821014743","n1821014790","n1821014765","n1821014777","n1821015095","n1821014653","n1821015135","n1821014836","n1821014964","n1821014974","n1821014636","n1821014682","n1821014663","n1821014665","n1821015109","n1821015155","n1821014930","n1821014669","n1821015004","n1821015427","n1821014916","n1821015093","n1821015086","n1821015386","n1821014799","n1821014913","n1821015434","n1821014728","n1821014900","n1821015068","n1821015039","n1821015443","n1821015406","n1821015280","n1821015319","n1821015368","n1821014774","n1821015090","n1821015175","n1821015195","n1821014687","n1821015359","n1821015449","n1821014956","n1821014838","n1821014768","n1821014698","n1821015323","n1821014756","n1821015255","n1821015400","n1821014717","n1821014868","n1821014778","n1821015214","n1821014944","n1821014697","n1821014671","n1821014928","n1821015294","n1821014822","n1821015284","n1821015351","n1821015022","n1821015133","n1821014644","n1821015010","n1821014625","n1821014657","n1821014946","n1821015099","n1821015114","n1821014629","n1821014865","n1821014997","n1821014926","n1821014933","n1821015199","n1821014819","n1821015080","n1821014692","n1821014677","n1821015358","n1821015367","n1821015360","n1821015105","n1821015247","n1821015005","n1821014809","n1821014794","n1821014761","n1821014879","n1821014801","n1821015377","n1821015059","n1821014730","n1821015050","n1821015271","n1821015143","n1821014989","n1821015019","n1821014672","n1821014649","n1821014684","n1821014703","n1821015021","n1821015382","n1821014842","n1821014720","n1821014847","n1821015104","n1821014987","n1821014886","n1821015267","n1821015221","n1821015015","n1821015423","n1821014954","n1821014903","n1821014939","n1821015212","n1821014789","n1821014712","n1821014708","n1821015078","n1821015277","n1821015249","n1821014646","n1821014793","n1821015053","n1821014707","n1821015306","n1821015112","n1821015288","n1821015380","n1821015437","n1821015178","n1821015158","n1821015272","n1821015235","n1821015163","n1821015154","n1821015253","n1821014632","n1821015372","n1821015103","n1821015311","n1821015301","n1821014885","n1821014811","n1821014977","n1821015051","n1821014942","n1821014745","n1821015432","n1821015075","n1821014664","n1821014695","n1821015116","n1821014639","n1821015421","n1821015248","n1821014758","n1821014834","n1821015083","n1821015455","n1821015241","n1821015108","n1821014713","n1821015137","n1821015055","n1821015211","n1821014904","n1821015376","n1821015398","n1821014771","n1821014840","n1821015062","n1819790554","n1819790560","n1819790767","n1819790696","n1819790706","n1819790606","n1819790607","n1819790544","n1819790779","n1819790760","n1819790926","n1819790927","n1819790647","n1819790657","n1819790649","n1819790679","n1819790915","n1819790739","n1819790549","n1819790671","n1819790686","n1819790798","n1819790791","n1819790563","n1819790720","n1819790704","n1819790795","n1819790836","n1819790622","n1819790615","n1819790654","n1819790931","n1819790595","n1819790753","n1819790612","n1819790623","n1819790564","n1819790552","n1819790645","n1819790625","n1819790605","n1819790668","n1819790731","n1819790718","n1819790781","n1819790665","n1819790659","n1819790726","n1819790642","n1819790854","n1819790697","n1819790867","n1819790833","n1819790555","n1819790774","n1819790881","n1819790530","n1819790909","n1819790891","n1819790590","n1819790738","n1819790609","n1819790528","n1819790674","n1819790583","n1819790559","n1819790863","n1819790912","n1819790685","n1819790913"]},"n185955128":{"id":"n185955128","loc":[-85.6189367,41.9519432]},"n185948818":{"id":"n185948818","loc":[-85.616755,41.952231]},"n185978819":{"id":"n185978819","loc":[-85.616773,41.954737]},"n185978821":{"id":"n185978821","loc":[-85.616699,41.954742]},"n2138420714":{"id":"n2138420714","loc":[-85.6176304,41.9515154]},"n2138420715":{"id":"n2138420715","loc":[-85.6177355,41.9515717]},"n2138420716":{"id":"n2138420716","loc":[-85.6192901,41.951573]},"n2138420718":{"id":"n2138420718","loc":[-85.6171481,41.9513579]},"n2138420719":{"id":"n2138420719","loc":[-85.6165981,41.9519199]},"n2138420720":{"id":"n2138420720","loc":[-85.6165719,41.9519922]},"n2138420721":{"id":"n2138420721","loc":[-85.6165832,41.9520757]},"n2138420722":{"id":"n2138420722","loc":[-85.6166355,41.9521453]},"n2138420723":{"id":"n2138420723","loc":[-85.6169161,41.9522788]},"n2138420724":{"id":"n2138420724","loc":[-85.6170882,41.9522538]},"n2138420725":{"id":"n2138420725","loc":[-85.6189204,41.9514674]},"n2138420726":{"id":"n2138420726","loc":[-85.6180346,41.9514735]},"n2138420727":{"id":"n2138420727","loc":[-85.6180362,41.9515719]},"n2138420728":{"id":"n2138420728","loc":[-85.6189204,41.9515727]},"n2138420744":{"id":"n2138420744","loc":[-85.618919,41.9519571]},"n2138420745":{"id":"n2138420745","loc":[-85.6194575,41.9522374]},"n2138420746":{"id":"n2138420746","loc":[-85.6181777,41.9536179]},"n2138420747":{"id":"n2138420747","loc":[-85.6176582,41.9533658]},"n2138420748":{"id":"n2138420748","loc":[-85.6179871,41.9530242]},"n2138420749":{"id":"n2138420749","loc":[-85.618429,41.9532476]},"n2138420750":{"id":"n2138420750","loc":[-85.6185538,41.9531194]},"n2138420751":{"id":"n2138420751","loc":[-85.6180765,41.9528677]},"n2138420752":{"id":"n2138420752","loc":[-85.6180394,41.9528855]},"n2138420753":{"id":"n2138420753","loc":[-85.6193752,41.9521695]},"n2138420754":{"id":"n2138420754","loc":[-85.6181374,41.9535376]},"n2138420755":{"id":"n2138420755","loc":[-85.6179898,41.9535545]},"n2138420756":{"id":"n2138420756","loc":[-85.6177286,41.9534228]},"n2138420757":{"id":"n2138420757","loc":[-85.6181011,41.9530292]},"n2138420759":{"id":"n2138420759","loc":[-85.6185158,41.9531194]},"n2138420760":{"id":"n2138420760","loc":[-85.6191318,41.9520425]},"n2138420761":{"id":"n2138420761","loc":[-85.6182348,41.9529815]},"n2138420762":{"id":"n2138420762","loc":[-85.6184853,41.9524248]},"n2138420763":{"id":"n2138420763","loc":[-85.6186764,41.9525193]},"n2138420764":{"id":"n2138420764","loc":[-85.6189421,41.9526483]},"n2138420765":{"id":"n2138420765","loc":[-85.6182875,41.9531222]},"n2138420766":{"id":"n2138420766","loc":[-85.6179141,41.9535163]},"n2138420767":{"id":"n2138420767","loc":[-85.6178363,41.9535735]},"n185948824":{"id":"n185948824","loc":[-85.6165667,41.9529715]},"n2138420758":{"id":"n2138420758","loc":[-85.6184408,41.953201]},"n2138422349":{"id":"n2138422349","loc":[-85.6175136,41.9533346]},"n2138422350":{"id":"n2138422350","loc":[-85.6171867,41.9531679]},"n2138422351":{"id":"n2138422351","loc":[-85.61722,41.9531305]},"n2138422352":{"id":"n2138422352","loc":[-85.6171889,41.9531158]},"n2138422353":{"id":"n2138422353","loc":[-85.6171733,41.9531284]},"n2138422354":{"id":"n2138422354","loc":[-85.616765,41.9529207]},"n2138422355":{"id":"n2138422355","loc":[-85.6167565,41.9529355]},"n2138422356":{"id":"n2138422356","loc":[-85.6164772,41.9527911]},"n2138422357":{"id":"n2138422357","loc":[-85.6168227,41.9524261]},"n2138422358":{"id":"n2138422358","loc":[-85.6171913,41.9526158]},"n2138422359":{"id":"n2138422359","loc":[-85.6172403,41.9525589]},"n2138422360":{"id":"n2138422360","loc":[-85.6172097,41.952542]},"n2138422361":{"id":"n2138422361","loc":[-85.6173948,41.9523512]},"n2138422362":{"id":"n2138422362","loc":[-85.6174256,41.9523678]},"n2138422363":{"id":"n2138422363","loc":[-85.6174831,41.9523086]},"n2138422364":{"id":"n2138422364","loc":[-85.6173316,41.9522289]},"n2138422365":{"id":"n2138422365","loc":[-85.6174507,41.9521024]},"n2138422366":{"id":"n2138422366","loc":[-85.6174773,41.9521155]},"n2138422367":{"id":"n2138422367","loc":[-85.6176577,41.9519232]},"n2138422368":{"id":"n2138422368","loc":[-85.6176336,41.9519105]},"n2138422369":{"id":"n2138422369","loc":[-85.617747,41.9517861]},"n2138422370":{"id":"n2138422370","loc":[-85.6182675,41.9520559]},"n2138422371":{"id":"n2138422371","loc":[-85.6182105,41.9521219]},"n2138422372":{"id":"n2138422372","loc":[-85.6183863,41.9522203]},"n2138422373":{"id":"n2138422373","loc":[-85.6180984,41.9525266]},"n2138422374":{"id":"n2138422374","loc":[-85.6179159,41.9524295]},"n2138422375":{"id":"n2138422375","loc":[-85.617854,41.9524979]},"n2138422376":{"id":"n2138422376","loc":[-85.6177686,41.9524531]},"n2138422377":{"id":"n2138422377","loc":[-85.6174716,41.9527765]},"n2138422378":{"id":"n2138422378","loc":[-85.6178545,41.9529756]},"n2138425424":{"id":"n2138425424","loc":[-85.6171736,41.9536385]},"n2138425425":{"id":"n2138425425","loc":[-85.6180159,41.9535782]},"n2138425426":{"id":"n2138425426","loc":[-85.6181068,41.9536282]},"n2138425427":{"id":"n2138425427","loc":[-85.6180673,41.9542678]},"n2138425428":{"id":"n2138425428","loc":[-85.6178636,41.9542634]},"n2138425429":{"id":"n2138425429","loc":[-85.6176204,41.9542046]},"n2138425430":{"id":"n2138425430","loc":[-85.6174366,41.9541031]},"n2138425431":{"id":"n2138425431","loc":[-85.6172942,41.9539781]},"n2138425432":{"id":"n2138425432","loc":[-85.6172171,41.9538399]},"n2138425433":{"id":"n2138425433","loc":[-85.6168138,41.9543266]},"n2138425434":{"id":"n2138425434","loc":[-85.6167779,41.9538098]},"n2138425435":{"id":"n2138425435","loc":[-85.6165849,41.9537073]},"n2138425441":{"id":"n2138425441","loc":[-85.616458,41.9543184]},"n2138425442":{"id":"n2138425442","loc":[-85.6166428,41.954345]},"n2138425445":{"id":"n2138425445","loc":[-85.6181332,41.9514117]},"n2138425446":{"id":"n2138425446","loc":[-85.6183263,41.9514111]},"n2138425447":{"id":"n2138425447","loc":[-85.6185033,41.9514102]},"n2138425449":{"id":"n2138425449","loc":[-85.6186809,41.9514093]},"n2138425451":{"id":"n2138425451","loc":[-85.6188681,41.9514082]},"n2138436008":{"id":"n2138436008","loc":[-85.6170474,41.9513604]},"n2138436009":{"id":"n2138436009","loc":[-85.6164937,41.9519586]},"n2138436010":{"id":"n2138436010","loc":[-85.616497,41.9520725]},"n2138436011":{"id":"n2138436011","loc":[-85.6165654,41.9521645]},"n2138436012":{"id":"n2138436012","loc":[-85.6166631,41.9522178]},"n2138436013":{"id":"n2138436013","loc":[-85.6167327,41.9522554]},"n2138436014":{"id":"n2138436014","loc":[-85.6172383,41.9525125]},"n2138439319":{"id":"n2138439319","loc":[-85.6170432,41.9524057]},"n2138439320":{"id":"n2138439320","loc":[-85.617691,41.9517107]},"n2138439321":{"id":"n2138439321","loc":[-85.6177727,41.9516794]},"n2138439322":{"id":"n2138439322","loc":[-85.619085,41.9516811]},"n2138439323":{"id":"n2138439323","loc":[-85.6179432,41.952895]},"n2138439324":{"id":"n2138439324","loc":[-85.6180389,41.9529384]},"n2138439325":{"id":"n2138439325","loc":[-85.6176303,41.9533604]},"n2138439326":{"id":"n2138439326","loc":[-85.6175538,41.9534396]},"n2138439327":{"id":"n2138439327","loc":[-85.6173806,41.9523658]},"n2138439328":{"id":"n2138439328","loc":[-85.6171841,41.9522542]},"n2138439329":{"id":"n2138439329","loc":[-85.6172077,41.9524958]},"n2138439330":{"id":"n2138439330","loc":[-85.6171235,41.9525809]},"n2138439331":{"id":"n2138439331","loc":[-85.6180938,41.9527349]},"n2138439332":{"id":"n2138439332","loc":[-85.6177023,41.9525253]},"n2138439333":{"id":"n2138439333","loc":[-85.6175543,41.9526865]},"n2138439334":{"id":"n2138439334","loc":[-85.6179589,41.9528783]},"n185948820":{"id":"n185948820","loc":[-85.6163249,41.952701]},"n185948822":{"id":"n185948822","loc":[-85.6163757,41.952855]},"n185955123":{"id":"n185955123","loc":[-85.6198103,41.9510408]},"n185958839":{"id":"n185958839","loc":[-85.611651,41.954761]},"n185965033":{"id":"n185965033","loc":[-85.614195,41.954754]},"n185976502":{"id":"n185976502","loc":[-85.617375,41.947559]},"n185976504":{"id":"n185976504","loc":[-85.6174164,41.9510804]},"n185978828":{"id":"n185978828","loc":[-85.613542,41.954756]},"n185978830":{"id":"n185978830","loc":[-85.610373,41.954774]},"n2138420713":{"id":"n2138420713","loc":[-85.6174641,41.9506942]},"n2138420717":{"id":"n2138420717","loc":[-85.6173027,41.9512895]},"n2138420768":{"id":"n2138420768","loc":[-85.61745,41.9501974]},"n2138420773":{"id":"n2138420773","loc":[-85.6174135,41.9489136]},"n2138425436":{"id":"n2138425436","loc":[-85.6159148,41.9538036]},"n2138425437":{"id":"n2138425437","loc":[-85.6159534,41.9539677]},"n2138425438":{"id":"n2138425438","loc":[-85.6160306,41.9540846]},"n2138425439":{"id":"n2138425439","loc":[-85.6161354,41.954181]},"n2138425440":{"id":"n2138425440","loc":[-85.6162733,41.954263]},"n2138425443":{"id":"n2138425443","loc":[-85.6183273,41.9510826]},"n2138425444":{"id":"n2138425444","loc":[-85.6181354,41.9510835]},"n2138425448":{"id":"n2138425448","loc":[-85.6185033,41.9510816]},"n2138425450":{"id":"n2138425450","loc":[-85.6186816,41.9510808]},"n2138425452":{"id":"n2138425452","loc":[-85.6188641,41.9510818]},"n2138435984":{"id":"n2138435984","loc":[-85.6167607,41.9501009]},"n2138436000":{"id":"n2138436000","loc":[-85.6173169,41.947558]},"n2138436001":{"id":"n2138436001","loc":[-85.6173362,41.948883]},"n2138436002":{"id":"n2138436002","loc":[-85.6167791,41.9492952]},"n2138436003":{"id":"n2138436003","loc":[-85.6167543,41.949349]},"n2138436004":{"id":"n2138436004","loc":[-85.6167648,41.9509125]},"n2138436005":{"id":"n2138436005","loc":[-85.6168832,41.9510412]},"n2138436006":{"id":"n2138436006","loc":[-85.6170045,41.9511417]},"n2138436007":{"id":"n2138436007","loc":[-85.6170624,41.9512483]},"n2138436017":{"id":"n2138436017","loc":[-85.6168094,41.9492729]},"n2138436021":{"id":"n2138436021","loc":[-85.6167553,41.9494886]},"n2138436023":{"id":"n2138436023","loc":[-85.6167585,41.9499707]},"n2138436025":{"id":"n2138436025","loc":[-85.6167567,41.9497018]},"w203838284":{"id":"w203838284","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425424","n2138425425","n2138425426","n2138425427","n2138425428","n2138425429","n2138425430","n2138425431","n2138425432","n2138425424"]},"w203837928":{"id":"w203837928","tags":{"highway":"service"},"nodes":["n2138420717","n2138420718","n2138420719","n2138420720","n2138420721","n2138420722","n185948818","n2138420723","n2138420724","n2138420715"]},"w203839364":{"id":"w203839364","tags":{"highway":"footway"},"nodes":["n2138439331","n2138439332"]},"w203837932":{"id":"w203837932","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420744","n2138420745","n2138420746","n2138420747","n2138420748","n2138420749","n2138420750","n2138420751","n2138420744"]},"w203839362":{"id":"w203839362","tags":{"highway":"footway"},"nodes":["n2138439327","n2138439328"]},"w203839363":{"id":"w203839363","tags":{"highway":"footway"},"nodes":["n2138439329","n2138439330"]},"w203837933":{"id":"w203837933","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n185955128","n2138420760","n2138420753","n2138420764","n2138420759","n2138420758","n2138420754","n2138420755","n2138420766","n2138420756"]},"w203837936":{"id":"w203837936","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420765","n2138420766"]},"w17966364":{"id":"w17966364","tags":{"access":"private","highway":"service","name":"Collins Dr"},"nodes":["n185961362","n185976502","n2138420773","n2138420768","n2138420713","n185976504","n2138420717","n2138420714","n2138420715","n2138420727","n2138420728","n2138420716"]},"w203838040":{"id":"w203838040","tags":{"amenity":"school","area":"yes","building":"yes","name":"Three Rivers Middle School"},"nodes":["n2138422349","n2138422350","n2138422351","n2138422352","n2138422353","n2138422354","n2138422355","n2138422356","n2138422357","n2138439330","n2138422358","n2138422359","n2138422360","n2138436014","n2138439327","n2138422361","n2138422362","n2138422363","n2138422364","n2138422365","n2138422366","n2138422367","n2138422368","n2138422369","n2138422370","n2138422371","n2138422372","n2138422373","n2138422374","n2138422375","n2138422376","n2138439332","n2138439333","n2138422377","n2138422378","n2138422349"]},"w17964049":{"id":"w17964049","tags":{"highway":"service"},"nodes":["n185955120","n185955123","n2138420716","n185955128","n2138420762","n2138420752","n2138420761","n2138420759"]},"w41074899":{"id":"w41074899","tags":{"highway":"secondary","name":"E Hoffman St","ref":"M 60"},"nodes":["n185978817","n185978819","n185978821","n185965033","n185978828","n185958839","n185978830"]},"w203839365":{"id":"w203839365","tags":{"highway":"footway"},"nodes":["n2138439333","n2138439334"]},"w203837935":{"id":"w203837935","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420762","n2138420763","n2138420764"]},"w203838287":{"id":"w203838287","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425446","n2138425447","n2138425448","n2138425443","n2138425446"]},"w203837934":{"id":"w203837934","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420760","n2138420763","n2138420761"]},"w203838289":{"id":"w203838289","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425449","n2138425451","n2138425452","n2138425450","n2138425449"]},"w17963047":{"id":"w17963047","tags":{"highway":"service"},"nodes":["n185948818","n2138436013","n185948820","n185948822","n185948824","n2138439326","n2138420767","n2138420766"]},"w203839091":{"id":"w203839091","tags":{"highway":"footway"},"nodes":["n185976502","n2138436000","n2138436001","n2138436017","n2138436002","n2138436003","n2138436021","n2138436025","n2138436023","n2138435984","n2138436004","n2138436005","n2138436006","n2138436007","n2138436008","n2138436009","n2138436010","n2138436011","n2138436012","n2138436013","n2138439319","n2138439329","n2138436014"]},"w204830797":{"id":"w204830797","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420756","n2138420757","n2138420765","n2138420758"]},"w203838288":{"id":"w203838288","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425447","n2138425449","n2138425450","n2138425448","n2138425447"]},"w203838285":{"id":"w203838285","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425433","n2138425434","n2138425435","n2138425436","n2138425437","n2138425438","n2138425439","n2138425440","n2138425441","n2138425442","n2138425433"]},"w203838286":{"id":"w203838286","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425443","n2138425444","n2138425445","n2138425446","n2138425443"]},"w203837929":{"id":"w203837929","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420725","n2138420726","n2138420727","n2138420728","n2138420725"]},"w203839361":{"id":"w203839361","tags":{"highway":"footway"},"nodes":["n2138439319","n2138439328","n2138439320","n2138439321","n2138439322","n2138439331","n2138439334","n2138439323","n2138439324","n2138439325","n2138439326"]},"n394381698":{"id":"n394381698","loc":[-85.614471,41.954755]},"n394381699":{"id":"n394381699","loc":[-85.6152,41.954744]},"n394381700":{"id":"n394381700","loc":[-85.615201,41.954081]},"n394381701":{"id":"n394381701","loc":[-85.614426,41.954042]},"n394381702":{"id":"n394381702","loc":[-85.616319,41.954749]},"n394381704":{"id":"n394381704","loc":[-85.616152,41.954752]},"n394381706":{"id":"n394381706","loc":[-85.615201,41.95483]},"n394490775":{"id":"n394490775","loc":[-85.613971,41.954839]},"n394490782":{"id":"n394490782","loc":[-85.614372,41.954841]},"n185958835":{"id":"n185958835","loc":[-85.611615,41.953704]},"n185958837":{"id":"n185958837","loc":[-85.611636,41.953938]},"n185958842":{"id":"n185958842","loc":[-85.611187,41.951686]},"n185958844":{"id":"n185958844","loc":[-85.611087,41.951741]},"n185958845":{"id":"n185958845","loc":[-85.611034,41.951852]},"n185958847":{"id":"n185958847","loc":[-85.611016,41.95196]},"n185958849":{"id":"n185958849","loc":[-85.610989,41.95328]},"n185958851":{"id":"n185958851","loc":[-85.611021,41.953484]},"n185958852":{"id":"n185958852","loc":[-85.611091,41.953603]},"n185958853":{"id":"n185958853","loc":[-85.6112,41.953661]},"n185958855":{"id":"n185958855","loc":[-85.611364,41.953686]},"n185965031":{"id":"n185965031","loc":[-85.614204,41.953696]},"n185965032":{"id":"n185965032","loc":[-85.6142,41.953978]},"n185965062":{"id":"n185965062","loc":[-85.614617,41.951639]},"n185965064":{"id":"n185965064","loc":[-85.61463,41.951852]},"n185965066":{"id":"n185965066","loc":[-85.614642,41.953436]},"n185965068":{"id":"n185965068","loc":[-85.6146,41.953551]},"n185965071":{"id":"n185965071","loc":[-85.614487,41.95363]},"n185965073":{"id":"n185965073","loc":[-85.614354,41.953672]},"n185966288":{"id":"n185966288","loc":[-85.61179,41.953695]},"n185966290":{"id":"n185966290","loc":[-85.612232,41.953685]},"n185966293":{"id":"n185966293","loc":[-85.613438,41.953677]},"n185966349":{"id":"n185966349","loc":[-85.611323,41.951653]},"n185966351":{"id":"n185966351","loc":[-85.611892,41.951642]},"n185966352":{"id":"n185966352","loc":[-85.612216,41.951641]},"n185966353":{"id":"n185966353","loc":[-85.613111,41.951639]},"n185966354":{"id":"n185966354","loc":[-85.613396,41.95164]},"n185966355":{"id":"n185966355","loc":[-85.614221,41.95164]},"n185973839":{"id":"n185973839","loc":[-85.61341,41.951919]},"n185973840":{"id":"n185973840","loc":[-85.613438,41.953308]},"n185980222":{"id":"n185980222","loc":[-85.613781,41.955164]},"n185980223":{"id":"n185980223","loc":[-85.613815,41.955237]},"n185980225":{"id":"n185980225","loc":[-85.613837,41.955316]},"n185990345":{"id":"n185990345","loc":[-85.612211,41.951977]},"n185955743":{"id":"n185955743","loc":[-85.613873,41.95635]},"n185980227":{"id":"n185980227","loc":[-85.613851,41.955415]},"n185980229":{"id":"n185980229","loc":[-85.613918,41.957134]},"n394381703":{"id":"n394381703","loc":[-85.616287,41.955674]},"n394381705":{"id":"n394381705","loc":[-85.615164,41.955676]},"n394490777":{"id":"n394490777","loc":[-85.613973,41.955979]},"n394490780":{"id":"n394490780","loc":[-85.614364,41.955987]},"w17965307":{"id":"w17965307","tags":{"highway":"residential","name":"Bates Ave"},"nodes":["n185958842","n185966349","n185966351","n185966352","n185966353","n185966354","n185966355","n185965062"]},"w17967957":{"id":"w17967957","tags":{"highway":"residential","name":"Krum Ave"},"nodes":["n185966352","n185990345","n185966290"]},"w17964508":{"id":"w17964508","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958842","n185958844","n185958845","n185958847","n185958849","n185958851","n185958852","n185958853","n185958855","n185958835"]},"w17964507":{"id":"w17964507","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958835","n185958837","n185958839"]},"w34367080":{"id":"w34367080","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381699","n394381706","n394381705","n394381703","n394381702","n394381704","n394381699"]},"w17965302":{"id":"w17965302","tags":{"highway":"residential","name":"Clausen Ave"},"nodes":["n185958835","n185966288","n185966290","n185966293","n185965031"]},"w17965156":{"id":"w17965156","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965062","n185965064","n185965066","n185965068","n185965071","n185965073","n185965031"]},"w34369812":{"id":"w34369812","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490775","n394490777","n394490780","n394490782","n394490775"]},"w17965151":{"id":"w17965151","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965031","n185965032","n185965033"]},"w17966756":{"id":"w17966756","tags":{"access":"private","highway":"service","name":"Lockport Dr"},"nodes":["n185978828","n185980222","n185980223","n185980225","n185980227","n185955743","n185980229"]},"w17966056":{"id":"w17966056","tags":{"highway":"residential","name":"Angell Ave"},"nodes":["n185966354","n185973839","n185973840","n185966293"]},"w34367079":{"id":"w34367079","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381700","n394381701","n394381698","n394381699","n394381700"]},"n185955744":{"id":"n185955744","loc":[-85.611753,41.956208]},"n185988932":{"id":"n185988932","loc":[-85.6159,41.956336]},"n185988934":{"id":"n185988934","loc":[-85.6159158,41.9590646]},"n185988935":{"id":"n185988935","loc":[-85.6157358,41.959364],"tags":{"highway":"turning_circle"}},"n2138447007":{"id":"n2138447007","loc":[-85.6130784,41.9590689]},"n2138447008":{"id":"n2138447008","loc":[-85.6133328,41.9593805]},"n2138447003":{"id":"n2138447003","loc":[-85.610238,41.9547745]},"n2138447004":{"id":"n2138447004","loc":[-85.6102652,41.9566041]},"n2138447005":{"id":"n2138447005","loc":[-85.610325,41.9568823]},"n2138447006":{"id":"n2138447006","loc":[-85.6105644,41.9571383]},"n2138447009":{"id":"n2138447009","loc":[-85.6135946,41.959948]},"n2138447010":{"id":"n2138447010","loc":[-85.6136071,41.9629372]},"n2138447011":{"id":"n2138447011","loc":[-85.6134392,41.9633182]},"n2138447012":{"id":"n2138447012","loc":[-85.6130151,41.9636073]},"n2138447013":{"id":"n2138447013","loc":[-85.6122729,41.9637125]},"n2138447014":{"id":"n2138447014","loc":[-85.6056682,41.963752]},"w17964174":{"id":"w17964174","tags":{"access":"private","highway":"service"},"nodes":["n185955743","n185955744"]},"w17967743":{"id":"w17967743","tags":{"access":"private","highway":"service","name":"Manistee River Rd"},"nodes":["n185971574","n185988932","n185971407","n185981301","n185967987","n185988934","n185988935"]},"w203839666":{"id":"w203839666","tags":{"highway":"residential","name":"Hov Aire Drive"},"nodes":["n2138447003","n2138447004","n2138447005","n2138447006","n2138447007","n2138447008","n2138447009","n2138447010","n2138447011","n2138447012","n2138447013","n2138447014"]}}';iD.data = {"deprecated":[{"old":{"amenity":"firepit"},"replace":{"leisure":"firepit"}},{"old":{"barrier":"wire_fence"},"replace":{"barrier":"fence","fence_type":"chain"}},{"old":{"barrier":"wood_fence"},"replace":{"barrier":"fence","fence_type":"wood"}},{"old":{"highway":"ford"},"replace":{"ford":"yes"}},{"old":{"highway":"stile"},"replace":{"barrier":"stile"}},{"old":{"highway":"incline"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"incline_steep"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"unsurfaced"},"replace":{"highway":"road","incline":"unpaved"}},{"old":{"landuse":"wood"},"replace":{"landuse":"forest","natural":"wood"}},{"old":{"natural":"marsh"},"replace":{"natural":"wetland","wetland":"marsh"}},{"old":{"power_source":"*"},"replace":{"generator:source":"$1"}},{"old":{"power_rating":"*"},"replace":{"generator:output":"$1"}},{"old":{"shop":"organic"},"replace":{"shop":"supermarket","organic":"only"}}],"discarded":["created_by","odbl","odbl:note","tiger:upload_uuid","tiger:tlid","tiger:source","tiger:separated","geobase:datasetName","geobase:uuid","sub_sea:type","KSJ2:ADS","KSJ2:ARE","KSJ2:AdminArea","KSJ2:COP_label","KSJ2:DFD","KSJ2:INT","KSJ2:INT_label","KSJ2:LOC","KSJ2:LPN","KSJ2:OPC","KSJ2:PubFacAdmin","KSJ2:RAC","KSJ2:RAC_label","KSJ2:RIC","KSJ2:RIN","KSJ2:WSC","KSJ2:coordinate","KSJ2:curve_id","KSJ2:curve_type","KSJ2:filename","KSJ2:lake_id","KSJ2:lat","KSJ2:long","KSJ2:river_id","yh:LINE_NAME","yh:LINE_NUM","yh:STRUCTURE","yh:TOTYUMONO","yh:TYPE","yh:WIDTH","yh:WIDTH_RANK","SK53_bulk:load"],"wikipedia":[["Abkhazian","Аҧсшәа","ab"],["Achinese","Acèh","ace"],["Afrikaans","Afrikaans","af"],["Akan","Akan","ak"],["Alemannisch","Alemannisch","als"],["Amharic","አማርኛ","am"],["Aragonese","aragonés","an"],["Old English","Ænglisc","ang"],["Arabic","العربية","ar"],["Aramaic","ܐܪܡܝܐ","arc"],["Egyptian Arabic","مصرى","arz"],["Assamese","অসমীয়া","as"],["Asturian","asturianu","ast"],["Avaric","авар","av"],["Aymara","Aymar aru","ay"],["Azerbaijani","azərbaycanca","az"],["South Azerbaijani","تۆرکجه","azb"],["Bashkir","башҡортса","ba"],["Bavarian","Boarisch","bar"],["Samogitian","žemaitėška","bat-smg"],["Bikol Central","Bikol Central","bcl"],["Belarusian","беларуская","be"],["беларуская (тарашкевіца)‎","беларуская (тарашкевіца)‎","be-x-old"],["Bulgarian","български","bg"],["भोजपुरी","भोजपुरी","bh"],["Bislama","Bislama","bi"],["Banjar","Bahasa Banjar","bjn"],["Bambara","bamanankan","bm"],["Bengali","বাংলা","bn"],["Tibetan","བོད་ཡིག","bo"],["Bishnupriya","বিষ্ণুপ্রিয়া মণিপুরী","bpy"],["Breton","brezhoneg","br"],["Bosnian","bosanski","bs"],["Buginese","ᨅᨔ ᨕᨘᨁᨗ","bug"],["буряад","буряад","bxr"],["Catalan","català","ca"],["Chavacano de Zamboanga","Chavacano de Zamboanga","cbk-zam"],["Min Dong Chinese","Mìng-dĕ̤ng-ngṳ̄","cdo"],["Chechen","нохчийн","ce"],["Cebuano","Cebuano","ceb"],["Chamorro","Chamoru","ch"],["Cherokee","ᏣᎳᎩ","chr"],["Cheyenne","Tsetsêhestâhese","chy"],["Central Kurdish","کوردیی ناوەندی","ckb"],["Corsican","corsu","co"],["Cree","Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ","cr"],["Crimean Turkish","qırımtatarca","crh"],["Czech","čeština","cs"],["Kashubian","kaszëbsczi","csb"],["Church Slavic","словѣньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ","cu"],["Chuvash","Чӑвашла","cv"],["Welsh","Cymraeg","cy"],["Danish","dansk","da"],["German","Deutsch","de"],["Zazaki","Zazaki","diq"],["Lower Sorbian","dolnoserbski","dsb"],["Divehi","ދިވެހިބަސް","dv"],["Dzongkha","ཇོང་ཁ","dz"],["Ewe","eʋegbe","ee"],["Greek","Ελληνικά","el"],["Emiliano-Romagnolo","emiliàn e rumagnòl","eml"],["English","English","en"],["Esperanto","Esperanto","eo"],["Spanish","español","es"],["Estonian","eesti","et"],["Basque","euskara","eu"],["Extremaduran","estremeñu","ext"],["Persian","فارسی","fa"],["Fulah","Fulfulde","ff"],["Finnish","suomi","fi"],["Võro","Võro","fiu-vro"],["Fijian","Na Vosa Vakaviti","fj"],["Faroese","føroyskt","fo"],["French","français","fr"],["Arpitan","arpetan","frp"],["Northern Frisian","Nordfriisk","frr"],["Friulian","furlan","fur"],["Western Frisian","Frysk","fy"],["Irish","Gaeilge","ga"],["Gagauz","Gagauz","gag"],["Gan Chinese","贛語","gan"],["Scottish Gaelic","Gàidhlig","gd"],["Galician","galego","gl"],["Gilaki","گیلکی","glk"],["Guarani","Avañe'ẽ","gn"],["Goan Konkani","गोवा कोंकणी / Gova Konknni","gom"],["Gothic","𐌲𐌿𐍄𐌹𐍃𐌺","got"],["Gujarati","ગુજરાતી","gu"],["Manx","Gaelg","gv"],["Hausa","Hausa","ha"],["Hakka Chinese","客家語/Hak-kâ-ngî","hak"],["Hawaiian","Hawai`i","haw"],["Hebrew","עברית","he"],["Hindi","हिन्दी","hi"],["Fiji Hindi","Fiji Hindi","hif"],["Croatian","hrvatski","hr"],["Upper Sorbian","hornjoserbsce","hsb"],["Haitian","Kreyòl ayisyen","ht"],["Hungarian","magyar","hu"],["Armenian","Հայերեն","hy"],["Interlingua","interlingua","ia"],["Indonesian","Bahasa Indonesia","id"],["Interlingue","Interlingue","ie"],["Igbo","Igbo","ig"],["Inupiaq","Iñupiak","ik"],["Iloko","Ilokano","ilo"],["Ido","Ido","io"],["Icelandic","íslenska","is"],["Italian","italiano","it"],["Inuktitut","ᐃᓄᒃᑎᑐᑦ/inuktitut","iu"],["Japanese","日本語","ja"],["Lojban","Lojban","jbo"],["Javanese","Basa Jawa","jv"],["Georgian","ქართული","ka"],["Kara-Kalpak","Qaraqalpaqsha","kaa"],["Kabyle","Taqbaylit","kab"],["Kabardian","Адыгэбзэ","kbd"],["Kongo","Kongo","kg"],["Kikuyu","Gĩkũyũ","ki"],["Kazakh","қазақша","kk"],["Kalaallisut","kalaallisut","kl"],["Khmer","ភាសាខ្មែរ","km"],["Kannada","ಕನ್ನಡ","kn"],["Korean","한국어","ko"],["Komi-Permyak","Перем Коми","koi"],["Karachay-Balkar","къарачай-малкъар","krc"],["Kashmiri","कॉशुर / کٲشُر","ks"],["Colognian","Ripoarisch","ksh"],["Kurdish","Kurdî","ku"],["Komi","коми","kv"],["Cornish","kernowek","kw"],["Kyrgyz","Кыргызча","ky"],["Latin","Latina","la"],["Ladino","Ladino","lad"],["Luxembourgish","Lëtzebuergesch","lb"],["лакку","лакку","lbe"],["Lezghian","лезги","lez"],["Ganda","Luganda","lg"],["Limburgish","Limburgs","li"],["Ligurian","Ligure","lij"],["Lombard","lumbaart","lmo"],["Lingala","lingála","ln"],["Lao","ລາວ","lo"],["Northern Luri","لۊری شومالی","lrc"],["Lithuanian","lietuvių","lt"],["Latgalian","latgaļu","ltg"],["Latvian","latviešu","lv"],["Maithili","मैथिली","mai"],["Basa Banyumasan","Basa Banyumasan","map-bms"],["Moksha","мокшень","mdf"],["Malagasy","Malagasy","mg"],["Eastern Mari","олык марий","mhr"],["Maori","Māori","mi"],["Minangkabau","Baso Minangkabau","min"],["Macedonian","македонски","mk"],["Malayalam","മലയാളം","ml"],["Mongolian","монгол","mn"],["Marathi","मराठी","mr"],["Western Mari","кырык мары","mrj"],["Malay","Bahasa Melayu","ms"],["Maltese","Malti","mt"],["Mirandese","Mirandés","mwl"],["Burmese","မြန်မာဘာသာ","my"],["Erzya","эрзянь","myv"],["Mazanderani","مازِرونی","mzn"],["Nauru","Dorerin Naoero","na"],["Nāhuatl","Nāhuatl","nah"],["Neapolitan","Napulitano","nap"],["Low German","Plattdüütsch","nds"],["Low Saxon (Netherlands)","Nedersaksies","nds-nl"],["Nepali","नेपाली","ne"],["Newari","नेपाल भाषा","new"],["Dutch","Nederlands","nl"],["Norwegian Nynorsk","norsk nynorsk","nn"],["Norwegian","norsk bokmål","no"],["Novial","Novial","nov"],["Nouormand","Nouormand","nrm"],["Northern Sotho","Sesotho sa Leboa","nso"],["Navajo","Diné bizaad","nv"],["Nyanja","Chi-Chewa","ny"],["Occitan","occitan","oc"],["Oromo","Oromoo","om"],["Oriya","ଓଡ଼ିଆ","or"],["Ossetic","Ирон","os"],["Punjabi","ਪੰਜਾਬੀ","pa"],["Pangasinan","Pangasinan","pag"],["Pampanga","Kapampangan","pam"],["Papiamento","Papiamentu","pap"],["Picard","Picard","pcd"],["Pennsylvania German","Deitsch","pdc"],["Palatine German","Pälzisch","pfl"],["Pali","पालि","pi"],["Norfuk / Pitkern","Norfuk / Pitkern","pih"],["Polish","polski","pl"],["Piedmontese","Piemontèis","pms"],["Western Punjabi","پنجابی","pnb"],["Pontic","Ποντιακά","pnt"],["Pashto","پښتو","ps"],["Portuguese","português","pt"],["Quechua","Runa Simi","qu"],["Romansh","rumantsch","rm"],["Romani","Romani","rmy"],["Rundi","Kirundi","rn"],["Romanian","română","ro"],["Aromanian","armãneashti","roa-rup"],["tarandíne","tarandíne","roa-tara"],["Russian","русский","ru"],["Rusyn","русиньскый","rue"],["Kinyarwanda","Kinyarwanda","rw"],["Sanskrit","संस्कृतम्","sa"],["Sakha","саха тыла","sah"],["Sardinian","sardu","sc"],["Sicilian","sicilianu","scn"],["Scots","Scots","sco"],["Sindhi","سنڌي","sd"],["Northern Sami","sámegiella","se"],["Sango","Sängö","sg"],["Serbo-Croatian","srpskohrvatski / српскохрватски","sh"],["Sinhala","සිංහල","si"],["Simple English","Simple English","simple"],["Slovak","slovenčina","sk"],["Slovenian","slovenščina","sl"],["Samoan","Gagana Samoa","sm"],["Shona","chiShona","sn"],["Somali","Soomaaliga","so"],["Albanian","shqip","sq"],["Serbian","српски / srpski","sr"],["Sranan Tongo","Sranantongo","srn"],["Swati","SiSwati","ss"],["Southern Sotho","Sesotho","st"],["Saterland Frisian","Seeltersk","stq"],["Sundanese","Basa Sunda","su"],["Swedish","svenska","sv"],["Swahili","Kiswahili","sw"],["Silesian","ślůnski","szl"],["Tamil","தமிழ்","ta"],["Telugu","తెలుగు","te"],["Tetum","tetun","tet"],["Tajik","тоҷикӣ","tg"],["Thai","ไทย","th"],["Tigrinya","ትግርኛ","ti"],["Turkmen","Türkmençe","tk"],["Tagalog","Tagalog","tl"],["Tswana","Setswana","tn"],["Tongan","lea faka-Tonga","to"],["Tok Pisin","Tok Pisin","tpi"],["Turkish","Türkçe","tr"],["Tsonga","Xitsonga","ts"],["Tatar","татарча/tatarça","tt"],["Tumbuka","chiTumbuka","tum"],["Twi","Twi","tw"],["Tahitian","reo tahiti","ty"],["Tuvinian","тыва дыл","tyv"],["Udmurt","удмурт","udm"],["Uyghur","ئۇيغۇرچە / Uyghurche","ug"],["Ukrainian","українська","uk"],["Urdu","اردو","ur"],["Uzbek","oʻzbekcha/ўзбекча","uz"],["Venda","Tshivenda","ve"],["Venetian","vèneto","vec"],["Veps","vepsän kel’","vep"],["Vietnamese","Tiếng Việt","vi"],["West Flemish","West-Vlams","vls"],["Volapük","Volapük","vo"],["Walloon","walon","wa"],["Waray","Winaray","war"],["Wolof","Wolof","wo"],["Wu Chinese","吴语","wuu"],["Kalmyk","хальмг","xal"],["Xhosa","isiXhosa","xh"],["Mingrelian","მარგალური","xmf"],["Yiddish","ייִדיש","yi"],["Yoruba","Yorùbá","yo"],["Zhuang","Vahcuengh","za"],["Zeelandic","Zeêuws","zea"],["Chinese","中文","zh"],["Classical Chinese","文言","zh-classical"],["Chinese (Min Nan)","Bân-lâm-gú","zh-min-nan"],["Cantonese","粵語","zh-yue"],["Zulu","isiZulu","zu"]],"imperial":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":0},"geometry":{"type":"MultiPolygon","coordinates":[[[[-1.426496,50.639342],[-1.445953,50.648139],[-1.452789,50.654283],[-1.485951,50.669338],[-1.497426,50.672309],[-1.535146,50.669379],[-1.551503,50.665107],[-1.569488,50.658026],[-1.545318,50.686103],[-1.50593,50.707709],[-1.418691,50.733791],[-1.420888,50.730455],[-1.423451,50.7237],[-1.425364,50.72012],[-1.400868,50.721991],[-1.377553,50.734198],[-1.343495,50.761054],[-1.318512,50.772162],[-1.295766,50.773179],[-1.144276,50.733791],[-1.119537,50.734198],[-1.10912,50.732856],[-1.097035,50.726955],[-1.096425,50.724433],[-1.097646,50.71601],[-1.097035,50.713324],[-1.094228,50.712633],[-1.085561,50.714016],[-1.082753,50.713324],[-1.062327,50.692816],[-1.062327,50.685289],[-1.066965,50.685248],[-1.069651,50.683498],[-1.071889,50.680976],[-1.075307,50.678534],[-1.112701,50.671454],[-1.128651,50.666449],[-1.156361,50.650784],[-1.162221,50.645982],[-1.164703,50.640937],[-1.164666,50.639543],[-1.426496,50.639342]]],[[[-7.240314,55.050389],[-7.013736,55.1615],[-6.958913,55.20349],[-6.571562,55.268366],[-6.509633,55.31398],[-6.226158,55.344406],[-6.07105,55.25001],[-5.712696,55.017635],[-5.242021,54.415204],[-5.695554,54.14284],[-5.72473,54.07455],[-6.041633,54.006238],[-6.153953,54.054931],[-6.220539,54.098803],[-6.242502,54.099758],[-6.263661,54.104682],[-6.269887,54.097927],[-6.28465,54.105226],[-6.299585,54.104037],[-6.313796,54.099696],[-6.327128,54.097888],[-6.338962,54.102952],[-6.346662,54.109877],[-6.354827,54.110652],[-6.368108,54.097319],[-6.369348,54.091118],[-6.367643,54.083418],[-6.366919,54.075098],[-6.371157,54.066778],[-6.377513,54.063264],[-6.401026,54.060887],[-6.426761,54.05541],[-6.433892,54.055306],[-6.4403,54.057993],[-6.446243,54.062438],[-6.450222,54.066675],[-6.450894,54.068432],[-6.47854,54.067709],[-6.564013,54.04895],[-6.571868,54.049519],[-6.587164,54.053343],[-6.595071,54.052412],[-6.60029,54.04895],[-6.605217,54.044475],[-6.610987,54.039235],[-6.616465,54.037271],[-6.630624,54.041819],[-6.657289,54.061146],[-6.672534,54.068432],[-6.657082,54.091945],[-6.655791,54.103314],[-6.666436,54.114786],[-6.643957,54.131839],[-6.634552,54.150133],[-6.640339,54.168013],[-6.648448,54.173665],[-6.663025,54.183826],[-6.683954,54.194368],[-6.694651,54.197985],[-6.706537,54.198915],[-6.717234,54.195143],[-6.724779,54.188631],[-6.73284,54.183567],[-6.744777,54.184187],[-6.766481,54.192352],[-6.787824,54.202998],[-6.807358,54.21633],[-6.823946,54.23235],[-6.829733,54.242375],[-6.833196,54.25209],[-6.837743,54.260513],[-6.846683,54.266456],[-6.882185,54.277257],[-6.864667,54.282734],[-6.856657,54.292811],[-6.858414,54.307332],[-6.870015,54.326001],[-6.879705,54.341594],[-6.885957,54.345624],[-6.897895,54.346193],[-6.905956,54.349035],[-6.915051,54.365933],[-6.922028,54.372703],[-6.984091,54.403089],[-7.017836,54.413166],[-7.049255,54.411512],[-7.078504,54.394717],[-7.127028,54.349759],[-7.159894,54.335186],[-7.168059,54.335031],[-7.185629,54.336943],[-7.18947,54.335692],[-7.19245,54.334721],[-7.193949,54.329967],[-7.191468,54.323869],[-7.187644,54.318804],[-7.185009,54.317254],[-7.184647,54.316634],[-7.192399,54.307384],[-7.193691,54.307539],[-7.199168,54.303457],[-7.206661,54.304903],[-7.211467,54.30418],[-7.209038,54.293431],[-7.1755,54.283664],[-7.181495,54.269763],[-7.14589,54.25209],[-7.159739,54.24067],[-7.153331,54.224237],[-7.174725,54.216072],[-7.229502,54.207545],[-7.240871,54.202326],[-7.249088,54.197416],[-7.255496,54.190854],[-7.261128,54.18088],[-7.256322,54.176901],[-7.247021,54.17225],[-7.24578,54.166979],[-7.265366,54.16114],[-7.26087,54.151166],[-7.263505,54.140986],[-7.27074,54.132253],[-7.280042,54.126155],[-7.293788,54.122021],[-7.297353,54.125896],[-7.29632,54.134991],[-7.296423,54.146515],[-7.295028,54.155404],[-7.292134,54.162638],[-7.295545,54.165119],[-7.325982,54.154577],[-7.333165,54.149409],[-7.333165,54.142743],[-7.310324,54.114683],[-7.316489,54.11428],[-7.326964,54.113597],[-7.375488,54.123312],[-7.390216,54.121194],[-7.39466,54.121917],[-7.396624,54.126258],[-7.403962,54.135043],[-7.41223,54.136438],[-7.422255,54.135456],[-7.425769,54.136955],[-7.414659,54.145688],[-7.439619,54.146929],[-7.480753,54.127653],[-7.502302,54.125121],[-7.609014,54.139901],[-7.620796,54.144965],[-7.624052,54.153336],[-7.625706,54.162173],[-7.632682,54.168529],[-7.70477,54.200362],[-7.722599,54.202326],[-7.782078,54.2],[-7.836959,54.204341],[-7.856441,54.211421],[-7.86967,54.226872],[-7.873649,54.271055],[-7.880264,54.287023],[-7.894966,54.293586],[-7.93411,54.297049],[-7.942075,54.298873],[-7.950802,54.300873],[-7.96801,54.31219],[-7.981033,54.326556],[-8.002194,54.357923],[-8.03134,54.358027],[-8.05648,54.365882],[-8.079941,54.380196],[-8.122419,54.415233],[-8.146346,54.430736],[-8.156035,54.439055],[-8.158128,54.447117],[-8.161177,54.454817],[-8.173837,54.461741],[-8.168467,54.463477],[-8.15017,54.46939],[-8.097046,54.478588],[-8.072448,54.487063],[-8.060976,54.493316],[-8.05586,54.497553],[-8.043561,54.512229],[-8.023278,54.529696],[-8.002194,54.543442],[-7.926411,54.533055],[-7.887137,54.532125],[-7.848844,54.54091],[-7.749264,54.596152],[-7.707871,54.604162],[-7.707944,54.604708],[-7.707951,54.604763],[-7.710558,54.624264],[-7.721204,54.625866],[-7.736758,54.619251],[-7.753553,54.614497],[-7.769159,54.618011],[-7.801199,54.634806],[-7.814996,54.639457],[-7.822541,54.638113],[-7.838044,54.63124],[-7.846416,54.631447],[-7.85427,54.636408],[-7.864347,54.649069],[-7.872771,54.652221],[-7.890082,54.655063],[-7.906619,54.661316],[-7.914835,54.671651],[-7.907135,54.686689],[-7.913233,54.688653],[-7.929666,54.696714],[-7.880109,54.711029],[-7.845899,54.731027],[-7.832153,54.730614],[-7.803576,54.716145],[-7.770503,54.706016],[-7.736603,54.707463],[-7.70229,54.718883],[-7.667512,54.738779],[-7.649683,54.744877],[-7.61537,54.739347],[-7.585398,54.744722],[-7.566639,54.738675],[-7.556149,54.738365],[-7.543075,54.741673],[-7.543023,54.743791],[-7.548398,54.747202],[-7.551705,54.754695],[-7.549741,54.779603],[-7.543385,54.793091],[-7.470831,54.845284],[-7.45507,54.863009],[-7.444735,54.884455],[-7.444735,54.894893],[-7.448972,54.920318],[-7.445251,54.932152],[-7.436983,54.938301],[-7.417139,54.943056],[-7.415755,54.944372],[-7.408665,54.951117],[-7.407424,54.959437],[-7.413109,54.984965],[-7.409078,54.992045],[-7.403755,54.99313],[-7.40112,54.994836],[-7.405254,55.003569],[-7.376987,55.02889],[-7.366962,55.035557],[-7.355024,55.040931],[-7.291152,55.046615],[-7.282987,55.051835],[-7.275288,55.058863],[-7.266503,55.065167],[-7.247097,55.069328],[-7.2471,55.069322],[-7.256744,55.050686],[-7.240956,55.050279],[-7.240314,55.050389]]],[[[-13.688588,57.596259],[-13.690419,57.596259],[-13.691314,57.596503],[-13.691314,57.597154],[-13.690419,57.597805],[-13.688588,57.597805],[-13.687652,57.597154],[-13.687652,57.596869],[-13.688588,57.596259]]],[[[-4.839121,54.469789],[-4.979941,54.457977],[-5.343644,54.878637],[-5.308469,55.176452],[-6.272566,55.418443],[-8.690528,57.833706],[-6.344705,59.061083],[-4.204785,58.63305],[-2.31566,60.699068],[-1.695335,60.76432],[-1.58092,60.866001],[-0.17022,60.897204],[-0.800508,59.770037],[-1.292368,57.732574],[-1.850077,55.766368],[-1.73054,55.782219],[1.892395,52.815229],[1.742775,51.364209],[1.080173,50.847526],[0.000774,50.664982],[-0.162997,50.752401],[-0.725152,50.731879],[-0.768853,50.741516],[-0.770985,50.736884],[-0.789947,50.730048],[-0.812815,50.734768],[-0.877742,50.761156],[-0.942879,50.758338],[-0.992581,50.737379],[-1.18513,50.766989],[-1.282741,50.792353],[-1.375004,50.772063],[-1.523427,50.719605],[-1.630649,50.695128],[-1.663617,50.670508],[-1.498021,50.40831],[-4.097427,49.735486],[-6.825199,49.700905],[-5.541541,51.446591],[-6.03361,51.732369],[-4.791746,52.635365],[-4.969244,52.637413],[-5.049473,53.131209],[-4.787393,53.409491],[-4.734148,53.424866],[-4.917096,53.508212],[-4.839121,54.469789]]]]}},{"type":"Feature","properties":{"id":0},"geometry":{"type":"MultiPolygon","coordinates":[[[[-157.018938,19.300864],[-179.437336,27.295312],[-179.480084,28.991459],[-168.707465,26.30325],[-163.107414,24.60499],[-153.841679,20.079306],[-154.233846,19.433391],[-153.61725,18.900587],[-154.429471,18.171036],[-156.780638,18.718492],[-157.018938,19.300864]]],[[[-78.91269,43.037032],[-78.964351,42.976393],[-78.981718,42.979043],[-78.998055,42.991111],[-79.01189,43.004358],[-79.022046,43.010539],[-79.023076,43.017015],[-79.00983,43.050867],[-79.011449,43.065291],[-78.993051,43.066174],[-78.975536,43.069707],[-78.958905,43.070884],[-78.943304,43.065291],[-78.917399,43.058521],[-78.908569,43.049396],[-78.91269,43.037032]]],[[[-123.03529,48.992515],[-123.035308,48.992499],[-123.045277,48.984361],[-123.08849,48.972235],[-123.089345,48.987982],[-123.090484,48.992499],[-123.090488,48.992515],[-123.035306,48.992515],[-123.03529,48.992515]]],[[[-103.837038,29.279906],[-103.864121,29.281366],[-103.928122,29.293019],[-104.01915,29.32033],[-104.057313,29.339037],[-104.105424,29.385675],[-104.139789,29.400584],[-104.161648,29.416759],[-104.194514,29.448927],[-104.212291,29.484661],[-104.218698,29.489829],[-104.227148,29.493033],[-104.251022,29.508588],[-104.267171,29.526571],[-104.292751,29.532824],[-104.320604,29.532255],[-104.338484,29.524013],[-104.349026,29.537578],[-104.430443,29.582795],[-104.437832,29.58543],[-104.444008,29.589203],[-104.448555,29.597678],[-104.452069,29.607109],[-104.455222,29.613387],[-104.469381,29.625402],[-104.516639,29.654315],[-104.530824,29.667906],[-104.535036,29.677802],[-104.535191,29.687853],[-104.537103,29.702116],[-104.543666,29.71643],[-104.561391,29.745421],[-104.570279,29.787511],[-104.583586,29.802575],[-104.601207,29.81477],[-104.619682,29.833064],[-104.623764,29.841487],[-104.637588,29.887996],[-104.656346,29.908201],[-104.660635,29.918433],[-104.663478,29.923084],[-104.676526,29.93683],[-104.680479,29.942308],[-104.682469,29.952126],[-104.680117,29.967784],[-104.680479,29.976466],[-104.699108,30.03145],[-104.701589,30.055324],[-104.698592,30.075271],[-104.684639,30.111135],[-104.680479,30.134131],[-104.67867,30.170356],[-104.681564,30.192939],[-104.695853,30.208441],[-104.715231,30.243995],[-104.724585,30.252211],[-104.742155,30.25986],[-104.74939,30.264459],[-104.761689,30.284199],[-104.774143,30.311588],[-104.788767,30.335927],[-104.807732,30.346418],[-104.8129,30.350707],[-104.814967,30.360577],[-104.816001,30.371997],[-104.818274,30.380524],[-104.824269,30.38719],[-104.83755,30.394063],[-104.844939,30.40104],[-104.853259,30.41215],[-104.855016,30.417473],[-104.853621,30.423984],[-104.852432,30.438867],[-104.854655,30.448737],[-104.864473,30.462018],[-104.866695,30.473025],[-104.865248,30.479898],[-104.859615,30.491112],[-104.859254,30.497261],[-104.863026,30.502377],[-104.879718,30.510852],[-104.882146,30.520929],[-104.884007,30.541858],[-104.886591,30.551883],[-104.898166,30.569401],[-104.928242,30.599529],[-104.93434,30.610536],[-104.941057,30.61405],[-104.972735,30.618029],[-104.98276,30.620716],[-104.989117,30.629553],[-104.991649,30.640301],[-104.992941,30.651464],[-104.995783,30.661747],[-105.008495,30.676992],[-105.027977,30.690117],[-105.049475,30.699264],[-105.06813,30.702675],[-105.087043,30.709806],[-105.133604,30.757917],[-105.140425,30.750476],[-105.153241,30.763188],[-105.157788,30.76572],[-105.160889,30.764118],[-105.162698,30.774919],[-105.167297,30.781171],[-105.17479,30.783962],[-105.185125,30.784634],[-105.195306,30.787941],[-105.204917,30.80241],[-105.2121,30.805718],[-105.21825,30.806803],[-105.229257,30.810214],[-105.232874,30.809128],[-105.239851,30.801532],[-105.243985,30.799103],[-105.249049,30.798845],[-105.259488,30.802979],[-105.265844,30.808405],[-105.270753,30.814348],[-105.277006,30.819412],[-105.334315,30.843803],[-105.363771,30.850366],[-105.376173,30.859565],[-105.41555,30.902456],[-105.496682,30.95651],[-105.530789,30.991701],[-105.555955,31.002605],[-105.565722,31.016661],[-105.578641,31.052163],[-105.59094,31.071438],[-105.605875,31.081928],[-105.623496,31.090351],[-105.643805,31.103684],[-105.668042,31.127869],[-105.675225,31.131951],[-105.692278,31.137635],[-105.76819,31.18001],[-105.777854,31.192722],[-105.78483,31.211016],[-105.861983,31.288376],[-105.880147,31.300881],[-105.896994,31.305997],[-105.897149,31.309511],[-105.908802,31.317004],[-105.928052,31.326461],[-105.934563,31.335504],[-105.941772,31.352351],[-105.948515,31.361239],[-105.961202,31.371006],[-106.004739,31.396948],[-106.021147,31.402167],[-106.046261,31.404648],[-106.065304,31.410952],[-106.099385,31.428884],[-106.141113,31.439167],[-106.164316,31.447797],[-106.174471,31.460251],[-106.209249,31.477305],[-106.215424,31.483919],[-106.21744,31.488725],[-106.218731,31.494616],[-106.222891,31.50459],[-106.232658,31.519938],[-106.274749,31.562622],[-106.286298,31.580141],[-106.312292,31.648612],[-106.331309,31.68215],[-106.35849,31.717548],[-106.39177,31.745919],[-106.428951,31.758476],[-106.473135,31.755065],[-106.492797,31.759044],[-106.501425,31.766344],[-106.506052,31.770258],[-106.517189,31.773824],[-106.558969,31.773876],[-106.584859,31.773927],[-106.610697,31.773979],[-106.636587,31.774082],[-106.662477,31.774134],[-106.688315,31.774237],[-106.714205,31.774237],[-106.740095,31.774289],[-106.765933,31.774392],[-106.791823,31.774444],[-106.817713,31.774496],[-106.843603,31.774547],[-106.869441,31.774599],[-106.895331,31.774702],[-106.921221,31.774702],[-106.947111,31.774754],[-106.973001,31.774857],[-106.998891,31.774909],[-107.02478,31.774961],[-107.05067,31.775013],[-107.076509,31.775064],[-107.102398,31.775168],[-107.128288,31.775168],[-107.154127,31.775219],[-107.180016,31.775374],[-107.205906,31.775374],[-107.231796,31.775426],[-107.257634,31.775478],[-107.283524,31.775529],[-107.309414,31.775633],[-107.335252,31.775684],[-107.361142,31.775788],[-107.387032,31.775788],[-107.412896,31.775839],[-107.438786,31.775943],[-107.464676,31.775994],[-107.490566,31.776098],[-107.516404,31.776149],[-107.542294,31.776201],[-107.568184,31.776253],[-107.594074,31.776304],[-107.619964,31.776408],[-107.645854,31.776459],[-107.671744,31.776459],[-107.697633,31.776563],[-107.723472,31.776614],[-107.749362,31.776666],[-107.775251,31.776718],[-107.801141,31.77677],[-107.82698,31.776873],[-107.852869,31.776925],[-107.878759,31.776925],[-107.904598,31.777028],[-107.930487,31.77708],[-107.956377,31.777131],[-107.982216,31.777183],[-108.008105,31.777235],[-108.033995,31.777338],[-108.059885,31.77739],[-108.085723,31.77739],[-108.111613,31.777545],[-108.137503,31.777545],[-108.163341,31.777648],[-108.189283,31.7777],[-108.215121,31.777751],[-108.215121,31.770723],[-108.215121,31.763695],[-108.215121,31.756667],[-108.215121,31.749639],[-108.215121,31.74256],[-108.215121,31.735583],[-108.215121,31.728555],[-108.215121,31.721476],[-108.215121,31.714396],[-108.215121,31.70742],[-108.215121,31.700392],[-108.215121,31.693312],[-108.215121,31.686284],[-108.215121,31.679256],[-108.215121,31.672176],[-108.21507,31.665148],[-108.215018,31.658172],[-108.215018,31.651092],[-108.215018,31.644064],[-108.215018,31.637036],[-108.215018,31.630008],[-108.215018,31.62298],[-108.215018,31.615952],[-108.215018,31.608873],[-108.215018,31.601845],[-108.215018,31.594817],[-108.215018,31.587789],[-108.215018,31.580761],[-108.215018,31.573733],[-108.215018,31.566653],[-108.215018,31.559625],[-108.214966,31.552597],[-108.214966,31.545569],[-108.214966,31.538489],[-108.214966,31.531461],[-108.214966,31.524485],[-108.214966,31.517405],[-108.214966,31.510378],[-108.214966,31.503401],[-108.214966,31.496322],[-108.214966,31.489242],[-108.214966,31.482214],[-108.214966,31.475238],[-108.214966,31.468158],[-108.214966,31.46113],[-108.214966,31.454102],[-108.214966,31.447074],[-108.214915,31.440046],[-108.214863,31.432966],[-108.214863,31.425938],[-108.214863,31.41891],[-108.214863,31.411882],[-108.214863,31.404803],[-108.214863,31.397826],[-108.214863,31.390798],[-108.214863,31.383719],[-108.214863,31.376639],[-108.214863,31.369663],[-108.214863,31.362635],[-108.214863,31.355555],[-108.214863,31.348527],[-108.214863,31.341551],[-108.214863,31.334471],[-108.214811,31.327443],[-108.257573,31.327391],[-108.300336,31.327391],[-108.34302,31.327391],[-108.385731,31.327391],[-108.428442,31.327391],[-108.471152,31.327391],[-108.513837,31.327391],[-108.556547,31.327391],[-108.59931,31.327391],[-108.64202,31.327391],[-108.684757,31.327391],[-108.727467,31.327391],[-108.770178,31.327391],[-108.812914,31.327391],[-108.855625,31.327391],[-108.898335,31.327391],[-108.941046,31.327391],[-108.968282,31.327391],[-108.983731,31.327391],[-109.026493,31.327391],[-109.04743,31.327391],[-109.069203,31.327391],[-109.111914,31.327391],[-109.154599,31.327391],[-109.197361,31.327391],[-109.240072,31.32734],[-109.282782,31.32734],[-109.325519,31.32734],[-109.368229,31.32734],[-109.410914,31.32734],[-109.45365,31.32734],[-109.496387,31.32734],[-109.539071,31.32734],[-109.581808,31.32734],[-109.624493,31.32734],[-109.667177,31.32734],[-109.709965,31.32734],[-109.75265,31.32734],[-109.795335,31.32734],[-109.838123,31.32734],[-109.880808,31.32734],[-109.923596,31.327288],[-109.96628,31.327236],[-110.008965,31.327236],[-110.051702,31.327236],[-110.094386,31.327236],[-110.137071,31.327236],[-110.179807,31.327236],[-110.222544,31.327236],[-110.265229,31.327236],[-110.308017,31.327236],[-110.350753,31.327236],[-110.39349,31.327236],[-110.436174,31.327236],[-110.478859,31.327236],[-110.521595,31.327236],[-110.56428,31.327236],[-110.606965,31.327236],[-110.649727,31.327236],[-110.692438,31.327236],[-110.7352,31.327236],[-110.777885,31.327236],[-110.820595,31.327236],[-110.863358,31.327236],[-110.906068,31.327236],[-110.948753,31.327185],[-111.006269,31.327185],[-111.067118,31.333644],[-111.094455,31.342532],[-111.145924,31.359069],[-111.197446,31.375554],[-111.248864,31.392142],[-111.300333,31.40873],[-111.351803,31.425318],[-111.403299,31.441855],[-111.454768,31.458339],[-111.506238,31.474979],[-111.915464,31.601431],[-112.324715,31.727987],[-112.733967,31.854543],[-113.143218,31.981046],[-113.552444,32.107602],[-113.961696,32.234132],[-114.370921,32.360687],[-114.780147,32.487243],[-114.816785,32.498534],[-114.819373,32.499363],[-114.822108,32.50024],[-114.809447,32.511324],[-114.795546,32.552226],[-114.794203,32.574111],[-114.802678,32.594497],[-114.786813,32.621033],[-114.781542,32.628061],[-114.758804,32.64483],[-114.751156,32.65222],[-114.739477,32.669066],[-114.731209,32.686636],[-114.723871,32.711519],[-114.724284,32.712835],[-114.724285,32.712836],[-114.764541,32.709839],[-114.838076,32.704206],[-114.911612,32.698703],[-114.985199,32.693122],[-115.058734,32.687567],[-115.13227,32.681986],[-115.205806,32.676456],[-115.27929,32.670823],[-115.352851,32.665346],[-115.426386,32.659765],[-115.499922,32.654209],[-115.573535,32.648654],[-115.647019,32.643073],[-115.720529,32.637518],[-115.794064,32.631963],[-115.8676,32.626408],[-115.941213,32.620827],[-116.014748,32.615271],[-116.088232,32.609664],[-116.161742,32.604161],[-116.235329,32.598554],[-116.308891,32.593025],[-116.382426,32.587469],[-116.455962,32.581888],[-116.529472,32.576333],[-116.603007,32.570804],[-116.676543,32.565223],[-116.750104,32.559667],[-116.82364,32.554086],[-116.897201,32.548531],[-116.970737,32.542976],[-117.044221,32.537421],[-117.125121,32.531669],[-117.125969,32.538258],[-117.239623,32.531308],[-120.274098,32.884264],[-121.652736,34.467248],[-124.367265,37.662798],[-126.739806,41.37928],[-126.996297,45.773888],[-124.770704,48.44258],[-123.734053,48.241906],[-123.1663,48.27837],[-123.193018,48.501035],[-123.176987,48.65482],[-122.912481,48.753561],[-122.899122,48.897797],[-122.837671,48.97502],[-122.743986,48.980582],[-122.753,48.992499],[-122.753012,48.992515],[-122.653258,48.992515],[-122.433375,48.992515],[-122.213517,48.992515],[-121.993763,48.992515],[-121.773958,48.992515],[-121.554152,48.992515],[-121.33432,48.992515],[-121.114515,48.992515],[-95.396937,48.99267],[-95.177106,48.99267],[-95.168527,48.995047],[-95.161887,49.001145],[-95.159329,49.01179],[-95.159665,49.10951],[-95.160027,49.223353],[-95.160337,49.313012],[-95.160569,49.369494],[-95.102821,49.35394],[-94.982518,49.356162],[-94.926087,49.345568],[-94.856195,49.318283],[-94.839142,49.308878],[-94.827256,49.292858],[-94.819892,49.252034],[-94.810358,49.229606],[-94.806121,49.210899],[-94.811185,49.166561],[-94.803743,49.146407],[-94.792039,49.12646],[-94.753772,49.026156],[-94.711217,48.914586],[-94.711734,48.862755],[-94.712147,48.842446],[-94.713284,48.823843],[-94.710907,48.807513],[-94.701786,48.790098],[-94.688893,48.778832],[-94.592852,48.726433],[-94.519161,48.70447],[-94.4795,48.700698],[-94.311577,48.713927],[-94.292586,48.711912],[-94.284034,48.709069],[-94.274499,48.704108],[-94.265482,48.697752],[-94.258454,48.690828],[-94.255767,48.683541],[-94.252459,48.662405],[-94.251038,48.65729],[-94.23215,48.652019],[-94.03485,48.643311],[-93.874885,48.636206],[-93.835741,48.617137],[-93.809386,48.543576],[-93.778664,48.519468],[-93.756779,48.516549],[-93.616297,48.531302],[-93.599889,48.526341],[-93.566584,48.538279],[-93.491756,48.542309],[-93.459924,48.557399],[-93.45225,48.572721],[-93.453774,48.586958],[-93.451475,48.597422],[-93.417316,48.604114],[-93.385716,48.614863],[-93.25774,48.630314],[-93.131701,48.62463],[-92.97972,48.61768],[-92.955588,48.612228],[-92.884197,48.579878],[-92.72555,48.548692],[-92.648604,48.536263],[-92.630181,48.519468],[-92.627468,48.502777],[-92.646743,48.497428],[-92.691366,48.489858],[-92.710641,48.482882],[-92.718909,48.459782],[-92.704052,48.445158],[-92.677129,48.441747],[-92.657053,48.438233],[-92.570521,48.446656],[-92.526932,48.445623],[-92.490629,48.433117],[-92.474532,48.410483],[-92.467581,48.394282],[-92.467064,48.353225],[-92.462465,48.329299],[-92.451381,48.312685],[-92.41823,48.282041],[-92.38464,48.232406],[-92.371851,48.222587],[-92.353815,48.222897],[-92.327874,48.229435],[-92.303663,48.239279],[-92.291029,48.249562],[-92.292062,48.270336],[-92.301416,48.290645],[-92.303095,48.310928],[-92.281598,48.33178],[-92.259118,48.339635],[-92.154732,48.350125],[-92.070499,48.346714],[-92.043421,48.334596],[-92.030114,48.313176],[-92.021355,48.287441],[-92.007997,48.262482],[-91.992158,48.247909],[-91.975492,48.236566],[-91.957302,48.228323],[-91.852244,48.195974],[-91.764988,48.187344],[-91.744137,48.179593],[-91.727575,48.168327],[-91.695509,48.13758],[-91.716438,48.112051],[-91.692512,48.097866],[-91.618615,48.089572],[-91.597479,48.090399],[-91.589676,48.088332],[-91.581098,48.080942],[-91.579806,48.070969],[-91.585129,48.06084],[-91.586989,48.052572],[-91.574845,48.048205],[-91.487098,48.053476],[-91.464722,48.048955],[-91.446274,48.040738],[-91.427929,48.036449],[-91.3654,48.057843],[-91.276362,48.064768],[-91.23807,48.082648],[-91.203963,48.107659],[-91.071103,48.170859],[-91.02816,48.184838],[-91.008109,48.194372],[-90.923153,48.227109],[-90.873802,48.234344],[-90.840678,48.220107],[-90.837939,48.210547],[-90.848843,48.198713],[-90.849721,48.189566],[-90.843003,48.176983],[-90.83427,48.171789],[-90.823883,48.168327],[-90.812307,48.160989],[-90.803057,48.147166],[-90.796701,48.117064],[-90.786469,48.10045],[-90.750347,48.083991],[-90.701307,48.08456],[-90.611079,48.103499],[-90.586843,48.104817],[-90.573872,48.097892],[-90.562194,48.088849],[-90.542014,48.083733],[-90.531601,48.08456],[-90.501887,48.094275],[-90.490493,48.096239],[-90.483465,48.094482],[-90.477858,48.091536],[-90.470623,48.089882],[-90.178625,48.116444],[-90.120386,48.115359],[-90.073257,48.101199],[-90.061036,48.091019],[-90.008222,48.029731],[-89.995329,48.018595],[-89.980317,48.010094],[-89.92045,47.98746],[-89.902441,47.985909],[-89.803454,48.013763],[-89.780975,48.017199],[-89.763302,48.017303],[-89.745964,48.013763],[-89.724596,48.005908],[-89.712788,48.003376],[-89.678656,48.008699],[-89.65659,48.007975],[-89.593105,47.996503],[-89.581753,47.996333],[-89.586724,47.992938],[-89.310872,47.981097],[-89.072861,48.046842],[-88.49789,48.212841],[-88.286621,48.156675],[-85.939935,47.280501],[-84.784644,46.770068],[-84.516909,46.435083],[-84.489712,46.446652],[-84.491052,46.457658],[-84.478301,46.466467],[-84.465408,46.478172],[-84.448096,46.489722],[-84.42324,46.511581],[-84.389702,46.520262],[-84.352469,46.522743],[-84.30534,46.501607],[-84.242011,46.526464],[-84.197285,46.546359],[-84.147676,46.541346],[-84.110443,46.526464],[-84.158812,46.433343],[-84.147676,46.399882],[-84.129046,46.375026],[-84.10543,46.347741],[-84.105944,46.346374],[-84.117195,46.347157],[-84.117489,46.338326],[-84.122361,46.331922],[-84.112061,46.287102],[-84.092672,46.227469],[-84.111983,46.20337],[-84.015118,46.149712],[-83.957038,46.045736],[-83.676821,46.15388],[-83.429449,46.086221],[-83.523049,45.892052],[-83.574563,45.890259],[-82.551615,44.857931],[-82.655591,43.968545],[-82.440632,43.096285],[-82.460131,43.084392],[-82.458894,43.083247],[-82.431813,43.039387],[-82.424748,43.02408],[-82.417242,43.01731],[-82.416369,43.01742],[-82.416412,43.017143],[-82.414603,42.983243],[-82.430442,42.951307],[-82.453179,42.918983],[-82.464781,42.883637],[-82.468036,42.863974],[-82.482325,42.835113],[-82.485271,42.818524],[-82.473618,42.798164],[-82.470982,42.790568],[-82.471344,42.779845],[-82.476951,42.761474],[-82.48341,42.719254],[-82.511264,42.646675],[-82.526224,42.619906],[-82.549246,42.590941],[-82.575833,42.571795],[-82.608467,42.561098],[-82.644331,42.557817],[-82.644698,42.557533],[-82.644932,42.561634],[-82.637132,42.568405],[-82.60902,42.579296],[-82.616673,42.582828],[-82.636985,42.599607],[-82.625357,42.616092],[-82.629331,42.626394],[-82.638751,42.633459],[-82.644344,42.640524],[-82.644166,42.641056],[-82.716083,42.617461],[-82.777592,42.408506],[-82.888693,42.406093],[-82.889991,42.403266],[-82.905739,42.387665],[-82.923842,42.374419],[-82.937972,42.366176],[-82.947686,42.363527],[-82.979624,42.359406],[-83.042618,42.340861],[-83.061899,42.32732],[-83.081622,42.30907],[-83.11342,42.279619],[-83.145306,42.066968],[-83.177398,41.960666],[-83.21512,41.794493],[-82.219051,41.516445],[-80.345329,42.13344],[-80.316455,42.123137],[-79.270266,42.591872],[-79.221058,42.582892],[-78.871842,42.860012],[-78.875011,42.867184],[-78.896205,42.897209],[-78.901651,42.908101],[-78.90901,42.952255],[-78.913426,42.957848],[-78.932118,42.9708],[-78.936386,42.979631],[-78.927997,43.002003],[-78.893114,43.029379],[-78.887963,43.051456],[-78.914897,43.076477],[-79.026167,43.086485],[-79.065231,43.10573],[-79.065273,43.105897],[-79.065738,43.120237],[-79.061423,43.130288],[-79.055583,43.138427],[-79.051604,43.146851],[-79.04933,43.159847],[-79.048607,43.170622],[-79.053775,43.260358],[-79.058425,43.277799],[-79.058631,43.2782],[-78.990696,43.286947],[-78.862059,43.324332],[-78.767813,43.336418],[-78.516117,43.50645],[-76.363317,43.943219],[-76.396746,44.106667],[-76.364697,44.111631],[-76.366146,44.117349],[-76.357462,44.131478],[-76.183493,44.223025],[-76.162644,44.229888],[-76.176117,44.30795],[-76.046414,44.354817],[-75.928746,44.391137],[-75.852508,44.381639],[-75.849095,44.386103],[-75.847623,44.392579],[-75.84674,44.398172],[-75.845415,44.40141],[-75.780803,44.432318],[-75.770205,44.446153],[-75.772266,44.463815],[-75.779184,44.48236],[-75.791496,44.496513],[-75.791183,44.496768],[-75.754622,44.527567],[-75.69969,44.581673],[-75.578199,44.661513],[-75.455958,44.741766],[-75.341831,44.816749],[-75.270233,44.863774],[-75.129647,44.925166],[-75.075594,44.935501],[-75.058721,44.941031],[-75.0149,44.96599],[-74.998647,44.972398],[-74.940201,44.987746],[-74.903744,45.005213],[-74.88651,45.009398],[-74.868474,45.010122],[-74.741557,44.998857],[-74.712961,44.999254],[-74.695875,44.99803],[-74.596114,44.998495],[-74.496352,44.999012],[-74.197146,45.000458],[-71.703551,45.012757],[-71.603816,45.013274],[-71.505848,45.013731],[-71.50408,45.013739],[-71.506613,45.037045],[-71.504752,45.052962],[-71.497259,45.066553],[-71.45659,45.110994],[-71.451215,45.121691],[-71.445996,45.140295],[-71.441604,45.150682],[-71.413026,45.186184],[-71.406567,45.204942],[-71.42269,45.217189],[-71.449045,45.226905],[-71.438813,45.233468],[-71.394888,45.241529],[-71.381245,45.250779],[-71.3521,45.278323],[-71.334323,45.28871],[-71.311534,45.294136],[-71.293396,45.292327],[-71.20937,45.254758],[-71.185133,45.248557],[-71.160329,45.245767],[-71.141725,45.252329],[-71.111029,45.287108],[-71.095242,45.300905],[-71.085553,45.304213],[-71.084952,45.304293],[-71.064211,45.307055],[-71.054418,45.310362],[-71.036667,45.323385],[-71.027598,45.33465],[-71.016539,45.343125],[-70.993155,45.347827],[-70.968118,45.34452],[-70.951608,45.332014],[-70.906908,45.246232],[-70.892412,45.234604],[-70.874351,45.245663],[-70.870605,45.255275],[-70.872491,45.274189],[-70.870243,45.283129],[-70.862621,45.290363],[-70.842389,45.301215],[-70.835258,45.309794],[-70.83208,45.328552],[-70.835465,45.373097],[-70.833837,45.393096],[-70.825982,45.410459],[-70.812986,45.42343],[-70.794873,45.430406],[-70.771877,45.430045],[-70.75255,45.422345],[-70.718004,45.397282],[-70.696739,45.388652],[-70.675785,45.388704],[-70.65359,45.395473],[-70.641316,45.408496],[-70.650257,45.427461],[-70.668162,45.439036],[-70.707385,45.4564],[-70.722836,45.470921],[-70.732009,45.491591],[-70.730329,45.507973],[-70.686792,45.572723],[-70.589614,45.651788],[-70.572406,45.662279],[-70.514735,45.681709],[-70.484763,45.699641],[-70.4728,45.703568],[-70.450424,45.703723],[-70.439132,45.705893],[-70.419315,45.716901],[-70.407351,45.731525],[-70.402442,45.749663],[-70.403941,45.771161],[-70.408282,45.781651],[-70.413682,45.787697],[-70.41717,45.793795],[-70.415232,45.804389],[-70.409935,45.810745],[-70.389807,45.825059],[-70.312654,45.867641],[-70.283173,45.890482],[-70.262528,45.923038],[-70.255939,45.948876],[-70.263148,45.956834],[-70.280434,45.959315],[-70.303947,45.968616],[-70.316298,45.982982],[-70.316892,45.999002],[-70.306143,46.035331],[-70.303637,46.038483],[-70.294309,46.044943],[-70.29201,46.048663],[-70.293017,46.054038],[-70.296092,46.057862],[-70.300795,46.061737],[-70.304774,46.065975],[-70.311362,46.071866],[-70.312629,46.079566],[-70.30033,46.089281],[-70.26444,46.106593],[-70.24948,46.120597],[-70.244002,46.141009],[-70.249247,46.162765],[-70.263329,46.183229],[-70.284801,46.191859],[-70.280899,46.211857],[-70.253407,46.251493],[-70.236173,46.288339],[-70.223693,46.300793],[-70.201886,46.305495],[-70.199509,46.315262],[-70.197028,46.336863],[-70.188398,46.358412],[-70.167418,46.368179],[-70.153052,46.372829],[-70.074323,46.419545],[-70.061817,46.445409],[-70.050086,46.511271],[-70.032723,46.609766],[-70.023628,46.661287],[-70.007763,46.704075],[-69.989961,46.721697],[-69.899708,46.811562],[-69.809403,46.901299],[-69.719099,46.991086],[-69.628794,47.080797],[-69.538464,47.17061],[-69.448159,47.260346],[-69.357906,47.350134],[-69.267628,47.439844],[-69.25091,47.452919],[-69.237268,47.45881],[-69.221972,47.459688],[-69.069655,47.431886],[-69.054023,47.418399],[-69.054333,47.389253],[-69.066193,47.32967],[-69.065134,47.296339],[-69.06356,47.290809],[-69.057486,47.269467],[-69.0402,47.249055],[-68.906229,47.190221],[-68.889718,47.190609],[-68.761819,47.23704],[-68.71779,47.245231],[-68.668801,47.243422],[-68.644203,47.245283],[-68.6256,47.255205],[-68.607926,47.269829],[-68.58524,47.28249],[-68.539662,47.299853],[-68.518009,47.304762],[-68.492016,47.307553],[-68.466746,47.305692],[-68.435327,47.291275],[-68.422563,47.293109],[-68.410212,47.297424],[-68.385614,47.301713],[-68.383392,47.307139],[-68.384839,47.315873],[-68.382049,47.32781],[-68.347839,47.358506],[-68.299728,47.367833],[-68.24645,47.360573],[-68.197047,47.341401],[-68.184335,47.333133],[-68.156068,47.306674],[-68.145061,47.301455],[-68.115398,47.292282],[-68.101446,47.286185],[-68.039382,47.245231],[-67.993184,47.223217],[-67.962436,47.197689],[-67.953703,47.18663],[-67.949982,47.172936],[-67.943419,47.164538],[-67.899132,47.138778],[-67.870607,47.107358],[-67.854742,47.09785],[-67.813556,47.081908],[-67.808699,47.075138],[-67.805185,47.035631],[-67.802549,46.901247],[-67.800017,46.766785],[-67.797433,46.632297],[-67.794849,46.497861],[-67.792317,46.363476],[-67.789733,46.229014],[-67.78715,46.094552],[-67.784566,45.960142],[-67.782757,45.95053],[-67.776556,45.942933],[-67.767461,45.935957],[-67.759658,45.928567],[-67.757849,45.919472],[-67.769425,45.903969],[-67.787356,45.890017],[-67.799242,45.875651],[-67.792627,45.858907],[-67.776091,45.840821],[-67.772835,45.828057],[-67.779863,45.815706],[-67.794126,45.799169],[-67.80627,45.781754],[-67.811127,45.76651],[-67.810816,45.762414],[-67.817811,45.754896],[-67.821785,45.740767],[-67.827673,45.739001],[-67.868884,45.744593],[-67.856815,45.723694],[-67.835768,45.703971],[-67.793821,45.676301],[-67.733034,45.651869],[-67.723173,45.645393],[-67.711546,45.642155],[-67.697564,45.64922],[-67.66695,45.620077],[-67.649435,45.611247],[-67.603073,45.605948],[-67.561862,45.596234],[-67.54052,45.593879],[-67.442056,45.603593],[-67.440939,45.604586],[-67.431306,45.597941],[-67.422107,45.568796],[-67.42619,45.533449],[-67.443036,45.522184],[-67.467531,45.508283],[-67.493214,45.493142],[-67.48231,45.455521],[-67.428825,45.38705],[-67.434561,45.350308],[-67.459056,45.318424],[-67.468668,45.301835],[-67.475024,45.282353],[-67.471303,45.266282],[-67.427585,45.236568],[-67.390533,45.193108],[-67.356272,45.165926],[-67.31922,45.153886],[-67.284648,45.169699],[-67.279584,45.179052],[-67.279222,45.187372],[-67.277207,45.195072],[-67.267336,45.202513],[-67.254986,45.205045],[-67.242428,45.202565],[-67.219071,45.192126],[-67.206166,45.189401],[-67.176015,45.178656],[-67.191274,45.180365],[-67.204376,45.178209],[-67.204724,45.177791],[-67.152423,45.148932],[-67.048033,45.043407],[-66.962727,45.047088],[-66.857192,44.968696],[-66.897268,44.817275],[-67.2159,44.593511],[-67.122366,44.423624],[-67.68447,44.192544],[-67.459678,40.781645],[-76.607854,32.495823],[-76.798479,32.713735],[-78.561892,29.037718],[-78.892446,29.039659],[-79.762295,26.719312],[-80.026352,24.932961],[-82.368794,23.994833],[-83.806281,29.068506],[-87.460772,29.089961],[-87.922646,28.666131],[-90.461001,28.246758],[-91.787336,29.11536],[-93.311871,29.12431],[-96.423449,26.057857],[-97.129057,25.991017],[-97.129509,25.966833],[-97.139358,25.965876],[-97.202171,25.960893],[-97.202176,25.960857],[-97.204941,25.960639],[-97.253051,25.963481],[-97.266358,25.960639],[-97.2692,25.944361],[-97.287649,25.928651],[-97.310981,25.922088],[-97.328447,25.933302],[-97.351107,25.918419],[-97.355112,25.912786],[-97.35227,25.894493],[-97.345165,25.871704],[-97.345733,25.852222],[-97.36599,25.843902],[-97.376015,25.846744],[-97.380124,25.853203],[-97.383121,25.860541],[-97.389891,25.865657],[-97.397823,25.865812],[-97.399476,25.861162],[-97.39989,25.855115],[-97.404179,25.851395],[-97.425418,25.854857],[-97.435727,25.869275],[-97.441309,25.884933],[-97.448259,25.892322],[-97.469421,25.892943],[-97.486319,25.895733],[-97.502209,25.901883],[-97.52027,25.912786],[-97.565177,25.954748],[-97.594322,25.966375],[-97.604787,25.979966],[-97.613055,25.995985],[-97.622641,26.00906],[-97.641451,26.022495],[-97.659874,26.03066],[-97.679614,26.034639],[-97.766948,26.039652],[-97.780306,26.043218],[-97.782321,26.058617],[-97.80201,26.063733],[-97.878181,26.063733],[-97.941666,26.056809],[-97.999233,26.064302],[-98.013057,26.063682],[-98.044166,26.048799],[-98.065457,26.042184],[-98.075146,26.046628],[-98.083311,26.070916],[-98.103103,26.074947],[-98.150232,26.063682],[-98.185062,26.065232],[-98.222656,26.075412],[-98.300429,26.111431],[-98.309809,26.121094],[-98.333037,26.15303],[-98.339264,26.159851],[-98.365774,26.160161],[-98.377272,26.163572],[-98.377272,26.173649],[-98.36934,26.19401],[-98.397193,26.201141],[-98.428845,26.217729],[-98.456544,26.225946],[-98.472383,26.207652],[-98.49295,26.230596],[-98.521527,26.240932],[-98.552791,26.248321],[-98.581627,26.262274],[-98.640564,26.24181],[-98.653663,26.244291],[-98.664696,26.250647],[-98.685289,26.268475],[-98.693325,26.270542],[-98.702239,26.271628],[-98.704255,26.27664],[-98.691465,26.290231],[-98.701413,26.299119],[-98.713169,26.303357],[-98.726217,26.30439],[-98.739911,26.303253],[-98.735932,26.320048],[-98.746397,26.332141],[-98.780839,26.351674],[-98.795851,26.368314],[-98.801329,26.372138],[-98.810295,26.372448],[-98.817323,26.368521],[-98.825023,26.366454],[-98.836081,26.372138],[-98.842334,26.365834],[-98.850835,26.364077],[-98.860524,26.366299],[-98.870214,26.372138],[-98.893029,26.367849],[-98.9299,26.39224],[-98.945377,26.378288],[-98.954136,26.393946],[-98.962844,26.399527],[-98.986951,26.400095],[-99.004056,26.393842],[-99.010515,26.392602],[-99.016432,26.394462],[-99.022995,26.403351],[-99.027878,26.406245],[-99.047645,26.406968],[-99.066351,26.404746],[-99.085498,26.40764],[-99.106427,26.423039],[-99.108907,26.434253],[-99.102525,26.446966],[-99.09374,26.459781],[-99.089373,26.47115],[-99.091492,26.484018],[-99.10299,26.512078],[-99.115108,26.525617],[-99.140946,26.531405],[-99.164873,26.540448],[-99.17128,26.563961],[-99.171548,26.56583],[-99.213953,26.568537],[-99.242801,26.579723],[-99.254575,26.6018],[-99.258844,26.614752],[-99.277683,26.638007],[-99.281951,26.649781],[-99.277389,26.657729],[-99.26635,26.653314],[-99.252662,26.644483],[-99.240299,26.639184],[-99.244861,26.652431],[-99.240299,26.697763],[-99.242507,26.713658],[-99.252368,26.743683],[-99.254575,26.75899],[-99.252368,26.799024],[-99.254575,26.810504],[-99.257666,26.813153],[-99.262229,26.814036],[-99.266497,26.817863],[-99.268263,26.827872],[-99.271649,26.832876],[-99.289458,26.84465],[-99.308444,26.830521],[-99.316539,26.822279],[-99.323457,26.810504],[-99.328166,26.797258],[-99.329197,26.789016],[-99.331699,26.78254],[-99.340383,26.77312],[-99.366728,26.761345],[-99.380269,26.777241],[-99.391896,26.796963],[-99.412207,26.796963],[-99.410883,26.808149],[-99.405437,26.818452],[-99.396606,26.824928],[-99.384979,26.824928],[-99.377178,26.816686],[-99.374823,26.804028],[-99.374234,26.791076],[-99.371291,26.783128],[-99.360694,26.780479],[-99.359369,26.790487],[-99.36452,26.810504],[-99.357897,26.822279],[-99.351274,26.83111],[-99.346123,26.840824],[-99.344062,26.855247],[-99.348772,26.899696],[-99.355101,26.920302],[-99.36452,26.934726],[-99.403377,26.952093],[-99.413974,26.964162],[-99.401758,26.985651],[-99.399991,26.999192],[-99.418831,27.007728],[-99.441938,27.013615],[-99.453271,27.019797],[-99.455332,27.025979],[-99.464751,27.039225],[-99.466959,27.047467],[-99.462544,27.057181],[-99.461635,27.056839],[-99.461728,27.056954],[-99.442039,27.089614],[-99.439404,27.098347],[-99.441419,27.107494],[-99.445734,27.114728],[-99.450178,27.120465],[-99.452452,27.125012],[-99.450333,27.145166],[-99.435786,27.188419],[-99.431988,27.207591],[-99.434029,27.22697],[-99.440902,27.244798],[-99.451832,27.26118],[-99.46612,27.276527],[-99.468963,27.278233],[-99.480409,27.283297],[-99.482941,27.286708],[-99.484879,27.294821],[-99.486584,27.297611],[-99.493199,27.30128],[-99.521362,27.311254],[-99.5148,27.321796],[-99.497591,27.338798],[-99.494026,27.348203],[-99.492889,27.358848],[-99.487721,27.37187],[-99.484621,27.391766],[-99.475706,27.414762],[-99.472916,27.426647],[-99.473639,27.463803],[-99.472916,27.468299],[-99.47643,27.48251],[-99.480409,27.490778],[-99.48829,27.494654],[-99.503689,27.495584],[-99.509503,27.500028],[-99.510071,27.510518],[-99.507074,27.533437],[-99.507203,27.57377],[-99.515006,27.588601],[-99.535031,27.604828],[-99.55503,27.613509],[-99.572264,27.61847],[-99.578232,27.622811],[-99.590247,27.642061],[-99.600169,27.646427],[-99.612442,27.643637],[-99.633526,27.633069],[-99.644869,27.632733],[-99.648642,27.636919],[-99.658693,27.654024],[-99.664739,27.659398],[-99.70037,27.659191],[-99.705692,27.66317],[-99.710674,27.670116],[-99.723056,27.687381],[-99.730652,27.691825],[-99.734037,27.702031],[-99.736311,27.713607],[-99.740445,27.722159],[-99.747344,27.726009],[-99.765198,27.731177],[-99.774577,27.735828],[-99.78685,27.748488],[-99.795428,27.761924],[-99.806963,27.771423],[-99.808167,27.772414],[-99.83292,27.776755],[-99.832971,27.782181],[-99.844779,27.793576],[-99.858241,27.803524],[-99.863357,27.804661],[-99.864727,27.814324],[-99.861858,27.83608],[-99.863357,27.845666],[-99.870928,27.854477],[-99.880204,27.859231],[-99.888007,27.864812],[-99.891288,27.876026],[-99.882684,27.89158],[-99.878808,27.901838],[-99.88134,27.906463],[-99.896766,27.912923],[-99.914336,27.928245],[-99.929916,27.946331],[-99.939683,27.961085],[-99.928289,27.975761],[-99.940717,27.983254],[-99.961852,27.987492],[-99.976606,27.992453],[-99.991127,28.007801],[-100.000584,28.02041],[-100.007457,28.033561],[-100.014123,28.050459],[-100.013503,28.056971],[-100.010506,28.063611],[-100.010196,28.068882],[-100.017585,28.070949],[-100.031538,28.081801],[-100.045077,28.095289],[-100.048023,28.102523],[-100.048901,28.115959],[-100.056498,28.137922],[-100.074895,28.154407],[-100.172873,28.198538],[-100.189203,28.201329],[-100.197626,28.207168],[-100.201192,28.220346],[-100.202949,28.234428],[-100.205946,28.242877],[-100.212819,28.245073],[-100.240724,28.249698],[-100.257932,28.260524],[-100.275089,28.277242],[-100.284339,28.296517],[-100.277931,28.314888],[-100.278551,28.331088],[-100.293899,28.353413],[-100.322631,28.386899],[-100.331675,28.422013],[-100.336326,28.458574],[-100.340201,28.464259],[-100.348315,28.470253],[-100.355549,28.478185],[-100.35679,28.489322],[-100.351622,28.496711],[-100.322631,28.510406],[-100.364024,28.524797],[-100.38423,28.537174],[-100.397769,28.557586],[-100.398751,28.568645],[-100.397097,28.592726],[-100.401438,28.60226],[-100.411463,28.609314],[-100.434821,28.619133],[-100.44619,28.626497],[-100.444898,28.643782],[-100.481381,28.686054],[-100.493939,28.708378],[-100.519054,28.804961],[-100.524996,28.814831],[-100.529285,28.819947],[-100.534453,28.830231],[-100.538639,28.835631],[-100.54515,28.83899],[-100.559671,28.839378],[-100.566234,28.842504],[-100.569696,28.84961],[-100.56334,28.86209],[-100.566234,28.869789],[-100.571763,28.8732],[-100.586543,28.879789],[-100.58954,28.883458],[-100.594966,28.899322],[-100.606955,28.910123],[-100.618841,28.917926],[-100.624318,28.924721],[-100.624783,28.93777],[-100.626696,28.948338],[-100.630778,28.956683],[-100.637909,28.962884],[-100.628918,28.98433],[-100.632793,29.005156],[-100.652224,29.044817],[-100.660854,29.102669],[-100.668967,29.116208],[-100.678165,29.119412],[-100.690826,29.121014],[-100.70204,29.12365],[-100.706846,29.130187],[-100.70974,29.135561],[-100.762501,29.173776],[-100.770098,29.187289],[-100.762088,29.208658],[-100.783172,29.243074],[-100.796143,29.257673],[-100.81609,29.270773],[-100.86389,29.290616],[-100.871797,29.296456],[-100.891227,29.318547],[-100.91474,29.337048],[-100.987397,29.366322],[-100.998301,29.372472],[-101.008068,29.380585],[-101.016232,29.390068],[-101.022175,29.40048],[-101.025948,29.414356],[-101.029617,29.442984],[-101.037782,29.460063],[-101.039026,29.460452],[-101.040188,29.457132],[-101.045487,29.451245],[-101.060205,29.449184],[-101.067711,29.45095],[-101.076101,29.453894],[-101.085962,29.454483],[-101.098031,29.449184],[-101.113043,29.466552],[-101.142774,29.475383],[-101.174124,29.475971],[-101.193699,29.469495],[-101.198703,29.473911],[-101.198851,29.476854],[-101.184132,29.497754],[-101.184868,29.512767],[-101.195171,29.521892],[-101.214157,29.518065],[-101.245213,29.493044],[-101.265818,29.487157],[-101.290545,29.49746],[-101.297315,29.503936],[-101.300995,29.512767],[-101.294372,29.520715],[-101.273177,29.524247],[-101.259195,29.533372],[-101.243888,29.554861],[-101.231966,29.580176],[-101.227845,29.599899],[-101.239178,29.616677],[-101.26052,29.613439],[-101.281272,29.597249],[-101.290545,29.575761],[-101.295255,29.570168],[-101.306146,29.574583],[-101.317626,29.584003],[-101.323955,29.592539],[-101.323661,29.603137],[-101.318804,29.616383],[-101.311445,29.628158],[-101.303497,29.634045],[-101.303669,29.631411],[-101.302727,29.633851],[-101.301073,29.649509],[-101.30978,29.654548],[-101.336239,29.654315],[-101.349029,29.660103],[-101.357684,29.667441],[-101.364351,29.676665],[-101.376624,29.700643],[-101.383368,29.718497],[-101.39962,29.740718],[-101.406545,29.752888],[-101.409309,29.765781],[-101.405098,29.778442],[-101.414012,29.774411],[-101.424218,29.771414],[-101.435096,29.770122],[-101.446103,29.771052],[-101.455689,29.77591],[-101.462433,29.788932],[-101.470908,29.791516],[-101.490286,29.785547],[-101.505763,29.773894],[-101.521809,29.765936],[-101.542893,29.771052],[-101.539689,29.779191],[-101.530516,29.796477],[-101.528604,29.801438],[-101.531912,29.811101],[-101.539172,29.817974],[-101.546458,29.820145],[-101.549766,29.815701],[-101.553977,29.796684],[-101.564907,29.786478],[-101.580281,29.781568],[-101.632216,29.775651],[-101.794531,29.795857],[-101.80298,29.801438],[-101.805978,29.811928],[-101.812695,29.812032],[-101.82409,29.805184],[-101.857602,29.805184],[-101.877524,29.810843],[-101.88742,29.81229],[-101.895455,29.808621],[-101.90238,29.803247],[-101.910881,29.799888],[-101.920157,29.798182],[-101.929613,29.797717],[-101.942662,29.803608],[-101.957054,29.814047],[-101.972246,29.818181],[-101.98793,29.805184],[-102.014595,29.810998],[-102.109344,29.80211],[-102.145647,29.815701],[-102.157248,29.824537],[-102.203679,29.846138],[-102.239775,29.849135],[-102.253444,29.855285],[-102.258276,29.873475],[-102.276181,29.869547],[-102.289023,29.878126],[-102.302175,29.889391],[-102.321011,29.893939],[-102.330235,29.888926],[-102.339769,29.870633],[-102.351061,29.866602],[-102.36323,29.864276],[-102.370723,29.857765],[-102.374547,29.848102],[-102.376589,29.821488],[-102.380051,29.811386],[-102.404132,29.780793],[-102.406096,29.777279],[-102.515288,29.784721],[-102.523066,29.782318],[-102.531127,29.769915],[-102.54154,29.762474],[-102.543349,29.760123],[-102.546578,29.757875],[-102.553141,29.756738],[-102.558309,29.759089],[-102.562882,29.769347],[-102.566758,29.771052],[-102.58531,29.764696],[-102.621225,29.747281],[-102.638743,29.743715],[-102.676054,29.74449],[-102.683469,29.743715],[-102.69104,29.736817],[-102.693624,29.729401],[-102.694709,29.720616],[-102.697758,29.709557],[-102.726748,29.664495],[-102.73127,29.650594],[-102.735507,29.649509],[-102.751656,29.622457],[-102.75176,29.620157],[-102.761346,29.603414],[-102.767598,29.59729],[-102.779665,29.592303],[-102.774084,29.579617],[-102.776461,29.575948],[-102.785892,29.571814],[-102.78075,29.558249],[-102.786512,29.550497],[-102.795478,29.54427],[-102.827311,29.470502],[-102.833951,29.461355],[-102.839067,29.45195],[-102.841134,29.438308],[-102.838705,29.426939],[-102.834984,29.415699],[-102.835191,29.403839],[-102.844545,29.390533],[-102.845578,29.384719],[-102.838033,29.370534],[-102.837672,29.366322],[-102.84656,29.361749],[-102.853872,29.361],[-102.859867,29.361155],[-102.864957,29.359527],[-102.876972,29.350871],[-102.883069,29.343766],[-102.885188,29.333379],[-102.885498,29.314801],[-102.899399,29.276095],[-102.899709,29.2639],[-102.892139,29.254391],[-102.867954,29.240387],[-102.858781,29.229147],[-102.869866,29.224781],[-102.896893,29.220285],[-102.942265,29.190209],[-102.947536,29.182018],[-102.969757,29.192845],[-102.988386,29.177135],[-103.015826,29.126776],[-103.024275,29.116157],[-103.032621,29.110214],[-103.072541,29.091404],[-103.080758,29.085203],[-103.085589,29.07572],[-103.091532,29.057866],[-103.095356,29.060294],[-103.104684,29.057866],[-103.109205,29.023372],[-103.122771,28.996474],[-103.147989,28.985105],[-103.187108,28.990221],[-103.241756,29.003502],[-103.301545,29.002365],[-103.316247,29.010065],[-103.311514,29.026043],[-103.309994,29.031175],[-103.3248,29.026808],[-103.330484,29.023733],[-103.342602,29.041226],[-103.351671,29.039417],[-103.360534,29.029831],[-103.372083,29.023733],[-103.38663,29.028798],[-103.414639,29.052414],[-103.423605,29.057866],[-103.435697,29.061121],[-103.478537,29.08205],[-103.529748,29.126776],[-103.535588,29.135122],[-103.538223,29.142408],[-103.541711,29.148816],[-103.550238,29.154656],[-103.558015,29.156206],[-103.58499,29.154656],[-103.673125,29.173569],[-103.702477,29.187858],[-103.749476,29.222972],[-103.759062,29.226848],[-103.770767,29.229845],[-103.777718,29.235297],[-103.769424,29.257543],[-103.774229,29.267517],[-103.78366,29.274803],[-103.794177,29.277594],[-103.837038,29.279906]]],[[[178.301106,52.056551],[179.595462,52.142083],[179.825447,51.992849],[179.661729,51.485763],[179.723231,51.459963],[179.408066,51.209841],[178.411463,51.523605],[177.698335,51.877899],[177.16784,51.581866],[176.487008,52.175325],[174.484678,52.08716],[172.866263,52.207379],[172.825506,52.716846],[172.747012,52.654022],[172.08261,52.952695],[172.942925,53.183013],[173.029416,52.993628],[173.127208,52.99494],[173.143321,52.990383],[173.175059,52.971747],[173.182932,52.968373],[176.45233,52.628178],[176.468135,52.488358],[177.900385,52.488358],[178.007601,52.179677],[178.301106,52.056551]]],[[[-168.899607,65.747626],[-168.909861,65.739569],[-168.926218,65.739895],[-168.942128,65.74372],[-168.951731,65.75316],[-168.942983,65.764716],[-168.920115,65.768866],[-168.907908,65.768297],[-168.902781,65.761542],[-168.899607,65.747626]]],[[[-131.160718,54.787192],[-132.853508,54.482536],[-134.77719,54.717786],[-142.6966,55.845503],[-142.861997,49.948308],[-155.675916,51.109976],[-164.492732,50.603976],[-164.691217,50.997975],[-171.246993,49.948308],[-171.215436,50.576636],[-173.341669,50.968826],[-173.362022,51.082198],[-177.799603,51.272899],[-179.155463,50.982285],[-179.476076,52.072632],[-177.11459,52.248701],[-177.146284,52.789384],[-174.777218,52.443779],[-174.773743,52.685853],[-173.653194,52.704099],[-173.790528,53.469081],[-171.063371,53.604473],[-170.777733,59.291898],[-174.324884,60.332184],[-171.736408,62.68026],[-172.315705,62.725352],[-171.995091,63.999658],[-168.501424,65.565173],[-168.714145,65.546708],[-168.853077,68.370871],[-161.115601,72.416214],[-146.132257,70.607941],[-140.692512,69.955349],[-141.145395,69.671641],[-141.015207,69.654202],[-141.006459,69.651272],[-141.005564,69.650946],[-141.005549,69.650941],[-141.005471,69.505164],[-141.001208,60.466879],[-141.001156,60.321074],[-140.994929,60.304382],[-140.979555,60.295804],[-140.909146,60.28366],[-140.768457,60.259269],[-140.660505,60.24051],[-140.533743,60.218548],[-140.518705,60.22387],[-140.506664,60.236324],[-140.475323,60.276477],[-140.462791,60.289138],[-140.447805,60.29446],[-140.424111,60.293168],[-140.32497,60.267537],[-140.169243,60.227229],[-140.01579,60.187387],[-139.967757,60.188369],[-139.916933,60.207851],[-139.826318,60.256478],[-139.728417,60.309033],[-139.679816,60.32681],[-139.628346,60.334096],[-139.517965,60.336732],[-139.413992,60.339212],[-139.262193,60.342778],[-139.101608,60.346602],[-139.079465,60.341021],[-139.06869,60.322056],[-139.073186,60.299835],[-139.113468,60.226816],[-139.149615,60.161187],[-139.183231,60.100157],[-139.182146,60.073389],[-139.112305,60.031376],[-139.060207,60.000059],[-139.051611,59.994892],[-139.003759,59.977219],[-138.842425,59.937686],[-138.742586,59.913192],[-138.704888,59.898464],[-138.697188,59.89371],[-138.692098,59.886888],[-138.654349,59.805498],[-138.63745,59.784052],[-138.59921,59.753822],[-138.488881,59.696357],[-138.363617,59.631142],[-138.219543,59.556004],[-138.067614,59.476991],[-137.91057,59.395187],[-137.758305,59.315915],[-137.611363,59.239331],[-137.594181,59.225275],[-137.582088,59.206568],[-137.5493,59.134531],[-137.521007,59.072364],[-137.484394,58.991904],[-137.507752,58.939969],[-137.50876,58.914906],[-137.486875,58.900075],[-137.453466,58.899145],[-137.423106,58.907723],[-137.338098,58.955472],[-137.2819,58.98715],[-137.172346,59.027148],[-137.062367,59.067572],[-137.047109,59.07331],[-136.942282,59.11107],[-136.840816,59.148174],[-136.785496,59.157217],[-136.671911,59.150809],[-136.613491,59.15422],[-136.569489,59.172152],[-136.484791,59.2538],[-136.483551,59.257469],[-136.466549,59.287803],[-136.467092,59.38449],[-136.467557,59.461643],[-136.415958,59.452238],[-136.36684,59.449551],[-136.319995,59.459059],[-136.275036,59.486448],[-136.244728,59.528202],[-136.258474,59.556107],[-136.29935,59.575745],[-136.350329,59.592384],[-136.2585,59.621582],[-136.145406,59.636826],[-136.02686,59.652846],[-135.923818,59.666747],[-135.830955,59.693257],[-135.641251,59.747362],[-135.482759,59.792475],[-135.465137,59.789685],[-135.404392,59.753305],[-135.345791,59.731032],[-135.259879,59.698218],[-135.221897,59.675273],[-135.192028,59.64711],[-135.157792,59.623287],[-135.106684,59.613158],[-135.087874,59.606544],[-135.032942,59.573109],[-135.018524,59.559363],[-135.016198,59.543447],[-135.01948,59.493166],[-135.023252,59.477146],[-135.037489,59.461591],[-135.078598,59.438337],[-135.095754,59.418855],[-134.993254,59.381906],[-135.00483,59.367127],[-135.014441,59.35152],[-135.016198,59.336173],[-134.979973,59.297415],[-134.95783,59.280982],[-134.932431,59.270647],[-134.839465,59.258141],[-134.74345,59.245119],[-134.70552,59.240106],[-134.692084,59.235249],[-134.68286,59.223001],[-134.671439,59.193752],[-134.66038,59.181298],[-134.610771,59.144556],[-134.582788,59.128847],[-134.556717,59.123059],[-134.509072,59.122801],[-134.477575,59.114946],[-134.451013,59.097893],[-134.398019,59.051952],[-134.387167,59.036863],[-134.385591,59.018828],[-134.399389,58.974954],[-134.343423,58.968857],[-134.329651,58.963017],[-134.320039,58.952682],[-134.32314,58.949168],[-134.330323,58.945344],[-134.333036,58.93413],[-134.327403,58.916457],[-134.316939,58.903796],[-134.22219,58.842714],[-134.108838,58.808246],[-133.983109,58.769902],[-133.87123,58.735899],[-133.831129,58.718019],[-133.796402,58.693421],[-133.700077,58.59937],[-133.626283,58.546402],[-133.547063,58.505577],[-133.463089,58.462221],[-133.392241,58.403878],[-133.43012,58.372097],[-133.41503,58.330549],[-133.374567,58.290965],[-133.257262,58.210298],[-133.165588,58.147305],[-133.142127,58.120588],[-133.094843,58.0331],[-133.075154,58.007882],[-132.99335,57.941917],[-132.917153,57.880499],[-132.83212,57.791564],[-132.70944,57.663303],[-132.629057,57.579277],[-132.552447,57.499075],[-132.455735,57.420992],[-132.362304,57.3457],[-132.304684,57.280355],[-132.230994,57.19682],[-132.276366,57.14889],[-132.34122,57.080393],[-132.16229,57.050317],[-132.031859,57.028406],[-132.107384,56.858753],[-131.871558,56.79346],[-131.865874,56.785708],[-131.872411,56.77297],[-131.882617,56.759146],[-131.887966,56.747958],[-131.886028,56.737055],[-131.880705,56.728838],[-131.864789,56.71349],[-131.838976,56.682278],[-131.830424,56.664759],[-131.826574,56.644606],[-131.832103,56.603368],[-131.825592,56.593343],[-131.799108,56.587658],[-131.692293,56.585074],[-131.585891,56.595048],[-131.560363,56.594066],[-131.536437,56.585229],[-131.491659,56.560166],[-131.345699,56.503271],[-131.215604,56.45255],[-131.100546,56.407669],[-131.016934,56.38705],[-130.839089,56.372452],[-130.760334,56.345192],[-130.645768,56.261942],[-130.602256,56.247059],[-130.495518,56.232434],[-130.47229,56.22489],[-130.458053,56.210653],[-130.427926,56.143964],[-130.418159,56.129702],[-130.403974,56.121898],[-130.290311,56.10097],[-130.243156,56.092391],[-130.211246,56.089962],[-130.116756,56.105646],[-130.094328,56.101486],[-130.071539,56.084123],[-130.039319,56.045521],[-130.026632,56.024101],[-130.01901,56.002216],[-130.014695,55.963252],[-130.016788,55.918913],[-130.019612,55.907978],[-130.019618,55.907952],[-130.022817,55.901353],[-130.049387,55.871405],[-130.104726,55.825263],[-130.136627,55.806464],[-130.148834,55.795356],[-130.163482,55.771145],[-130.167307,55.766262],[-130.170806,55.759833],[-130.173655,55.749498],[-130.170806,55.740953],[-130.163808,55.734565],[-130.160064,55.727118],[-130.167388,55.715399],[-130.155914,55.700141],[-130.142893,55.689521],[-130.131825,55.676581],[-130.126454,55.653998],[-130.12857,55.63642],[-130.135121,55.619127],[-130.153147,55.58511],[-130.148671,55.578192],[-130.146881,55.569322],[-130.146962,55.547187],[-130.112172,55.509345],[-130.101674,55.481147],[-130.095082,55.472113],[-130.065419,55.446112],[-130.057525,55.434882],[-130.052561,55.414008],[-130.054311,55.366645],[-130.05012,55.345445],[-130.039296,55.330756],[-129.989247,55.284003],[-130.031239,55.26435],[-130.050038,55.252875],[-130.067494,55.239],[-130.078236,55.233791],[-130.100494,55.230292],[-130.104726,55.225653],[-130.105702,55.211127],[-130.10912,55.200751],[-130.115793,55.191596],[-130.126454,55.180976],[-130.151967,55.163275],[-130.159983,55.153713],[-130.167592,55.129584],[-130.173695,55.117743],[-130.200266,55.104153],[-130.211781,55.084133],[-130.228871,55.04385],[-130.238678,55.03441],[-130.261342,55.022895],[-130.269846,55.016547],[-130.275706,55.006985],[-130.286366,54.983222],[-130.294342,54.971869],[-130.326568,54.952094],[-130.335561,54.938707],[-130.365387,54.907294],[-130.385243,54.896552],[-130.430816,54.881252],[-130.488759,54.844184],[-130.580312,54.806383],[-130.597485,54.803391],[-130.71074,54.733215],[-131.160718,54.787192]]]]}}]},"featureIcons":{"circle-stroked-24":{"x":0,"y":0,"width":24,"height":24},"circle-stroked-18":{"x":24,"y":0,"width":18,"height":18},"circle-stroked-12":{"x":42,"y":0,"width":12,"height":12},"circle-24":{"x":54,"y":0,"width":24,"height":24},"circle-18":{"x":78,"y":0,"width":18,"height":18},"circle-12":{"x":96,"y":0,"width":12,"height":12},"square-stroked-24":{"x":108,"y":0,"width":24,"height":24},"square-stroked-18":{"x":132,"y":0,"width":18,"height":18},"square-stroked-12":{"x":150,"y":0,"width":12,"height":12},"square-24":{"x":162,"y":0,"width":24,"height":24},"square-18":{"x":186,"y":0,"width":18,"height":18},"square-12":{"x":204,"y":0,"width":12,"height":12},"triangle-stroked-24":{"x":216,"y":0,"width":24,"height":24},"triangle-stroked-18":{"x":240,"y":0,"width":18,"height":18},"triangle-stroked-12":{"x":258,"y":0,"width":12,"height":12},"triangle-24":{"x":0,"y":24,"width":24,"height":24},"triangle-18":{"x":24,"y":24,"width":18,"height":18},"triangle-12":{"x":42,"y":24,"width":12,"height":12},"star-stroked-24":{"x":54,"y":24,"width":24,"height":24},"star-stroked-18":{"x":78,"y":24,"width":18,"height":18},"star-stroked-12":{"x":96,"y":24,"width":12,"height":12},"star-24":{"x":108,"y":24,"width":24,"height":24},"star-18":{"x":132,"y":24,"width":18,"height":18},"star-12":{"x":150,"y":24,"width":12,"height":12},"cross-24":{"x":162,"y":24,"width":24,"height":24},"cross-18":{"x":186,"y":24,"width":18,"height":18},"cross-12":{"x":204,"y":24,"width":12,"height":12},"marker-stroked-24":{"x":216,"y":24,"width":24,"height":24},"marker-stroked-18":{"x":240,"y":24,"width":18,"height":18},"marker-stroked-12":{"x":258,"y":24,"width":12,"height":12},"marker-24":{"x":0,"y":48,"width":24,"height":24},"marker-18":{"x":24,"y":48,"width":18,"height":18},"marker-12":{"x":42,"y":48,"width":12,"height":12},"religious-jewish-24":{"x":54,"y":48,"width":24,"height":24},"religious-jewish-18":{"x":78,"y":48,"width":18,"height":18},"religious-jewish-12":{"x":96,"y":48,"width":12,"height":12},"religious-christian-24":{"x":108,"y":48,"width":24,"height":24},"religious-christian-18":{"x":132,"y":48,"width":18,"height":18},"religious-christian-12":{"x":150,"y":48,"width":12,"height":12},"religious-muslim-24":{"x":162,"y":48,"width":24,"height":24},"religious-muslim-18":{"x":186,"y":48,"width":18,"height":18},"religious-muslim-12":{"x":204,"y":48,"width":12,"height":12},"cemetery-24":{"x":216,"y":48,"width":24,"height":24},"cemetery-18":{"x":240,"y":48,"width":18,"height":18},"cemetery-12":{"x":258,"y":48,"width":12,"height":12},"rocket-24":{"x":0,"y":72,"width":24,"height":24},"rocket-18":{"x":24,"y":72,"width":18,"height":18},"rocket-12":{"x":42,"y":72,"width":12,"height":12},"airport-24":{"x":54,"y":72,"width":24,"height":24},"airport-18":{"x":78,"y":72,"width":18,"height":18},"airport-12":{"x":96,"y":72,"width":12,"height":12},"heliport-24":{"x":108,"y":72,"width":24,"height":24},"heliport-18":{"x":132,"y":72,"width":18,"height":18},"heliport-12":{"x":150,"y":72,"width":12,"height":12},"rail-24":{"x":162,"y":72,"width":24,"height":24},"rail-18":{"x":186,"y":72,"width":18,"height":18},"rail-12":{"x":204,"y":72,"width":12,"height":12},"rail-metro-24":{"x":216,"y":72,"width":24,"height":24},"rail-metro-18":{"x":240,"y":72,"width":18,"height":18},"rail-metro-12":{"x":258,"y":72,"width":12,"height":12},"rail-light-24":{"x":0,"y":96,"width":24,"height":24},"rail-light-18":{"x":24,"y":96,"width":18,"height":18},"rail-light-12":{"x":42,"y":96,"width":12,"height":12},"bus-24":{"x":54,"y":96,"width":24,"height":24},"bus-18":{"x":78,"y":96,"width":18,"height":18},"bus-12":{"x":96,"y":96,"width":12,"height":12},"fuel-24":{"x":108,"y":96,"width":24,"height":24},"fuel-18":{"x":132,"y":96,"width":18,"height":18},"fuel-12":{"x":150,"y":96,"width":12,"height":12},"parking-24":{"x":162,"y":96,"width":24,"height":24},"parking-18":{"x":186,"y":96,"width":18,"height":18},"parking-12":{"x":204,"y":96,"width":12,"height":12},"parking-garage-24":{"x":216,"y":96,"width":24,"height":24},"parking-garage-18":{"x":240,"y":96,"width":18,"height":18},"parking-garage-12":{"x":258,"y":96,"width":12,"height":12},"airfield-24":{"x":0,"y":120,"width":24,"height":24},"airfield-18":{"x":24,"y":120,"width":18,"height":18},"airfield-12":{"x":42,"y":120,"width":12,"height":12},"roadblock-24":{"x":54,"y":120,"width":24,"height":24},"roadblock-18":{"x":78,"y":120,"width":18,"height":18},"roadblock-12":{"x":96,"y":120,"width":12,"height":12},"ferry-24":{"x":108,"y":120,"width":24,"height":24},"ferry-18":{"x":132,"y":120,"width":18,"height":18},"ferry-12":{"x":150,"y":120,"width":12,"height":12},"harbor-24":{"x":162,"y":120,"width":24,"height":24},"harbor-18":{"x":186,"y":120,"width":18,"height":18},"harbor-12":{"x":204,"y":120,"width":12,"height":12},"bicycle-24":{"x":216,"y":120,"width":24,"height":24},"bicycle-18":{"x":240,"y":120,"width":18,"height":18},"bicycle-12":{"x":258,"y":120,"width":12,"height":12},"park-24":{"x":0,"y":144,"width":24,"height":24},"park-18":{"x":24,"y":144,"width":18,"height":18},"park-12":{"x":42,"y":144,"width":12,"height":12},"park2-24":{"x":54,"y":144,"width":24,"height":24},"park2-18":{"x":78,"y":144,"width":18,"height":18},"park2-12":{"x":96,"y":144,"width":12,"height":12},"museum-24":{"x":108,"y":144,"width":24,"height":24},"museum-18":{"x":132,"y":144,"width":18,"height":18},"museum-12":{"x":150,"y":144,"width":12,"height":12},"lodging-24":{"x":162,"y":144,"width":24,"height":24},"lodging-18":{"x":186,"y":144,"width":18,"height":18},"lodging-12":{"x":204,"y":144,"width":12,"height":12},"monument-24":{"x":216,"y":144,"width":24,"height":24},"monument-18":{"x":240,"y":144,"width":18,"height":18},"monument-12":{"x":258,"y":144,"width":12,"height":12},"zoo-24":{"x":0,"y":168,"width":24,"height":24},"zoo-18":{"x":24,"y":168,"width":18,"height":18},"zoo-12":{"x":42,"y":168,"width":12,"height":12},"garden-24":{"x":54,"y":168,"width":24,"height":24},"garden-18":{"x":78,"y":168,"width":18,"height":18},"garden-12":{"x":96,"y":168,"width":12,"height":12},"campsite-24":{"x":108,"y":168,"width":24,"height":24},"campsite-18":{"x":132,"y":168,"width":18,"height":18},"campsite-12":{"x":150,"y":168,"width":12,"height":12},"theatre-24":{"x":162,"y":168,"width":24,"height":24},"theatre-18":{"x":186,"y":168,"width":18,"height":18},"theatre-12":{"x":204,"y":168,"width":12,"height":12},"art-gallery-24":{"x":216,"y":168,"width":24,"height":24},"art-gallery-18":{"x":240,"y":168,"width":18,"height":18},"art-gallery-12":{"x":258,"y":168,"width":12,"height":12},"pitch-24":{"x":0,"y":192,"width":24,"height":24},"pitch-18":{"x":24,"y":192,"width":18,"height":18},"pitch-12":{"x":42,"y":192,"width":12,"height":12},"soccer-24":{"x":54,"y":192,"width":24,"height":24},"soccer-18":{"x":78,"y":192,"width":18,"height":18},"soccer-12":{"x":96,"y":192,"width":12,"height":12},"america-football-24":{"x":108,"y":192,"width":24,"height":24},"america-football-18":{"x":132,"y":192,"width":18,"height":18},"america-football-12":{"x":150,"y":192,"width":12,"height":12},"tennis-24":{"x":162,"y":192,"width":24,"height":24},"tennis-18":{"x":186,"y":192,"width":18,"height":18},"tennis-12":{"x":204,"y":192,"width":12,"height":12},"basketball-24":{"x":216,"y":192,"width":24,"height":24},"basketball-18":{"x":240,"y":192,"width":18,"height":18},"basketball-12":{"x":258,"y":192,"width":12,"height":12},"baseball-24":{"x":0,"y":216,"width":24,"height":24},"baseball-18":{"x":24,"y":216,"width":18,"height":18},"baseball-12":{"x":42,"y":216,"width":12,"height":12},"golf-24":{"x":54,"y":216,"width":24,"height":24},"golf-18":{"x":78,"y":216,"width":18,"height":18},"golf-12":{"x":96,"y":216,"width":12,"height":12},"swimming-24":{"x":108,"y":216,"width":24,"height":24},"swimming-18":{"x":132,"y":216,"width":18,"height":18},"swimming-12":{"x":150,"y":216,"width":12,"height":12},"cricket-24":{"x":162,"y":216,"width":24,"height":24},"cricket-18":{"x":186,"y":216,"width":18,"height":18},"cricket-12":{"x":204,"y":216,"width":12,"height":12},"skiing-24":{"x":216,"y":216,"width":24,"height":24},"skiing-18":{"x":240,"y":216,"width":18,"height":18},"skiing-12":{"x":258,"y":216,"width":12,"height":12},"school-24":{"x":0,"y":240,"width":24,"height":24},"school-18":{"x":24,"y":240,"width":18,"height":18},"school-12":{"x":42,"y":240,"width":12,"height":12},"college-24":{"x":54,"y":240,"width":24,"height":24},"college-18":{"x":78,"y":240,"width":18,"height":18},"college-12":{"x":96,"y":240,"width":12,"height":12},"library-24":{"x":108,"y":240,"width":24,"height":24},"library-18":{"x":132,"y":240,"width":18,"height":18},"library-12":{"x":150,"y":240,"width":12,"height":12},"post-24":{"x":162,"y":240,"width":24,"height":24},"post-18":{"x":186,"y":240,"width":18,"height":18},"post-12":{"x":204,"y":240,"width":12,"height":12},"fire-station-24":{"x":216,"y":240,"width":24,"height":24},"fire-station-18":{"x":240,"y":240,"width":18,"height":18},"fire-station-12":{"x":258,"y":240,"width":12,"height":12},"town-hall-24":{"x":0,"y":264,"width":24,"height":24},"town-hall-18":{"x":24,"y":264,"width":18,"height":18},"town-hall-12":{"x":42,"y":264,"width":12,"height":12},"police-24":{"x":54,"y":264,"width":24,"height":24},"police-18":{"x":78,"y":264,"width":18,"height":18},"police-12":{"x":96,"y":264,"width":12,"height":12},"prison-24":{"x":108,"y":264,"width":24,"height":24},"prison-18":{"x":132,"y":264,"width":18,"height":18},"prison-12":{"x":150,"y":264,"width":12,"height":12},"embassy-24":{"x":162,"y":264,"width":24,"height":24},"embassy-18":{"x":186,"y":264,"width":18,"height":18},"embassy-12":{"x":204,"y":264,"width":12,"height":12},"beer-24":{"x":216,"y":264,"width":24,"height":24},"beer-18":{"x":240,"y":264,"width":18,"height":18},"beer-12":{"x":258,"y":264,"width":12,"height":12},"restaurant-24":{"x":0,"y":288,"width":24,"height":24},"restaurant-18":{"x":24,"y":288,"width":18,"height":18},"restaurant-12":{"x":42,"y":288,"width":12,"height":12},"cafe-24":{"x":54,"y":288,"width":24,"height":24},"cafe-18":{"x":78,"y":288,"width":18,"height":18},"cafe-12":{"x":96,"y":288,"width":12,"height":12},"shop-24":{"x":108,"y":288,"width":24,"height":24},"shop-18":{"x":132,"y":288,"width":18,"height":18},"shop-12":{"x":150,"y":288,"width":12,"height":12},"fast-food-24":{"x":162,"y":288,"width":24,"height":24},"fast-food-18":{"x":186,"y":288,"width":18,"height":18},"fast-food-12":{"x":204,"y":288,"width":12,"height":12},"bar-24":{"x":216,"y":288,"width":24,"height":24},"bar-18":{"x":240,"y":288,"width":18,"height":18},"bar-12":{"x":258,"y":288,"width":12,"height":12},"bank-24":{"x":0,"y":312,"width":24,"height":24},"bank-18":{"x":24,"y":312,"width":18,"height":18},"bank-12":{"x":42,"y":312,"width":12,"height":12},"grocery-24":{"x":54,"y":312,"width":24,"height":24},"grocery-18":{"x":78,"y":312,"width":18,"height":18},"grocery-12":{"x":96,"y":312,"width":12,"height":12},"cinema-24":{"x":108,"y":312,"width":24,"height":24},"cinema-18":{"x":132,"y":312,"width":18,"height":18},"cinema-12":{"x":150,"y":312,"width":12,"height":12},"pharmacy-24":{"x":162,"y":312,"width":24,"height":24},"pharmacy-18":{"x":186,"y":312,"width":18,"height":18},"pharmacy-12":{"x":204,"y":312,"width":12,"height":12},"hospital-24":{"x":216,"y":312,"width":24,"height":24},"hospital-18":{"x":240,"y":312,"width":18,"height":18},"hospital-12":{"x":258,"y":312,"width":12,"height":12},"danger-24":{"x":0,"y":336,"width":24,"height":24},"danger-18":{"x":24,"y":336,"width":18,"height":18},"danger-12":{"x":42,"y":336,"width":12,"height":12},"industrial-24":{"x":54,"y":336,"width":24,"height":24},"industrial-18":{"x":78,"y":336,"width":18,"height":18},"industrial-12":{"x":96,"y":336,"width":12,"height":12},"warehouse-24":{"x":108,"y":336,"width":24,"height":24},"warehouse-18":{"x":132,"y":336,"width":18,"height":18},"warehouse-12":{"x":150,"y":336,"width":12,"height":12},"commercial-24":{"x":162,"y":336,"width":24,"height":24},"commercial-18":{"x":186,"y":336,"width":18,"height":18},"commercial-12":{"x":204,"y":336,"width":12,"height":12},"building-24":{"x":216,"y":336,"width":24,"height":24},"building-18":{"x":240,"y":336,"width":18,"height":18},"building-12":{"x":258,"y":336,"width":12,"height":12},"place-of-worship-24":{"x":0,"y":360,"width":24,"height":24},"place-of-worship-18":{"x":24,"y":360,"width":18,"height":18},"place-of-worship-12":{"x":42,"y":360,"width":12,"height":12},"alcohol-shop-24":{"x":54,"y":360,"width":24,"height":24},"alcohol-shop-18":{"x":78,"y":360,"width":18,"height":18},"alcohol-shop-12":{"x":96,"y":360,"width":12,"height":12},"logging-24":{"x":108,"y":360,"width":24,"height":24},"logging-18":{"x":132,"y":360,"width":18,"height":18},"logging-12":{"x":150,"y":360,"width":12,"height":12},"oil-well-24":{"x":162,"y":360,"width":24,"height":24},"oil-well-18":{"x":186,"y":360,"width":18,"height":18},"oil-well-12":{"x":204,"y":360,"width":12,"height":12},"slaughterhouse-24":{"x":216,"y":360,"width":24,"height":24},"slaughterhouse-18":{"x":240,"y":360,"width":18,"height":18},"slaughterhouse-12":{"x":258,"y":360,"width":12,"height":12},"dam-24":{"x":0,"y":384,"width":24,"height":24},"dam-18":{"x":24,"y":384,"width":18,"height":18},"dam-12":{"x":42,"y":384,"width":12,"height":12},"water-24":{"x":54,"y":384,"width":24,"height":24},"water-18":{"x":78,"y":384,"width":18,"height":18},"water-12":{"x":96,"y":384,"width":12,"height":12},"wetland-24":{"x":108,"y":384,"width":24,"height":24},"wetland-18":{"x":132,"y":384,"width":18,"height":18},"wetland-12":{"x":150,"y":384,"width":12,"height":12},"disability-24":{"x":162,"y":384,"width":24,"height":24},"disability-18":{"x":186,"y":384,"width":18,"height":18},"disability-12":{"x":204,"y":384,"width":12,"height":12},"telephone-24":{"x":216,"y":384,"width":24,"height":24},"telephone-18":{"x":240,"y":384,"width":18,"height":18},"telephone-12":{"x":258,"y":384,"width":12,"height":12},"emergency-telephone-24":{"x":0,"y":408,"width":24,"height":24},"emergency-telephone-18":{"x":24,"y":408,"width":18,"height":18},"emergency-telephone-12":{"x":42,"y":408,"width":12,"height":12},"toilets-24":{"x":54,"y":408,"width":24,"height":24},"toilets-18":{"x":78,"y":408,"width":18,"height":18},"toilets-12":{"x":96,"y":408,"width":12,"height":12},"waste-basket-24":{"x":108,"y":408,"width":24,"height":24},"waste-basket-18":{"x":132,"y":408,"width":18,"height":18},"waste-basket-12":{"x":150,"y":408,"width":12,"height":12},"music-24":{"x":162,"y":408,"width":24,"height":24},"music-18":{"x":186,"y":408,"width":18,"height":18},"music-12":{"x":204,"y":408,"width":12,"height":12},"land-use-24":{"x":216,"y":408,"width":24,"height":24},"land-use-18":{"x":240,"y":408,"width":18,"height":18},"land-use-12":{"x":258,"y":408,"width":12,"height":12},"city-24":{"x":0,"y":432,"width":24,"height":24},"city-18":{"x":24,"y":432,"width":18,"height":18},"city-12":{"x":42,"y":432,"width":12,"height":12},"town-24":{"x":54,"y":432,"width":24,"height":24},"town-18":{"x":78,"y":432,"width":18,"height":18},"town-12":{"x":96,"y":432,"width":12,"height":12},"village-24":{"x":108,"y":432,"width":24,"height":24},"village-18":{"x":132,"y":432,"width":18,"height":18},"village-12":{"x":150,"y":432,"width":12,"height":12},"farm-24":{"x":162,"y":432,"width":24,"height":24},"farm-18":{"x":186,"y":432,"width":18,"height":18},"farm-12":{"x":204,"y":432,"width":12,"height":12},"bakery-24":{"x":216,"y":432,"width":24,"height":24},"bakery-18":{"x":240,"y":432,"width":18,"height":18},"bakery-12":{"x":258,"y":432,"width":12,"height":12},"dog-park-24":{"x":0,"y":456,"width":24,"height":24},"dog-park-18":{"x":24,"y":456,"width":18,"height":18},"dog-park-12":{"x":42,"y":456,"width":12,"height":12},"lighthouse-24":{"x":54,"y":456,"width":24,"height":24},"lighthouse-18":{"x":78,"y":456,"width":18,"height":18},"lighthouse-12":{"x":96,"y":456,"width":12,"height":12},"clothing-store-24":{"x":108,"y":456,"width":24,"height":24},"clothing-store-18":{"x":132,"y":456,"width":18,"height":18},"clothing-store-12":{"x":150,"y":456,"width":12,"height":12},"polling-place-24":{"x":162,"y":456,"width":24,"height":24},"polling-place-18":{"x":186,"y":456,"width":18,"height":18},"polling-place-12":{"x":204,"y":456,"width":12,"height":12},"playground-24":{"x":216,"y":456,"width":24,"height":24},"playground-18":{"x":240,"y":456,"width":18,"height":18},"playground-12":{"x":258,"y":456,"width":12,"height":12},"entrance-24":{"x":0,"y":480,"width":24,"height":24},"entrance-18":{"x":24,"y":480,"width":18,"height":18},"entrance-12":{"x":42,"y":480,"width":12,"height":12},"heart-24":{"x":54,"y":480,"width":24,"height":24},"heart-18":{"x":78,"y":480,"width":18,"height":18},"heart-12":{"x":96,"y":480,"width":12,"height":12},"london-underground-24":{"x":108,"y":480,"width":24,"height":24},"london-underground-18":{"x":132,"y":480,"width":18,"height":18},"london-underground-12":{"x":150,"y":480,"width":12,"height":12},"minefield-24":{"x":162,"y":480,"width":24,"height":24},"minefield-18":{"x":186,"y":480,"width":18,"height":18},"minefield-12":{"x":204,"y":480,"width":12,"height":12},"rail-underground-24":{"x":216,"y":480,"width":24,"height":24},"rail-underground-18":{"x":240,"y":480,"width":18,"height":18},"rail-underground-12":{"x":258,"y":480,"width":12,"height":12},"rail-above-24":{"x":0,"y":504,"width":24,"height":24},"rail-above-18":{"x":24,"y":504,"width":18,"height":18},"rail-above-12":{"x":42,"y":504,"width":12,"height":12},"camera-24":{"x":54,"y":504,"width":24,"height":24},"camera-18":{"x":78,"y":504,"width":18,"height":18},"camera-12":{"x":96,"y":504,"width":12,"height":12},"laundry-24":{"x":108,"y":504,"width":24,"height":24},"laundry-18":{"x":132,"y":504,"width":18,"height":18},"laundry-12":{"x":150,"y":504,"width":12,"height":12},"car-24":{"x":162,"y":504,"width":24,"height":24},"car-18":{"x":186,"y":504,"width":18,"height":18},"car-12":{"x":204,"y":504,"width":12,"height":12},"suitcase-24":{"x":216,"y":504,"width":24,"height":24},"suitcase-18":{"x":240,"y":504,"width":18,"height":18},"suitcase-12":{"x":258,"y":504,"width":12,"height":12},"hairdresser-24":{"x":0,"y":528,"width":24,"height":24},"hairdresser-18":{"x":24,"y":528,"width":18,"height":18},"hairdresser-12":{"x":42,"y":528,"width":12,"height":12},"chemist-24":{"x":54,"y":528,"width":24,"height":24},"chemist-18":{"x":78,"y":528,"width":18,"height":18},"chemist-12":{"x":96,"y":528,"width":12,"height":12},"mobilephone-24":{"x":108,"y":528,"width":24,"height":24},"mobilephone-18":{"x":132,"y":528,"width":18,"height":18},"mobilephone-12":{"x":150,"y":528,"width":12,"height":12},"scooter-24":{"x":162,"y":528,"width":24,"height":24},"scooter-18":{"x":186,"y":528,"width":18,"height":18},"scooter-12":{"x":204,"y":528,"width":12,"height":12},"gift-24":{"x":216,"y":528,"width":24,"height":24},"gift-18":{"x":240,"y":528,"width":18,"height":18},"gift-12":{"x":258,"y":528,"width":12,"height":12},"ice-cream-24":{"x":0,"y":552,"width":24,"height":24},"ice-cream-18":{"x":24,"y":552,"width":18,"height":18},"ice-cream-12":{"x":42,"y":552,"width":12,"height":12}},"locales":["af","sq","ar","ar-AA","hy","ast","bn","bs","bg-BG","ca","zh","zh-CN","zh-HK","zh-TW","yue","hr","cs","da","nl","en-GB","eo","et","fi","fr","gl","de","el","hi","hu","is","id","it","ja","kn","ko","lv","lij","lt","no","fa","pl","pt","pt-BR","ro","ru","sc","sr","si","sk","sl","es","sv","tl","ta","te","th","tr","uk","vi"],"en":{"modes":{"add_area":{"title":"Area","description":"Add parks, buildings, lakes or other areas to the map.","tail":"Click on the map to start drawing an area, like a park, lake, or building."},"add_line":{"title":"Line","description":"Add highways, streets, pedestrian paths, canals or other lines to the map.","tail":"Click on the map to start drawing a road, path, or route."},"add_point":{"title":"Point","description":"Add restaurants, monuments, postal boxes or other points to the map.","tail":"Click on the map to add a point."},"browse":{"title":"Browse","description":"Pan and zoom the map."},"draw_area":{"tail":"Click to add nodes to your area. Click the first node to finish the area."},"draw_line":{"tail":"Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."}},"operations":{"add":{"annotation":{"point":"Added a point.","vertex":"Added a node to a way.","relation":"Added a relation."}},"start":{"annotation":{"line":"Started a line.","area":"Started an area."}},"continue":{"key":"A","title":"Continue","description":"Continue this line.","not_eligible":"No line can be continued here.","multiple":"Several lines can be continued here. To choose a line, press the Shift key and click on it to select it.","annotation":{"line":"Continued a line.","area":"Continued an area."}},"cancel_draw":{"annotation":"Canceled drawing."},"change_role":{"annotation":"Changed the role of a relation member."},"change_tags":{"annotation":"Changed tags."},"circularize":{"title":"Circularize","description":{"line":"Make this line circular.","area":"Make this area circular."},"key":"O","annotation":{"line":"Made a line circular.","area":"Made an area circular."},"not_closed":"This can't be made circular because it's not a loop.","too_large":"This can't be made circular because not enough of it is currently visible.","connected_to_hidden":"This can't be made circular because it is connected to a hidden feature."},"orthogonalize":{"title":"Square","description":{"line":"Square the corners of this line.","area":"Square the corners of this area."},"key":"S","annotation":{"line":"Squared the corners of a line.","area":"Squared the corners of an area."},"not_squarish":"This can't be made square because it is not squarish.","too_large":"This can't be made square because not enough of it is currently visible.","connected_to_hidden":"This can't be made square because it is connected to a hidden feature."},"straighten":{"title":"Straighten","description":"Straighten this line.","key":"S","annotation":"Straightened a line.","too_bendy":"This can't be straightened because it bends too much.","connected_to_hidden":"This line can't be straightened because it is connected to a hidden feature."},"delete":{"title":"Delete","description":"Delete object permanently.","annotation":{"point":"Deleted a point.","vertex":"Deleted a node from a way.","line":"Deleted a line.","area":"Deleted an area.","relation":"Deleted a relation.","multiple":"Deleted {n} objects."},"incomplete_relation":"This feature can't be deleted because it hasn't been fully downloaded.","part_of_relation":"This feature can't be deleted because it's part of a larger relation. You must remove it from the relation first.","connected_to_hidden":"This can't be deleted because it is connected to a hidden feature."},"add_member":{"annotation":"Added a member to a relation."},"delete_member":{"annotation":"Removed a member from a relation."},"connect":{"annotation":{"point":"Connected a way to a point.","vertex":"Connected a way to another.","line":"Connected a way to a line.","area":"Connected a way to an area."}},"disconnect":{"title":"Disconnect","description":"Disconnect these lines/areas from each other.","key":"D","annotation":"Disconnected lines/areas.","not_connected":"There aren't enough lines/areas here to disconnect.","connected_to_hidden":"This can't be disconnected because it is connected to a hidden feature.","relation":"This can't be disconnected because it connects members of a relation."},"merge":{"title":"Merge","description":"Merge these features.","key":"C","annotation":"Merged {n} features.","not_eligible":"These features can't be merged.","not_adjacent":"These features can't be merged because they aren't connected.","restriction":"These features can't be merged because at least one is a member of a \"{relation}\" relation.","incomplete_relation":"These features can't be merged because at least one hasn't been fully downloaded.","conflicting_tags":"These features can't be merged because some of their tags have conflicting values."},"move":{"title":"Move","description":"Move this to a different location.","key":"M","annotation":{"point":"Moved a point.","vertex":"Moved a node in a way.","line":"Moved a line.","area":"Moved an area.","multiple":"Moved multiple objects."},"incomplete_relation":"This feature can't be moved because it hasn't been fully downloaded.","too_large":"This can't be moved because not enough of it is currently visible.","connected_to_hidden":"This can't be moved because it is connected to a hidden feature."},"rotate":{"title":"Rotate","description":"Rotate this object around its center point.","key":"R","annotation":{"line":"Rotated a line.","area":"Rotated an area."},"too_large":"This can't be rotated because not enough of it is currently visible.","connected_to_hidden":"This can't be rotated because it is connected to a hidden feature."},"reverse":{"title":"Reverse","description":"Make this line go in the opposite direction.","key":"V","annotation":"Reversed a line."},"split":{"title":"Split","description":{"line":"Split this line into two at this node.","area":"Split the boundary of this area into two.","multiple":"Split the lines/area boundaries at this node into two."},"key":"X","annotation":{"line":"Split a line.","area":"Split an area boundary.","multiple":"Split {n} lines/area boundaries."},"not_eligible":"Lines can't be split at their beginning or end.","multiple_ways":"There are too many lines here to split.","connected_to_hidden":"This can't be split because it is connected to a hidden feature."},"restriction":{"help":{"select":"Click to select a road segment.","toggle":"Click to toggle turn restrictions.","toggle_on":"Click to add a \"{restriction}\" restriction.","toggle_off":"Click to remove the \"{restriction}\" restriction."},"annotation":{"create":"Added a turn restriction","delete":"Deleted a turn restriction"}}},"undo":{"tooltip":"Undo: {action}","nothing":"Nothing to undo."},"redo":{"tooltip":"Redo: {action}","nothing":"Nothing to redo."},"tooltip_keyhint":"Shortcut:","browser_notice":"This editor is supported in Firefox, Chrome, Safari, Opera, and Internet Explorer 11 and above. Please upgrade your browser or use Potlatch 2 to edit the map.","translate":{"translate":"Translate","localized_translation_label":"Multilingual name","localized_translation_language":"Choose language","localized_translation_name":"Name"},"zoom_in_edit":"Zoom in to Edit","logout":"logout","loading_auth":"Connecting to OpenStreetMap...","report_a_bug":"Report a bug","help_translate":"Help translate","feature_info":{"hidden_warning":"{count} hidden features","hidden_details":"These features are currently hidden: {details}"},"status":{"error":"Unable to connect to API.","offline":"The API is offline. Please try editing later.","readonly":"The API is read-only. You will need to wait to save your changes."},"commit":{"title":"Save Changes","description_placeholder":"Brief description of your contributions","message_label":"Changeset comment","upload_explanation":"The changes you upload will be visible on all maps that use OpenStreetMap data.","upload_explanation_with_user":"The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.","save":"Save","cancel":"Cancel","changes":"{count} Changes","warnings":"Warnings","modified":"Modified","deleted":"Deleted","created":"Created","about_changeset_comments":"About changeset comments","about_changeset_comments_link":"//wiki.openstreetmap.org/wiki/Good_changeset_comments"},"contributors":{"list":"Edits by {users}","truncated_list":"Edits by {users} and {count} others"},"infobox":{"selected":"{n} selected","geometry":"Geometry","closed":"closed","center":"Center","perimeter":"Perimeter","length":"Length","area":"Area","centroid":"Centroid","location":"Location","metric":"Metric","imperial":"Imperial"},"geometry":{"point":"point","vertex":"vertex","line":"line","area":"area","relation":"relation"},"geocoder":{"search":"Search worldwide...","no_results_visible":"No results in visible map area","no_results_worldwide":"No results found"},"geolocate":{"title":"Show My Location"},"inspector":{"no_documentation_combination":"There is no documentation available for this tag combination","no_documentation_key":"There is no documentation available for this key","show_more":"Show More","view_on_osm":"View on openstreetmap.org","all_fields":"All fields","all_tags":"All tags","all_members":"All members","all_relations":"All relations","new_relation":"New relation...","role":"Role","choose":"Select feature type","results":"{n} results for {search}","reference":"View on OpenStreetMap Wiki","back_tooltip":"Change feature","remove":"Remove","search":"Search","multiselect":"Selected items","unknown":"Unknown","incomplete":"<not downloaded>","feature_list":"Search features","edit":"Edit feature","check":{"yes":"Yes","no":"No"},"none":"None","node":"Node","way":"Way","relation":"Relation","location":"Location","add_fields":"Add field:"},"background":{"title":"Background","description":"Background settings","percent_brightness":"{opacity}% brightness","none":"None","best_imagery":"Best known imagery source for this location","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"Fix alignment","imagery_source_faq":"Where does this imagery come from?","reset":"reset","minimap":{"description":"Minimap","tooltip":"Show a zoomed out map to help locate the area currently displayed."}},"map_data":{"title":"Map Data","description":"Map Data","data_layers":"Data Layers","fill_area":"Fill Areas","map_features":"Map Features","autohidden":"These features have been automatically hidden because too many would be shown on the screen.  You can zoom in to edit them."},"feature":{"points":{"description":"Points","tooltip":"Points of Interest"},"major_roads":{"description":"Major Roads","tooltip":"Highways, Streets, etc."},"minor_roads":{"description":"Minor Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically.  When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"boundary":{"label":"Type"},"brand":{"label":"Brand"},"building":{"label":"Building"},"building_area":{"label":"Building"},"capacity":{"label":"Capacity","placeholder":"50, 100, 200..."},"cardinal_direction":{"label":"Direction","options":{"N":"North","E":"East","S":"South","W":"West","NE":"Northeast","SE":"Southeast","SW":"Southwest","NW":"Northwest","NNE":"North-northeast","ENE":"East-northeast","ESE":"East-southeast","SSE":"South-southeast","SSW":"South-southwest","WSW":"West-southwest","WNW":"West-northwest","NNW":"North-northwest"}},"clock_direction":{"label":"Direction","options":{"clockwise":"Clockwise","anticlockwise":"Counterclockwise"}},"collection_times":{"label":"Collection Times"},"construction":{"label":"Type"},"content":{"label":"Contents"},"country":{"label":"Country"},"covered":{"label":"Covered"},"craft":{"label":"Type"},"crop":{"label":"Crop"},"crossing":{"label":"Type"},"cuisine":{"label":"Cuisine"},"cycleway":{"label":"Bike Lanes","placeholder":"none","types":{"cycleway:left":"Left side","cycleway:right":"Right side"},"options":{"none":{"title":"None","description":"No bike lane"},"lane":{"title":"Standard bike lane","description":"A bike lane separated from auto traffic by a painted line"},"shared_lane":{"title":"Shared bike lane","description":"A bike lane with no separation from auto traffic"},"track":{"title":"Bike track","description":"A bike lane separated from traffic by a physical barrier"},"share_busway":{"title":"Bike lane shared with bus","description":"A bike lane shared with a bus lane"},"opposite_lane":{"title":"Opposite bike lane","description":"A bike lane that travels in the opposite direction of traffic"},"opposite":{"title":"Contraflow bike lane","description":"A bike lane that travels in both directions on a one-way street"}}},"delivery":{"label":"Delivery"},"denomination":{"label":"Denomination"},"denotation":{"label":"Denotation"},"description":{"label":"Description"},"diaper":{"label":"Diaper Changing Available"},"dock":{"label":"Type"},"drive_through":{"label":"Drive-Through"},"electrified":{"label":"Electrification","placeholder":"Contact Line, Electrified Rail...","options":{"contact_line":"Contact Line","rail":"Electrified Rail","yes":"Yes (unspecified)","no":"No"}},"elevation":{"label":"Elevation"},"emergency":{"label":"Emergency"},"entrance":{"label":"Type"},"except":{"label":"Exceptions"},"fax":{"label":"Fax","placeholder":"+31 42 123 4567"},"fee":{"label":"Fee"},"fire_hydrant/type":{"label":"Type","options":{"pillar":"Pillar/Aboveground","underground":"Underground","wall":"Wall","pond":"Pond"}},"fixme":{"label":"Fix Me"},"fuel":{"label":"Fuel"},"fuel/biodiesel":{"label":"Sells Biodiesel"},"fuel/diesel":{"label":"Sells Diesel"},"fuel/e10":{"label":"Sells E10"},"fuel/e85":{"label":"Sells E85"},"fuel/lpg":{"label":"Sells Propane"},"fuel/octane_100":{"label":"Sells Racing Gasoline"},"fuel/octane_91":{"label":"Sells Regular Gasoline"},"fuel/octane_95":{"label":"Sells Midgrade Gasoline"},"fuel/octane_98":{"label":"Sells Premium Gasoline"},"gauge":{"label":"Gauge"},"gender":{"label":"Gender","placeholder":"Unknown","options":{"male":"Male","female":"Female","unisex":"Unisex"}},"generator/method":{"label":"Method"},"generator/source":{"label":"Source"},"generator/type":{"label":"Type"},"golf_hole":{"label":"Reference","placeholder":"Hole number (1-18)"},"handicap":{"label":"Handicap","placeholder":"1-18"},"handrail":{"label":"Handrail"},"highway":{"label":"Type"},"historic":{"label":"Type"},"hoops":{"label":"Hoops","placeholder":"1, 2, 4..."},"iata":{"label":"IATA"},"icao":{"label":"ICAO"},"incline":{"label":"Incline"},"incline_steps":{"label":"Incline","options":{"up":"Up","down":"Down"}},"information":{"label":"Type"},"internet_access":{"label":"Internet Access","options":{"yes":"Yes","no":"No","wlan":"Wifi","wired":"Wired","terminal":"Terminal"}},"lamp_type":{"label":"Type"},"landuse":{"label":"Type"},"lanes":{"label":"Lanes","placeholder":"1, 2, 3..."},"layer":{"label":"Layer"},"leaf_cycle":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous","mixed":"Mixed"}},"leaf_cycle_singular":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous"}},"leaf_type":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","mixed":"Mixed","leafless":"Leafless"}},"leaf_type_singular":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","leafless":"Leafless"}},"leisure":{"label":"Type"},"length":{"label":"Length (Meters)"},"level":{"label":"Level"},"levels":{"label":"Levels","placeholder":"2, 4, 6..."},"lit":{"label":"Lit"},"location":{"label":"Location"},"man_made":{"label":"Type"},"maxspeed":{"label":"Speed Limit","placeholder":"40, 50, 60..."},"maxstay":{"label":"Max Stay"},"mtb/scale":{"label":"Mountain Biking Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Solid gravel/packed earth, no obstacles, wide curves","1":"1: Some loose surface, small obstacles, wide curves","2":"2: Much loose surface, large obstacles, easy hairpins","3":"3: Slippery surface, large obstacles, tight hairpins","4":"4: Loose surface or boulders, dangerous hairpins","5":"5: Maximum difficulty, boulder fields, landslides","6":"6: Not rideable except by the very best mountain bikers"}},"mtb/scale/imba":{"label":"IMBA Trail Difficulty","placeholder":"Easy, Medium, Difficult...","options":{"0":"Easiest (white circle)","1":"Easy (green circle)","2":"Medium (blue square)","3":"Difficult (black diamond)","4":"Extremely Difficult (double black diamond)"}},"mtb/scale/uphill":{"label":"Mountain Biking Uphill Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Avg. incline <10%, gravel/packed earth, no obstacles","1":"1: Avg. incline <15%, gravel/packed earth, few small objects","2":"2: Avg. incline <20%, stable surface, fistsize rocks/roots","3":"3: Avg. incline <25%, variable surface, fistsize rocks/branches","4":"4: Avg. incline <30%, poor condition, big rocks/branches","5":"5: Very steep, bike generally needs to be pushed or carried"}},"name":{"label":"Name","placeholder":"Common name (if any)"},"natural":{"label":"Natural"},"network":{"label":"Network"},"note":{"label":"Note"},"office":{"label":"Type"},"oneway":{"label":"One Way","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"oneway_yes":{"label":"One Way","options":{"undefined":"Assumed to be Yes","yes":"Yes","no":"No"}},"opening_hours":{"label":"Hours"},"operator":{"label":"Operator"},"par":{"label":"Par","placeholder":"3, 4, 5..."},"park_ride":{"label":"Park and Ride"},"parking":{"label":"Type","options":{"surface":"Surface","multi-storey":"Multilevel","underground":"Underground","sheds":"Sheds","carports":"Carports","garage_boxes":"Garage Boxes","lane":"Roadside Lane"}},"phone":{"label":"Phone","placeholder":"+31 42 123 4567"},"piste/difficulty":{"label":"Difficulty","placeholder":"Easy, Intermediate, Advanced...","options":{"novice":"Novice (instructional)","easy":"Easy (green circle)","intermediate":"Intermediate (blue square)","advanced":"Advanced (black diamond)","expert":"Expert (double black diamond)","freeride":"Freeride (off-piste)","extreme":"Extreme (climbing equipment required)"}},"piste/grooming":{"label":"Grooming","options":{"classic":"Classic","mogul":"Mogul","backcountry":"Backcountry","classic+skating":"Classic and Skating","scooter":"Scooter/Snowmobile","skating":"Skating"}},"piste/type":{"label":"Type","options":{"downhill":"Downhill","nordic":"Nordic","skitour":"Skitour","sled":"Sled","hike":"Hike","sleigh":"Sleigh","ice_skate":"Ice Skate","snow_park":"Snow Park","playground":"Playground"}},"place":{"label":"Type"},"population":{"label":"Population"},"power":{"label":"Type"},"power_supply":{"label":"Power Supply"},"railway":{"label":"Type"},"recycling/cans":{"label":"Accepts Cans"},"recycling/clothes":{"label":"Accepts Clothes"},"recycling/glass":{"label":"Accepts Glass"},"recycling/glass_bottles":{"label":"Accepts Glass Bottles"},"recycling/paper":{"label":"Accepts Paper"},"recycling/plastic":{"label":"Accepts Plastic"},"recycling/type":{"label":"Recycling Type","options":{"container":"Container","centre":"Recycling Center"}},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio_type":{"label":"Type"},"substation":{"label":"Type"},"supervised":{"label":"Supervised"},"surface":{"label":"Surface"},"tactile_paving":{"label":"Tactile Paving"},"takeaway":{"label":"Takeaway","placeholder":"Yes, No, Takeaway Only...","options":{"yes":"Yes","no":"No","only":"Takeaway Only"}},"toilets/disposal":{"label":"Disposal","options":{"flush":"Flush","pitlatrine":"Pit/Latrine","chemical":"Chemical","bucket":"Bucket"}},"tourism":{"label":"Type"},"towertype":{"label":"Tower type"},"tracktype":{"label":"Track Type","placeholder":"Solid, Mostly Solid, Soft...","options":{"grade1":"Solid: paved or heavily compacted hardcore surface","grade2":"Mostly Solid: gravel/rock with some soft material mixed in","grade3":"Even mixture of hard and soft materials","grade4":"Mostly Soft: soil/sand/grass with some hard material mixed in","grade5":"Soft: soil/sand/grass"}},"traffic_signals":{"label":"Type"},"trail_visibility":{"label":"Trail Visibility","placeholder":"Excellent, Good, Bad...","options":{"excellent":"Excellent: unambiguous path or markers everywhere","good":"Good: markers visible, sometimes require searching","intermediate":"Intermediate: few markers, path mostly visible","bad":"Bad: no markers, path sometimes invisible/pathless","horrible":"Horrible: often pathless, some orientation skills required","no":"No: pathless, excellent orientation skills required"}},"trees":{"label":"Trees"},"tunnel":{"label":"Tunnel"},"vending":{"label":"Type of Goods"},"water":{"label":"Type"},"water_point":{"label":"Water Point"},"waterway":{"label":"Type"},"website":{"label":"Website","placeholder":"http://example.com/"},"wetland":{"label":"Type"},"wheelchair":{"label":"Wheelchair Access"},"width":{"label":"Width (Meters)"},"wikipedia":{"label":"Wikipedia"}},"presets":{"address":{"name":"Address","terms":""},"aerialway":{"name":"Aerialway","terms":"ski lift,funifor,funitel"},"aerialway/cable_car":{"name":"Cable Car","terms":"tramway,ropeway"},"aerialway/chair_lift":{"name":"Chair Lift","terms":""},"aerialway/gondola":{"name":"Gondola","terms":""},"aerialway/magic_carpet":{"name":"Magic Carpet Lift","terms":""},"aerialway/platter":{"name":"Platter Lift","terms":"button lift,poma lift"},"aerialway/pylon":{"name":"Aerialway Pylon","terms":""},"aerialway/rope_tow":{"name":"Rope Tow Lift","terms":"handle tow,bugel lift"},"aerialway/station":{"name":"Aerialway Station","terms":""},"aerialway/t-bar":{"name":"T-bar Lift","terms":""},"aeroway":{"name":"Aeroway","terms":""},"aeroway/aerodrome":{"name":"Airport","terms":"airplane,airport,aerodrome"},"aeroway/apron":{"name":"Apron","terms":"ramp"},"aeroway/gate":{"name":"Airport gate","terms":""},"aeroway/hangar":{"name":"Hangar","terms":""},"aeroway/helipad":{"name":"Helipad","terms":"helicopter,helipad,heliport"},"aeroway/runway":{"name":"Runway","terms":"landing strip"},"aeroway/taxiway":{"name":"Taxiway","terms":""},"aeroway/terminal":{"name":"Airport terminal","terms":"airport,aerodrome"},"amenity":{"name":"Amenity","terms":""},"amenity/arts_centre":{"name":"Arts Center","terms":""},"amenity/atm":{"name":"ATM","terms":"money,cash,machine"},"amenity/bank":{"name":"Bank","terms":"credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"},"amenity/bar":{"name":"Bar","terms":"dive,beer,bier,booze"},"amenity/bbq":{"name":"Barbecue/Grill","terms":"bbq"},"amenity/bench":{"name":"Bench","terms":"seat"},"amenity/bicycle_parking":{"name":"Bicycle Parking","terms":"bike"},"amenity/bicycle_rental":{"name":"Bicycle Rental","terms":"bike"},"amenity/bicycle_repair_station":{"name":"Bicycle Repair Tool Stand","terms":"bike,repair,chain,pump"},"amenity/biergarten":{"name":"Beer Garden","terms":"beer,bier,booze"},"amenity/boat_rental":{"name":"Boat Rental","terms":""},"amenity/bureau_de_change":{"name":"Currency Exchange","terms":"bureau de change,money changer"},"amenity/bus_station":{"name":"Bus Station","terms":""},"amenity/cafe":{"name":"Cafe","terms":"bistro,coffee,tea"},"amenity/car_rental":{"name":"Car Rental","terms":""},"amenity/car_sharing":{"name":"Car Sharing","terms":""},"amenity/car_wash":{"name":"Car Wash","terms":""},"amenity/casino":{"name":"Casino","terms":"gambling,roulette,craps,poker,blackjack"},"amenity/charging_station":{"name":"Charging Station","terms":"EV,Electric Vehicle,Supercharger"},"amenity/childcare":{"name":"Nursery/Childcare","terms":"daycare,orphanage,playgroup"},"amenity/cinema":{"name":"Cinema","terms":"drive-in,film,flick,movie,theater,picture,show,screen"},"amenity/clinic":{"name":"Clinic","terms":"medical,urgentcare"},"amenity/clock":{"name":"Clock","terms":""},"amenity/college":{"name":"College Grounds","terms":"university"},"amenity/community_centre":{"name":"Community Center","terms":"event,hall"},"amenity/compressed_air":{"name":"Compressed Air","terms":""},"amenity/courthouse":{"name":"Courthouse","terms":""},"amenity/dentist":{"name":"Dentist","terms":"tooth,teeth"},"amenity/doctors":{"name":"Doctor","terms":"medic*"},"amenity/dojo":{"name":"Dojo / Martial Arts Academy","terms":"martial arts,dojang"},"amenity/drinking_water":{"name":"Drinking Water","terms":"fountain,potable"},"amenity/embassy":{"name":"Embassy","terms":""},"amenity/fast_food":{"name":"Fast Food","terms":"restaurant"},"amenity/ferry_terminal":{"name":"Ferry Terminal","terms":""},"amenity/fire_station":{"name":"Fire Station","terms":""},"amenity/fountain":{"name":"Fountain","terms":""},"amenity/fuel":{"name":"Gas Station","terms":"petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"},"amenity/grave_yard":{"name":"Graveyard","terms":""},"amenity/grit_bin":{"name":"Grit Bin","terms":"salt,sand"},"amenity/hospital":{"name":"Hospital Grounds","terms":"clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"},"amenity/hunting_stand":{"name":"Hunting Stand","terms":""},"amenity/kindergarten":{"name":"Preschool/Kindergarten Grounds","terms":"kindergarden,pre-school"},"amenity/library":{"name":"Library","terms":"book"},"amenity/marketplace":{"name":"Marketplace","terms":""},"amenity/motorcycle_parking":{"name":"Motorcycle Parking","terms":""},"amenity/nightclub":{"name":"Nightclub","terms":"disco*,night club,dancing,dance club"},"amenity/parking":{"name":"Car Parking","terms":""},"amenity/parking_entrance":{"name":"Parking Garage Entrance/Exit","terms":""},"amenity/parking_space":{"name":"Parking Space","terms":""},"amenity/pharmacy":{"name":"Pharmacy","terms":"drug,medicine"},"amenity/place_of_worship":{"name":"Place of Worship","terms":"abbey,basilica,bethel,cathedral,chancel,chantry,chapel,church,fold,house of God,house of prayer,house of worship,minster,mission,mosque,oratory,parish,sacellum,sanctuary,shrine,synagogue,tabernacle,temple"},"amenity/place_of_worship/buddhist":{"name":"Buddhist Temple","terms":"stupa,vihara,monastery,temple,pagoda,zendo,dojo"},"amenity/place_of_worship/christian":{"name":"Church","terms":"christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"},"amenity/place_of_worship/jewish":{"name":"Synagogue","terms":"jewish"},"amenity/place_of_worship/muslim":{"name":"Mosque","terms":"muslim"},"amenity/police":{"name":"Police","terms":"badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"},"amenity/post_box":{"name":"Mailbox","terms":"letter,post"},"amenity/post_office":{"name":"Post Office","terms":"letter,mail"},"amenity/pub":{"name":"Pub","terms":"dive,beer,bier,booze"},"amenity/public_bookcase":{"name":"Public Bookcase","terms":"library,bookcrossing"},"amenity/ranger_station":{"name":"Ranger Station","terms":"visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"},"amenity/recycling":{"name":"Recycling","terms":"can,bottle,garbage,scrap,trash"},"amenity/register_office":{"name":"Register Office","terms":""},"amenity/restaurant":{"name":"Restaurant","terms":"bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"},"amenity/sanitary_dump_station":{"name":"RV Toilet Disposal","terms":"Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"},"amenity/school":{"name":"School Grounds","terms":"academy,elementary school,middle school,high school"},"amenity/shelter":{"name":"Shelter","terms":"lean-to,gazebo,picnic"},"amenity/social_facility":{"name":"Social Facility","terms":""},"amenity/social_facility/food_bank":{"name":"Food Bank","terms":""},"amenity/social_facility/group_home":{"name":"Elderly Group Home","terms":"old,senior,living"},"amenity/social_facility/homeless_shelter":{"name":"Homeless Shelter","terms":"houseless,unhoused,displaced"},"amenity/studio":{"name":"Studio","terms":"recording,radio,television"},"amenity/swimming_pool":{"name":"Swimming Pool","terms":""},"amenity/taxi":{"name":"Taxi Stand","terms":"cab"},"amenity/telephone":{"name":"Telephone","terms":"phone"},"amenity/theatre":{"name":"Theater","terms":"theatre,performance,play,musical"},"amenity/toilets":{"name":"Toilets","terms":"bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."},"amenity/townhall":{"name":"Town Hall","terms":"village,city,government,courthouse,municipal"},"amenity/university":{"name":"University Grounds","terms":"college"},"amenity/vending_machine/cigarettes":{"name":"Cigarette Vending Machine","terms":"cigarette"},"amenity/vending_machine/condoms":{"name":"Condom Vending Machine","terms":"condom"},"amenity/vending_machine/drinks":{"name":"Drink Vending Machine","terms":"drink,soda,beverage,juice,pop"},"amenity/vending_machine/excrement_bags":{"name":"Excrement Bag Vending Machine","terms":"excrement bags,poop,dog,animal"},"amenity/vending_machine/news_papers":{"name":"Newspaper Vending Machine","terms":"newspaper"},"amenity/vending_machine/parcel_pickup_dropoff":{"name":"Parcel Pickup/Dropoff Vending Machine","terms":"parcel,mail,pickup"},"amenity/vending_machine/parking_tickets":{"name":"Parking Ticket Vending Machine","terms":"parking,ticket"},"amenity/vending_machine/public_transport_tickets":{"name":"Transit Ticket Vending Machine","terms":"bus,train,ferry,rail,ticket,transportation"},"amenity/vending_machine/sweets":{"name":"Snack Vending Machine","terms":"candy,gum,chip,pretzel,cookie,cracker"},"amenity/vending_machine/vending_machine":{"name":"Vending Machine","terms":""},"amenity/veterinary":{"name":"Veterinary","terms":"pet clinic,veterinarian,animal hospital,pet doctor"},"amenity/waste_basket":{"name":"Waste Basket","terms":"bin,rubbish,litter,trash,garbage"},"amenity/waste_disposal":{"name":"Garbage Dumpster","terms":"rubbish,litter,trash"},"amenity/water_point":{"name":"RV Drinking Water","terms":""},"area":{"name":"Area","terms":""},"area/highway":{"name":"Road Surface","terms":""},"barrier":{"name":"Barrier","terms":""},"barrier/block":{"name":"Block","terms":""},"barrier/bollard":{"name":"Bollard","terms":""},"barrier/cattle_grid":{"name":"Cattle Grid","terms":""},"barrier/city_wall":{"name":"City Wall","terms":""},"barrier/cycle_barrier":{"name":"Cycle Barrier","terms":""},"barrier/ditch":{"name":"Ditch","terms":""},"barrier/entrance":{"name":"Entrance","terms":""},"barrier/fence":{"name":"Fence","terms":""},"barrier/gate":{"name":"Gate","terms":""},"barrier/hedge":{"name":"Hedge","terms":""},"barrier/kissing_gate":{"name":"Kissing Gate","terms":""},"barrier/lift_gate":{"name":"Lift Gate","terms":""},"barrier/retaining_wall":{"name":"Retaining Wall","terms":""},"barrier/stile":{"name":"Stile","terms":""},"barrier/toll_booth":{"name":"Toll Booth","terms":""},"barrier/wall":{"name":"Wall","terms":""},"boundary/administrative":{"name":"Administrative Boundary","terms":""},"building":{"name":"Building","terms":""},"building/apartments":{"name":"Apartments","terms":""},"building/barn":{"name":"Barn","terms":""},"building/bunker":{"name":"Bunker","terms":""},"building/cabin":{"name":"Cabin","terms":""},"building/cathedral":{"name":"Cathedral Building","terms":""},"building/chapel":{"name":"Chapel Building","terms":""},"building/church":{"name":"Church Building","terms":""},"building/college":{"name":"College Building","terms":"university"},"building/commercial":{"name":"Commercial Building","terms":""},"building/construction":{"name":"Building Under Construction","terms":""},"building/detached":{"name":"Detached House","terms":"home,single,family,residence,dwelling"},"building/dormitory":{"name":"Dormitory","terms":""},"building/entrance":{"name":"Entrance/Exit","terms":""},"building/garage":{"name":"Garage","terms":""},"building/garages":{"name":"Garages","terms":""},"building/greenhouse":{"name":"Greenhouse","terms":""},"building/hospital":{"name":"Hospital Building","terms":""},"building/hotel":{"name":"Hotel Building","terms":""},"building/house":{"name":"House","terms":"home,family,residence,dwelling"},"building/hut":{"name":"Hut","terms":""},"building/industrial":{"name":"Industrial Building","terms":""},"building/kindergarten":{"name":"Preschool/Kindergarten Building","terms":"kindergarden,pre-school"},"building/public":{"name":"Public Building","terms":""},"building/residential":{"name":"Residential Building","terms":""},"building/retail":{"name":"Retail Building","terms":""},"building/roof":{"name":"Roof","terms":""},"building/school":{"name":"School Building","terms":"academy,elementary school,middle school,high school"},"building/semidetached_house":{"name":"Semi-Detached House","terms":"home,double,duplex,twin,family,residence,dwelling"},"building/shed":{"name":"Shed","terms":""},"building/stable":{"name":"Stable","terms":""},"building/static_caravan":{"name":"Static Mobile Home","terms":""},"building/terrace":{"name":"Row Houses","terms":"home,terrace,brownstone,family,residence,dwelling"},"building/train_station":{"name":"Train Station","terms":""},"building/university":{"name":"University Building","terms":"college"},"building/warehouse":{"name":"Warehouse","terms":""},"craft":{"name":"Craft","terms":""},"craft/basket_maker":{"name":"Basket Maker","terms":""},"craft/beekeeper":{"name":"Beekeeper","terms":""},"craft/blacksmith":{"name":"Blacksmith","terms":""},"craft/boatbuilder":{"name":"Boat Builder","terms":""},"craft/bookbinder":{"name":"Bookbinder","terms":"book repair"},"craft/brewery":{"name":"Brewery","terms":"beer,bier"},"craft/carpenter":{"name":"Carpenter","terms":"woodworker"},"craft/carpet_layer":{"name":"Carpet Layer","terms":""},"craft/caterer":{"name":"Caterer","terms":""},"craft/clockmaker":{"name":"Clockmaker","terms":""},"craft/confectionery":{"name":"Confectionery","terms":"sweets,candy"},"craft/dressmaker":{"name":"Dressmaker","terms":"seamstress"},"craft/electrician":{"name":"Electrician","terms":"power,wire"},"craft/gardener":{"name":"Gardener","terms":"landscaper,grounds keeper"},"craft/glaziery":{"name":"Glaziery","terms":"glass,stained-glass,window"},"craft/handicraft":{"name":"Handicraft","terms":""},"craft/hvac":{"name":"HVAC","terms":"heat*,vent*,air conditioning"},"craft/insulator":{"name":"Insulator","terms":""},"craft/jeweler":{"name":"Jeweler","terms":""},"craft/key_cutter":{"name":"Key Cutter","terms":""},"craft/locksmith":{"name":"Locksmith","terms":""},"craft/metal_construction":{"name":"Metal Construction","terms":""},"craft/optician":{"name":"Optician","terms":""},"craft/painter":{"name":"Painter","terms":""},"craft/photographer":{"name":"Photographer","terms":""},"craft/photographic_laboratory":{"name":"Photographic Laboratory","terms":"film"},"craft/plasterer":{"name":"Plasterer","terms":""},"craft/plumber":{"name":"Plumber","terms":"pipe"},"craft/pottery":{"name":"Pottery","terms":"ceramic"},"craft/rigger":{"name":"Rigger","terms":""},"craft/roofer":{"name":"Roofer","terms":""},"craft/saddler":{"name":"Saddler","terms":""},"craft/sailmaker":{"name":"Sailmaker","terms":""},"craft/sawmill":{"name":"Sawmill","terms":"lumber"},"craft/scaffolder":{"name":"Scaffolder","terms":""},"craft/sculpter":{"name":"Sculpter","terms":""},"craft/shoemaker":{"name":"Shoemaker","terms":"cobbler"},"craft/stonemason":{"name":"Stonemason","terms":"masonry"},"craft/sweep":{"name":"Chimney Sweep","terms":""},"craft/tailor":{"name":"Tailor","terms":"clothes,suit"},"craft/tiler":{"name":"Tiler","terms":""},"craft/tinsmith":{"name":"Tinsmith","terms":""},"craft/upholsterer":{"name":"Upholsterer","terms":""},"craft/watchmaker":{"name":"Watchmaker","terms":""},"craft/window_construction":{"name":"Window Construction","terms":"glass"},"craft/winery":{"name":"Winery","terms":""},"embankment":{"name":"Embankment","terms":""},"emergency/ambulance_station":{"name":"Ambulance Station","terms":"EMS,EMT,rescue"},"emergency/fire_hydrant":{"name":"Fire Hydrant","terms":""},"emergency/phone":{"name":"Emergency Phone","terms":""},"entrance":{"name":"Entrance/Exit","terms":""},"footway/crossing":{"name":"Street Crossing","terms":""},"footway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"footway/sidewalk":{"name":"Sidewalk","terms":""},"ford":{"name":"Ford","terms":""},"golf/bunker":{"name":"Sand Trap","terms":"hazard,bunker"},"golf/fairway":{"name":"Fairway","terms":""},"golf/green":{"name":"Putting Green","terms":""},"golf/hole":{"name":"Golf Hole","terms":""},"golf/lateral_water_hazard":{"name":"Lateral Water Hazard","terms":""},"golf/rough":{"name":"Rough","terms":""},"golf/tee":{"name":"Tee Box","terms":"teeing ground"},"golf/water_hazard":{"name":"Water Hazard","terms":""},"highway":{"name":"Highway","terms":""},"highway/bridleway":{"name":"Bridle Path","terms":"bridleway,equestrian,horse"},"highway/bus_stop":{"name":"Bus Stop","terms":""},"highway/corridor":{"name":"Indoor Corridor","terms":"gallery,hall,hallway,indoor,passage,passageway"},"highway/crossing":{"name":"Street Crossing","terms":""},"highway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"highway/cycleway":{"name":"Cycle Path","terms":"bike"},"highway/footway":{"name":"Foot Path","terms":"hike,hiking,trackway,trail,walk"},"highway/living_street":{"name":"Living Street","terms":""},"highway/mini_roundabout":{"name":"Mini-Roundabout","terms":""},"highway/motorway":{"name":"Motorway","terms":""},"highway/motorway_junction":{"name":"Motorway Junction / Exit","terms":""},"highway/motorway_link":{"name":"Motorway Link","terms":"ramp,on ramp,off ramp"},"highway/path":{"name":"Path","terms":"hike,hiking,trackway,trail,walk"},"highway/pedestrian":{"name":"Pedestrian Street","terms":""},"highway/primary":{"name":"Primary Road","terms":""},"highway/primary_link":{"name":"Primary Link","terms":"ramp,on ramp,off ramp"},"highway/raceway":{"name":"Motor Raceway","terms":"auto*,race*,nascar"},"highway/residential":{"name":"Residential Road","terms":""},"highway/rest_area":{"name":"Rest Area","terms":"rest stop"},"highway/road":{"name":"Unknown Road","terms":""},"highway/secondary":{"name":"Secondary Road","terms":""},"highway/secondary_link":{"name":"Secondary Link","terms":"ramp,on ramp,off ramp"},"highway/service":{"name":"Service Road","terms":""},"highway/service/alley":{"name":"Alley","terms":""},"highway/service/drive-through":{"name":"Drive-Through","terms":""},"highway/service/driveway":{"name":"Driveway","terms":""},"highway/service/emergency_access":{"name":"Emergency Access","terms":""},"highway/service/parking_aisle":{"name":"Parking Aisle","terms":""},"highway/services":{"name":"Service Area","terms":"services,travel plaza,service station"},"highway/steps":{"name":"Steps","terms":"stairs,staircase"},"highway/stop":{"name":"Stop Sign","terms":"stop sign"},"highway/street_lamp":{"name":"Street Lamp","terms":"streetlight,street light,lamp,light,gaslight"},"highway/tertiary":{"name":"Tertiary Road","terms":""},"highway/tertiary_link":{"name":"Tertiary Link","terms":"ramp,on ramp,off ramp"},"highway/track":{"name":"Unmaintained Track Road","terms":"woods road,forest road,logging road,fire road,farm road,agricultural road,ranch road,carriage road,primitive,unmaintained,rut,offroad,4wd,4x4,four wheel drive,atv,quad,jeep,double track,two track"},"highway/traffic_signals":{"name":"Traffic Signals","terms":"light,stoplight,traffic light"},"highway/trunk":{"name":"Trunk Road","terms":""},"highway/trunk_link":{"name":"Trunk Link","terms":"ramp,on ramp,off ramp"},"highway/turning_circle":{"name":"Turning Circle","terms":"cul-de-sac"},"highway/unclassified":{"name":"Minor Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":""},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/computer":{"name":"Computer Store","terms":""},"shop/confectionery":{"name":"Candy Store","terms":""},"shop/convenience":{"name":"Convenience Store","terms":""},"shop/copyshop":{"name":"Copy Store","terms":""},"shop/cosmetics":{"name":"Cosmetics Store","terms":""},"shop/craft":{"name":"Arts and Crafts Store","terms":"art*,paint*,frame"},"shop/curtain":{"name":"Curtain Store","terms":"drape*,window"},"shop/dairy":{"name":"Dairy Store","terms":"milk,egg,cheese"},"shop/deli":{"name":"Deli","terms":"lunch,meat,sandwich"},"shop/department_store":{"name":"Department Store","terms":""},"shop/doityourself":{"name":"DIY Store","terms":""},"shop/dry_cleaning":{"name":"Dry Cleaner","terms":""},"shop/electronics":{"name":"Electronics Store","terms":"appliance,audio,computer,tv"},"shop/erotic":{"name":"Erotic Store","terms":"sex,porn"},"shop/fabric":{"name":"Fabric Store","terms":"sew"},"shop/farm":{"name":"Produce Stand","terms":"farm shop,farm stand"},"shop/fashion":{"name":"Fashion Store","terms":""},"shop/fishmonger":{"name":"Fishmonger","terms":""},"shop/florist":{"name":"Florist","terms":"flower"},"shop/frame":{"name":"Framing Shop","terms":"art*,paint*,photo*,frame"},"shop/funeral_directors":{"name":"Funeral Home","terms":"undertaker,memorial home"},"shop/furnace":{"name":"Furnace Store","terms":"oven,stove"},"shop/furniture":{"name":"Furniture Store","terms":"chair,sofa,table"},"shop/garden_centre":{"name":"Garden Center","terms":"landscape,mulch,shrub,tree"},"shop/gift":{"name":"Gift Shop","terms":""},"shop/greengrocer":{"name":"Greengrocer","terms":"fruit,vegetable"},"shop/hairdresser":{"name":"Hairdresser","terms":""},"shop/hardware":{"name":"Hardware Store","terms":""},"shop/hearing_aids":{"name":"Hearing Aids Store","terms":""},"shop/herbalist":{"name":"Herbalist","terms":""},"shop/hifi":{"name":"Hifi Store","terms":"stereo,video"},"shop/houseware":{"name":"Houseware Store","terms":"home,household"},"shop/interior_decoration":{"name":"Interior Decoration Store","terms":""},"shop/jewelry":{"name":"Jeweler","terms":"diamond,gem,ring"},"shop/kiosk":{"name":"News Kiosk","terms":""},"shop/kitchen":{"name":"Kitchen Design Store","terms":""},"shop/laundry":{"name":"Laundry","terms":""},"shop/leather":{"name":"Leather Store","terms":""},"shop/locksmith":{"name":"Locksmith","terms":"key,lockpick"},"shop/lottery":{"name":"Lottery Shop","terms":""},"shop/mall":{"name":"Mall","terms":""},"shop/massage":{"name":"Massage Shop","terms":""},"shop/medical_supply":{"name":"Medical Supply Store","terms":""},"shop/mobile_phone":{"name":"Mobile Phone Store","terms":""},"shop/money_lender":{"name":"Money Lender","terms":""},"shop/motorcycle":{"name":"Motorcycle Dealership","terms":""},"shop/music":{"name":"Music Store","terms":"CD,vinyl"},"shop/musical_instrument":{"name":"Musical Instrument Store","terms":""},"shop/newsagent":{"name":"Newspaper/Magazine Shop","terms":""},"shop/optician":{"name":"Optician","terms":"eye,glasses"},"shop/organic":{"name":"Organic Goods Store","terms":""},"shop/outdoor":{"name":"Outdoors Store","terms":"camping,climbing,hiking"},"shop/paint":{"name":"Paint Store","terms":""},"shop/pawnbroker":{"name":"Pawn Shop","terms":""},"shop/pet":{"name":"Pet Store","terms":"cat,dog,fish"},"shop/photo":{"name":"Photography Store","terms":"camera,film"},"shop/pyrotechnics":{"name":"Fireworks Store","terms":""},"shop/radiotechnics":{"name":"Radio/Electronic Component Store","terms":""},"shop/religion":{"name":"Religious Store","terms":""},"shop/scuba_diving":{"name":"Scuba Diving Shop","terms":""},"shop/seafood":{"name":"Seafood Shop","terms":"fishmonger"},"shop/second_hand":{"name":"Consignment/Thrift Store","terms":"secondhand,second hand,resale,thrift,used"},"shop/shoes":{"name":"Shoe Store","terms":""},"shop/sports":{"name":"Sporting Goods Store","terms":""},"shop/stationery":{"name":"Stationery Store","terms":"card,paper"},"shop/storage_rental":{"name":"Storage Rental","terms":""},"shop/supermarket":{"name":"Supermarket","terms":"grocery,store,shop"},"shop/tailor":{"name":"Tailor","terms":"clothes,suit"},"shop/tattoo":{"name":"Tattoo Parlor","terms":""},"shop/tea":{"name":"Tea Store","terms":""},"shop/ticket":{"name":"Ticket Seller","terms":""},"shop/tobacco":{"name":"Tobacco Shop","terms":""},"shop/toys":{"name":"Toy Store","terms":""},"shop/travel_agency":{"name":"Travel Agency","terms":""},"shop/tyres":{"name":"Tire Store","terms":""},"shop/vacant":{"name":"Vacant Shop","terms":""},"shop/vacuum_cleaner":{"name":"Vacuum Cleaner Store","terms":""},"shop/variety_store":{"name":"Variety Store","terms":""},"shop/video":{"name":"Video Store","terms":"DVD"},"shop/video_games":{"name":"Video Game Store","terms":""},"shop/water_sports":{"name":"Watersport/Swim Shop","terms":""},"shop/weapons":{"name":"Weapon Shop","terms":"ammo,gun,knife,knives"},"shop/window_blind":{"name":"Window Blind Store","terms":""},"shop/wine":{"name":"Wine Shop","terms":""},"tourism":{"name":"Tourism","terms":""},"tourism/alpine_hut":{"name":"Alpine Hut","terms":""},"tourism/artwork":{"name":"Artwork","terms":"mural,sculpture,statue"},"tourism/attraction":{"name":"Tourist Attraction","terms":""},"tourism/camp_site":{"name":"Camp Site","terms":"Tent"},"tourism/caravan_site":{"name":"RV Park","terms":"Motor Home,Camper"},"tourism/chalet":{"name":"Chalet","terms":""},"tourism/gallery":{"name":"Art Gallery","terms":"art*,exhibit*,paint*,photo*,sculpt*"},"tourism/guest_house":{"name":"Guest House","terms":"B&B,Bed and Breakfast"},"tourism/hostel":{"name":"Hostel","terms":""},"tourism/hotel":{"name":"Hotel","terms":""},"tourism/information":{"name":"Information","terms":""},"tourism/motel":{"name":"Motel","terms":""},"tourism/museum":{"name":"Museum","terms":"art*,exhibit*,gallery,foundation,hall,institution,paint*,photo*,sculpt*"},"tourism/picnic_site":{"name":"Picnic Site","terms":"camp"},"tourism/theme_park":{"name":"Theme Park","terms":""},"tourism/viewpoint":{"name":"Viewpoint","terms":""},"tourism/zoo":{"name":"Zoo","terms":""},"traffic_calming/bump":{"name":"Speed Bump","terms":"speed hump"},"traffic_calming/hump":{"name":"Speed Hump","terms":"speed bump"},"traffic_calming/rumble_strip":{"name":"Rumble Strip","terms":"sleeper lines,audible lines,growlers"},"traffic_calming/table":{"name":"Raised Pedestrian Crossing","terms":"speed table,flat top hump"},"type/boundary":{"name":"Boundary","terms":""},"type/boundary/administrative":{"name":"Administrative Boundary","terms":""},"type/multipolygon":{"name":"Multipolygon","terms":""},"type/restriction":{"name":"Restriction","terms":""},"type/restriction/no_left_turn":{"name":"No Left Turn","terms":""},"type/restriction/no_right_turn":{"name":"No Right Turn","terms":""},"type/restriction/no_straight_on":{"name":"No Straight On","terms":""},"type/restriction/no_u_turn":{"name":"No U-turn","terms":""},"type/restriction/only_left_turn":{"name":"Left Turn Only","terms":""},"type/restriction/only_right_turn":{"name":"Right Turn Only","terms":""},"type/restriction/only_straight_on":{"name":"No Turns","terms":""},"type/route":{"name":"Route","terms":""},"type/route/bicycle":{"name":"Cycle Route","terms":""},"type/route/bus":{"name":"Bus Route","terms":""},"type/route/detour":{"name":"Detour Route","terms":""},"type/route/ferry":{"name":"Ferry Route","terms":""},"type/route/foot":{"name":"Foot Route","terms":""},"type/route/hiking":{"name":"Hiking Route","terms":""},"type/route/pipeline":{"name":"Pipeline Route","terms":""},"type/route/power":{"name":"Power Route","terms":""},"type/route/road":{"name":"Road Route","terms":""},"type/route/train":{"name":"Train Route","terms":""},"type/route/tram":{"name":"Tram Route","terms":""},"type/route_master":{"name":"Route Master","terms":""},"type/site":{"name":"Site","terms":""},"vertex":{"name":"Other","terms":""},"waterway":{"name":"Waterway","terms":""},"waterway/boatyard":{"name":"Boatyard","terms":""},"waterway/canal":{"name":"Canal","terms":""},"waterway/dam":{"name":"Dam","terms":""},"waterway/ditch":{"name":"Ditch","terms":""},"waterway/dock":{"name":"Dock","terms":""},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}]};