X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/76c6409b67650facb610ded754d82d3518c9edb9..b10dd0949ddb7c6002b2bb1513c4c0a54f5fac4f:/vendor/assets/iD/iD.js?ds=inline diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js index 8ffa66d17..11c3eb29b 100644 --- a/vendor/assets/iD/iD.js +++ b/vendor/assets/iD/iD.js @@ -174,11 +174,13 @@ } })(this); -d3 = (function(){ - var d3 = {version: "3.3.10"}; // semver -d3.ascending = function(a, b) { +!function(){ + var d3 = {version: "3.4.6"}; // semver +d3.ascending = d3_ascending; + +function d3_ascending(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -}; +} d3.descending = function(a, b) { return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; }; @@ -250,17 +252,17 @@ function d3_number(x) { } d3.mean = function(array, f) { - var n = array.length, + var s = 0, + n = array.length, a, - m = 0, i = -1, - j = 0; + j = n; if (arguments.length === 1) { - while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; + while (++i < n) if (d3_number(a = array[i])) s += a; else --j; } else { - while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; + while (++i < n) if (d3_number(a = f.call(array, array[i], i))) s += a; else --j; } - return j ? m : undefined; + return j ? s / j : undefined; }; // R-7 per d3.quantile = function(values, p) { @@ -274,16 +276,17 @@ d3.quantile = function(values, p) { d3.median = function(array, f) { if (arguments.length > 1) array = array.map(f); array = array.filter(d3_number); - return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined; + return array.length ? d3.quantile(array.sort(d3_ascending), .5) : undefined; }; -d3.bisector = function(f) { + +function d3_bisector(compare) { return { left: function(a, x, lo, hi) { if (arguments.length < 3) lo = 0; if (arguments.length < 4) hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; - if (f.call(a, a[mid], mid) < x) lo = mid + 1; + if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; } return lo; @@ -293,17 +296,23 @@ d3.bisector = function(f) { if (arguments.length < 4) hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; - if (x < f.call(a, a[mid], mid)) hi = mid; + if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; } return lo; } }; -}; +} + +var d3_bisect = d3_bisector(d3_ascending); +d3.bisectLeft = d3_bisect.left; +d3.bisect = d3.bisectRight = d3_bisect.right; -var d3_bisector = d3.bisector(function(d) { return d; }); -d3.bisectLeft = d3_bisector.left; -d3.bisect = d3.bisectRight = d3_bisector.right; +d3.bisector = function(f) { + return d3_bisector(f.length === 1 + ? function(d, x) { return d3_ascending(f(d), x); } + : f); +}; d3.shuffle = function(array) { var m = array.length, t, i; while (m) { @@ -425,24 +434,15 @@ d3.map = function(object) { function d3_Map() {} d3_class(d3_Map, { - has: function(key) { - return d3_map_prefix + key in this; - }, + has: d3_map_has, get: function(key) { return this[d3_map_prefix + key]; }, set: function(key, value) { return this[d3_map_prefix + key] = value; }, - remove: function(key) { - key = d3_map_prefix + key; - return key in this && delete this[key]; - }, - keys: function() { - var keys = []; - this.forEach(function(key) { keys.push(key); }); - return keys; - }, + remove: d3_map_remove, + keys: d3_map_keys, values: function() { var values = []; this.forEach(function(key, value) { values.push(value); }); @@ -453,18 +453,42 @@ d3_class(d3_Map, { this.forEach(function(key, value) { entries.push({key: key, value: value}); }); return entries; }, + size: d3_map_size, + empty: d3_map_empty, forEach: function(f) { - for (var key in this) { - if (key.charCodeAt(0) === d3_map_prefixCode) { - f.call(this, key.substring(1), this[key]); - } - } + for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]); } }); var d3_map_prefix = "\0", // prevent collision with built-ins d3_map_prefixCode = d3_map_prefix.charCodeAt(0); +function d3_map_has(key) { + return d3_map_prefix + key in this; +} + +function d3_map_remove(key) { + key = d3_map_prefix + key; + return key in this && delete this[key]; +} + +function d3_map_keys() { + var keys = []; + this.forEach(function(key) { keys.push(key); }); + return keys; +} + +function d3_map_size() { + var size = 0; + for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) ++size; + return size; +} + +function d3_map_empty() { + for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) return false; + return true; +} + d3.nest = function() { var nest = {}, keys = [], @@ -570,9 +594,7 @@ d3.set = function(array) { function d3_Set() {} d3_class(d3_Set, { - has: function(value) { - return d3_map_prefix + value in this; - }, + has: d3_map_has, add: function(value) { this[d3_map_prefix + value] = true; return value; @@ -581,19 +603,11 @@ d3_class(d3_Set, { value = d3_map_prefix + value; return value in this && delete this[value]; }, - values: function() { - var values = []; - this.forEach(function(value) { - values.push(value); - }); - return values; - }, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, forEach: function(f) { - for (var value in this) { - if (value.charCodeAt(0) === d3_map_prefixCode) { - f.call(this, value.substring(1)); - } - } + for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1)); } }); d3.behavior = {}; @@ -794,7 +808,7 @@ var d3_select = function(s, n) { return n.querySelector(s); }, // Prefer Sizzle, if available. if (typeof Sizzle === "function") { d3_select = function(s, n) { return Sizzle(s, n)[0] || null; }; - d3_selectAll = function(s, n) { return Sizzle.uniqueSort(Sizzle(s, n)); }; + d3_selectAll = Sizzle; d3_selectMatches = Sizzle.matchesSelector; } @@ -954,7 +968,7 @@ d3_selectionPrototype.classed = function(name, value) { // probably doesn't support it on SVG elements (which can be animated). if (typeof name === "string") { var node = this.node(), - n = (name = name.trim().split(/^|\s+/g)).length, + n = (name = d3_selection_classes(name)).length, i = -1; if (value = node.classList) { while (++i < n) if (!value.contains(name[i])) return false; @@ -979,9 +993,13 @@ function d3_selection_classedRe(name) { return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); } +function d3_selection_classes(name) { + return name.trim().split(/^|\s+/); +} + // Multiple class names are allowed (e.g., "foo bar"). function d3_selection_classed(name, value) { - name = name.trim().split(/\s+/).map(d3_selection_classedName); + name = d3_selection_classes(name).map(d3_selection_classedName); var n = name.length; function classedConstant() { @@ -1328,7 +1346,7 @@ d3_selectionPrototype.sort = function(comparator) { }; function d3_selection_sortComparator(comparator) { - if (!arguments.length) comparator = d3.ascending; + if (!arguments.length) comparator = d3_ascending; return function(a, b) { return a && b ? comparator(a.__data__, b.__data__) : !a - !b; }; @@ -1608,29 +1626,12 @@ d3.mouse = function(container) { return d3_mousePoint(container, d3_eventSource()); }; -// https://bugs.webkit.org/show_bug.cgi?id=44083 -var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; - function d3_mousePoint(container, e) { if (e.changedTouches) e = e.changedTouches[0]; var svg = container.ownerSVGElement || container; if (svg.createSVGPoint) { var point = svg.createSVGPoint(); - if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) { - svg = d3.select("body").append("svg").style({ - position: "absolute", - top: 0, - left: 0, - margin: 0, - padding: 0, - border: "none" - }, "important"); - var ctm = svg[0][0].getScreenCTM(); - d3_mouse_bug44083 = !(ctm.f || ctm.e); - svg.remove(); - } - if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; - else point.x = e.clientX, point.y = e.clientY; + point.x = e.clientX, point.y = e.clientY; point = point.matrixTransform(container.getScreenCTM().inverse()); return [point.x, point.y]; } @@ -1658,6 +1659,14 @@ function d3_sgn(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; } +// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of +// the 3D cross product in a quadrant I Cartesian coordinate system (+x is +// right, +y is up). Returns a positive value if ABC is counter-clockwise, +// negative if clockwise, and zero if the points are collinear. +function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); +} + function d3_acos(x) { return x > 1 ? 0 : x < -1 ? π : Math.acos(x); } @@ -1756,37 +1765,37 @@ d3.behavior.zoom = function() { zoom.event = function(g) { g.each(function() { - var event_ = event.of(this, arguments), + var dispatch = event.of(this, arguments), view1 = view; if (d3_transitionInheritId) { - d3.select(this).transition() - .each("start.zoom", function() { - view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state - zoomstarted(event_); - }) - .tween("zoom:zoom", function() { - var dx = size[0], - dy = size[1], - cx = dx / 2, - cy = dy / 2, - i = d3.interpolateZoom( - [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k], - [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k] - ); - return function(t) { - var l = i(t), k = dx / l[2]; - this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k}; - zoomed(event_); - }; - }) - .each("end.zoom", function() { - zoomended(event_); - }); + d3.select(this).transition() + .each("start.zoom", function() { + view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state + zoomstarted(dispatch); + }) + .tween("zoom:zoom", function() { + var dx = size[0], + dy = size[1], + cx = dx / 2, + cy = dy / 2, + i = d3.interpolateZoom( + [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k], + [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k] + ); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k}; + zoomed(dispatch); + }; + }) + .each("end.zoom", function() { + zoomended(dispatch); + }); } else { this.__chart__ = view; - zoomstarted(event_); - zoomed(event_); - zoomended(event_); + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); } }); } @@ -1862,65 +1871,65 @@ d3.behavior.zoom = function() { if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert)); } - function zoomstarted(event) { - event({type: "zoomstart"}); + function zoomstarted(dispatch) { + dispatch({type: "zoomstart"}); } - function zoomed(event) { + function zoomed(dispatch) { rescale(); - event({type: "zoom", scale: view.k, translate: [view.x, view.y]}); + dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]}); } - function zoomended(event) { - event({type: "zoomend"}); + function zoomended(dispatch) { + dispatch({type: "zoomend"}); } function mousedowned() { - var target = this, - event_ = event.of(target, arguments), - eventTarget = d3.event.target, + var that = this, + target = d3.event.target, + dispatch = event.of(that, arguments), dragged = 0, - w = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), - l = location(d3.mouse(target)), + subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), + location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(); - d3_selection_interrupt.call(target); - zoomstarted(event_); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); function moved() { dragged = 1; - translateTo(d3.mouse(target), l); - zoomed(event_); + translateTo(d3.mouse(that), location0); + zoomed(dispatch); } function ended() { - w.on(mousemove, d3_window === target ? mousewheelreset : null).on(mouseup, null); - dragRestore(dragged && d3.event.target === eventTarget); - zoomended(event_); + subject.on(mousemove, d3_window === that ? mousewheelreset : null).on(mouseup, null); + dragRestore(dragged && d3.event.target === target); + zoomended(dispatch); } } // These closures persist for as long as at least one touch is active. function touchstarted() { - var target = this, - event_ = event.of(target, arguments), + var that = this, + dispatch = event.of(that, arguments), locations0 = {}, // touchstart locations distance0 = 0, // distance² between initial touches scale0, // scale when we started touching - eventId = d3.event.changedTouches[0].identifier, - touchmove = "touchmove.zoom-" + eventId, - touchend = "touchend.zoom-" + eventId, - w = d3.select(d3_window).on(touchmove, moved).on(touchend, ended), - t = d3.select(target).on(mousedown, null).on(touchstart, started), // prevent duplicate events + zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, + touchmove = "touchmove" + zoomName, + touchend = "touchend" + zoomName, + target = d3.select(d3.event.target).on(touchmove, moved).on(touchend, ended), + subject = d3.select(that).on(mousedown, null).on(touchstart, started), // prevent duplicate events dragRestore = d3_event_dragSuppress(); - d3_selection_interrupt.call(target); + d3_selection_interrupt.call(that); started(); - zoomstarted(event_); + zoomstarted(dispatch); // Updates locations of any touches in locations0. function relocate() { - var touches = d3.touches(target); + var touches = d3.touches(that); scale0 = view.k; touches.forEach(function(t) { if (t.identifier in locations0) locations0[t.identifier] = location(t); @@ -1945,7 +1954,7 @@ d3.behavior.zoom = function() { scaleTo(view.k * 2); translateTo(p, l); d3_eventPreventDefault(); - zoomed(event_); + zoomed(dispatch); } touchtime = now; } else if (touches.length > 1) { @@ -1956,7 +1965,7 @@ d3.behavior.zoom = function() { } function moved() { - var touches = d3.touches(target), + var touches = d3.touches(that), p0, l0, p1, l1; for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { @@ -1977,7 +1986,7 @@ d3.behavior.zoom = function() { touchtime = null; translateTo(p0, l0); - zoomed(event_); + zoomed(dispatch); } function ended() { @@ -1995,24 +2004,24 @@ d3.behavior.zoom = function() { } } // Otherwise, remove touchmove and touchend listeners. - w.on(touchmove, null).on(touchend, null); - t.on(mousedown, mousedowned).on(touchstart, touchstarted); + target.on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); dragRestore(); - zoomended(event_); + zoomended(dispatch); } } function mousewheeled() { - var event_ = event.of(this, arguments); + var dispatch = event.of(this, arguments); if (mousewheelTimer) clearTimeout(mousewheelTimer); - else d3_selection_interrupt.call(this), zoomstarted(event_); - mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(event_); }, 50); + else d3_selection_interrupt.call(this), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50); d3_eventPreventDefault(); var point = center || d3.mouse(this); if (!translate0) translate0 = location(point); scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); translateTo(point, translate0); - zoomed(event_); + zoomed(dispatch); } function mousewheelreset() { @@ -2020,15 +2029,15 @@ d3.behavior.zoom = function() { } function dblclicked() { - var event_ = event.of(this, arguments), + var dispatch = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2; - zoomstarted(event_); + zoomstarted(dispatch); scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); translateTo(p, l); - zoomed(event_); - zoomended(event_); + zoomed(dispatch); + zoomended(dispatch); } return d3.rebind(zoom, event, "on"); @@ -2047,6 +2056,15 @@ function d3_functor(v) { d3.functor = d3_functor; +d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } +}; + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, // is an interval (or frame) active? @@ -2257,7 +2275,6 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { clip.lineEnd = ringEnd; segments = []; polygon = []; - listener.polygonStart(); }, polygonEnd: function() { clip.point = point; @@ -2267,13 +2284,15 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { segments = d3.merge(segments); var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; listener.lineStart(); interpolate(null, null, 1, listener); listener.lineEnd(); } - listener.polygonEnd(); + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; segments = polygon = null; }, sphere: function() { @@ -2300,6 +2319,7 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), + polygonStarted = false, polygon, ring; @@ -2335,9 +2355,12 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { var n = segment.length - 1, i = -1, point; - listener.lineStart(); - while (++i < n) listener.point((point = segment[i])[0], point[1]); - listener.lineEnd(); + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } return; } @@ -2531,11 +2554,13 @@ function d3_geo_areaRingStart() { // previous point, current point. Uses a formula derived from Cagnoli’s // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). var dλ = λ - λ0, + sdλ = dλ >= 0 ? 1 : -1, + adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, - u = cosφ0 * cosφ + k * Math.cos(dλ), - v = k * Math.sin(dλ); + u = cosφ0 * cosφ + k * Math.cos(adλ), + v = k * sdλ * Math.sin(adλ); d3_geo_areaRingSum.add(Math.atan2(v, u)); // Advance the previous points. @@ -2622,11 +2647,13 @@ function d3_geo_pointInPolygon(point, polygon) { sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, - antimeridian = abs(dλ) > π, + sdλ = dλ >= 0 ? 1 : -1, + adλ = sdλ * dλ, + antimeridian = adλ > π, k = sinφ0 * sinφ; - d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosφ0 * cosφ + k * Math.cos(dλ))); + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); - polarAngle += antimeridian ? dλ + (dλ >= 0 ? τ : -τ): dλ; + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; // Are the longitudes either side of the point's meridian, and are the // latitudes smaller than the parallel? @@ -3218,9 +3245,9 @@ function d3_geo_clipExtent(x0, y0, x1, y1) { for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { b = v[j]; if (a[1] <= y) { - if (b[1] > y && isLeft(a, b, p) > 0) ++wn; + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; } else { - if (b[1] <= y && isLeft(a, b, p) < 0) --wn; + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; } a = b; } @@ -3228,10 +3255,6 @@ function d3_geo_clipExtent(x0, y0, x1, y1) { return wn !== 0; } - function isLeft(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]); - } - function interpolate(from, to, direction, listener) { var a = 0, a1 = 0; if (from == null || @@ -4546,6 +4569,131 @@ function d3_geom_polygonClosed(coordinates) { b = coordinates[coordinates.length - 1]; return !(a[0] - b[0] || a[1] - b[1]); } +function d3_geom_pointX(d) { + return d[0]; +} + +function d3_geom_pointY(d) { + return d[1]; +} + +/** + * Computes the 2D convex hull of a set of points using Graham's scanning + * algorithm. The algorithm has been implemented as described in Cormen, + * Leiserson, and Rivest's Introduction to Algorithms. The running time of + * this algorithm is O(n log n), where n is the number of input points. + * + * @param vertices [[x1, y1], [x2, y2], …] + * @returns polygon [[x1, y1], [x2, y2], …] + */ +d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, + y = d3_geom_pointY; + + if (arguments.length) return hull(vertices); + + function hull(data) { + if (data.length < 3) return []; + + var fx = d3_functor(x), + fy = d3_functor(y), + n = data.length, + vertices, // TODO use parallel arrays + plen = n - 1, + points = [], + stack = [], + d, + i, j, h = 0, x1, y1, x2, y2, u, v, a, sp; + + if (fx === d3_geom_pointX && y === d3_geom_pointY) vertices = data; + else for (i = 0, vertices = []; i < n; ++i) { + vertices.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]); + } + + // find the starting ref point: leftmost point with the minimum y coord + for (i = 1; i < n; ++i) { + if (vertices[i][1] < vertices[h][1] + || vertices[i][1] == vertices[h][1] + && vertices[i][0] < vertices[h][0]) h = i; + } + + // calculate polar angles from ref point and sort + for (i = 0; i < n; ++i) { + if (i === h) continue; + y1 = vertices[i][1] - vertices[h][1]; + x1 = vertices[i][0] - vertices[h][0]; + points.push({angle: Math.atan2(y1, x1), index: i}); + } + points.sort(function(a, b) { return a.angle - b.angle; }); + + // toss out duplicate angles + a = points[0].angle; + v = points[0].index; + u = 0; + for (i = 1; i < plen; ++i) { + j = points[i].index; + if (a == points[i].angle) { + // keep angle for point most distant from the reference + x1 = vertices[v][0] - vertices[h][0]; + y1 = vertices[v][1] - vertices[h][1]; + x2 = vertices[j][0] - vertices[h][0]; + y2 = vertices[j][1] - vertices[h][1]; + if (x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2) { + points[i].index = -1; + continue; + } else { + points[u].index = -1; + } + } + a = points[i].angle; + u = i; + v = j; + } + + // initialize the stack + stack.push(h); + for (i = 0, j = 0; i < 2; ++j) { + if (points[j].index > -1) { + stack.push(points[j].index); + i++; + } + } + sp = stack.length; + + // do graham's scan + for (; j < plen; ++j) { + if (points[j].index < 0) continue; // skip tossed out points + while (!d3_geom_hullCCW(stack[sp - 2], stack[sp - 1], points[j].index, vertices)) { + --sp; + } + stack[sp++] = points[j].index; + } + + // construct the hull + var poly = []; + for (i = sp - 1; i >= 0; --i) poly.push(data[stack[i]]); + return poly; + } + + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; + }; + + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; + }; + + return hull; +}; + +// are three points in counter-clockwise order? +function d3_geom_hullCCW(i1, i2, i3, v) { + var t, a, b, c, d, e, f; + t = v[i1]; a = t[0]; b = t[1]; + t = v[i2]; c = t[0]; d = t[1]; + t = v[i3]; e = t[0]; f = t[1]; + return (f - b) * (c - a) - (d - b) * (e - a) > 0; +} var d3_ease_default = function() { return d3_identity; }; @@ -4995,7 +5143,7 @@ function d3_rgb_parse(format, rgb, hsl) { b = 0, // blue channel; int in [0, 255] m1, // CSS color specification match m2, // CSS color specification type (e.g., rgb) - name; + color; /* Handle hsl, rgb. */ m1 = /([a-z]+)\((.*)\)/i.exec(format); @@ -5020,22 +5168,19 @@ function d3_rgb_parse(format, rgb, hsl) { } /* Named colors. */ - if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b); + if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b); /* Hexadecimal colors: #rgb and #rrggbb. */ - if (format != null && format.charAt(0) === "#") { + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.substring(1), 16))) { if (format.length === 4) { - r = format.charAt(1); r += r; - g = format.charAt(2); g += g; - b = format.charAt(3); b += b; + r = (color & 0xf00) >> 4; r = (r >> 4) | r; + g = (color & 0xf0); g = (g >> 4) | g; + b = (color & 0xf); b = (b << 4) | b; } else if (format.length === 7) { - r = format.substring(1, 3); - g = format.substring(3, 5); - b = format.substring(5, 7); + r = (color & 0xff0000) >> 16; + g = (color & 0xff00) >> 8; + b = (color & 0xff); } - r = parseInt(r, 16); - g = parseInt(g, 16); - b = parseInt(b, 16); } return rgb(r, g, b); @@ -5304,89 +5449,55 @@ function d3_interpolateNumber(a, b) { d3.interpolateString = d3_interpolateString; function d3_interpolateString(a, b) { - var m, // current match - i, // current index - j, // current index (for coalescing) - s0 = 0, // start index of current string prefix - s1 = 0, // end index of current string prefix + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b + am, // current match in a + bm, // current match in b + bs, // string preceding current number in b, if any + i = -1, // index in s s = [], // string constants and placeholders - q = [], // number interpolators - n, // q.length - o; + q = []; // number interpolators // Coerce inputs to strings. a = a + "", b = b + ""; - // Reset our regular expression! - d3_interpolate_number.lastIndex = 0; - - // Find all numbers in b. - for (i = 0; m = d3_interpolate_number.exec(b); ++i) { - if (m.index) s.push(b.substring(s0, s1 = m.index)); - q.push({i: s.length, x: m[0]}); - s.push(null); - s0 = d3_interpolate_number.lastIndex; - } - if (s0 < b.length) s.push(b.substring(s0)); - - // Find all numbers in a. - for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { - o = q[i]; - if (o.x == m[0]) { // The numbers match, so coalesce. - if (o.i) { - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i - 1] += o.x; - s.splice(o.i, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } else { // This match is followed by a string, so coalesce twice. - s[o.i - 1] += o.x + s[o.i + 1]; - s.splice(o.i, 2); - for (j = i + 1; j < n; ++j) q[j].i -= 2; - } - } else { - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i] = o.x; - } else { // This match is followed by a string, so coalesce twice. - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } - } - q.splice(i, 1); - n--; - i--; - } else { - o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); - } + // Interpolate pairs of numbers in a & b. + while ((am = d3_interpolate_numberA.exec(a)) + && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { // a string precedes the next number in b + bs = b.substring(bi, bs); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match + if (s[i]) s[i] += bm; // coalesce with previous string + else s[++i] = bm; + } else { // interpolate non-matching numbers + s[++i] = null; + q.push({i: i, x: d3_interpolateNumber(am, bm)}); + } + bi = d3_interpolate_numberB.lastIndex; } - // Remove any numbers in b not found in a. - while (i < n) { - o = q.pop(); - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i] = o.x; - } else { // This match is followed by a string, so coalesce twice. - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - } - n--; + // Add remains of b. + if (bi < b.length) { + bs = b.substring(bi); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; } // Special optimization for only a single match. - if (s.length === 1) { - return s[0] == null - ? (o = q[0].x, function(t) { return o(t) + ""; }) - : function() { return b; }; - } - // Otherwise, interpolate each of the numbers and rejoin the string. - return function(t) { - for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; + return s.length < 2 + ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; }) + : function() { return b; }) + : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); } -var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; +var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, + d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); d3.interpolate = d3_interpolate; @@ -5401,7 +5512,8 @@ d3.interpolators = [ var t = typeof b; return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString) : b instanceof d3_Color ? d3_interpolateRgb - : t === "object" ? (Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject) + : Array.isArray(b) ? d3_interpolateArray + : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); } ]; @@ -5664,6 +5776,7 @@ d3_transitionPrototype.ease = function(value) { d3_transitionPrototype.delay = function(value) { var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].delay; return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { node.__transition__[id].delay = +value.call(node, node.__data__, i, j); } : (value = +value, function(node) { node.__transition__[id].delay = value; })); @@ -5671,6 +5784,7 @@ d3_transitionPrototype.delay = function(value) { d3_transitionPrototype.duration = function(value) { var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].duration; return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j)); } : (value = Math.max(1, value), function(node) { node.__transition__[id].duration = value; })); @@ -5932,8 +6046,14 @@ function d3_html(request) { d3.xml = d3_xhrType(function(request) { return request.responseXML; }); - return d3; -})(); + if (typeof define === "function" && define.amd) { + define(d3); + } else if (typeof module === "object" && module.exports) { + module.exports = d3; + } else { + this.d3 = d3; + } +}(); d3.combobox = function() { var event = d3.dispatch('accept'), data = [], @@ -5980,8 +6100,12 @@ d3.combobox = function() { // on mousedown d3.event.stopPropagation(); d3.event.preventDefault(); - input.node().focus(); - fetch('', render); + if (!shown) { + input.node().focus(); + fetch('', render); + } else { + hide(); + } }); }); @@ -6930,7 +7054,7 @@ var JXON = new (function () { /** * @license * Lo-Dash 2.3.0 (Custom Build) - * Build: `lodash include="any,assign,bind,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,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" exports="global,node"` + * Build: `lodash --debug --output js/lib/lodash.js include="any,assign,bind,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,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"` * Copyright 2012-2013 The Dojo Foundation * Based on Underscore.js 1.5.2 * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors @@ -9233,6 +9357,57 @@ var JXON = new (function () { return result; } + /** + * Creates a shallow clone of `object` composed of the specified properties. + * Property names may be specified as individual arguments or as arrays of + * property names. If a callback is provided it will be executed for each + * property of `object` picking the properties the callback returns truey + * for. The callback is bound to `thisArg` and invoked with three arguments; + * (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The source object. + * @param {Function|...string|string[]} [callback] The function called per + * iteration or property names to pick, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name'); + * // => { 'name': 'fred' } + * + * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) { + * return key.charAt(0) != '_'; + * }); + * // => { 'name': 'fred' } + */ + function pick(object, callback, thisArg) { + var result = {}; + if (typeof callback != 'function') { + var index = -1, + props = baseFlatten(arguments, true, false, 1), + length = isObject(object) ? props.length : 0; + + while (++index < length) { + var key = props[index]; + if (key in object) { + result[key] = object[key]; + } + } + } else { + callback = lodash.createCallback(callback, thisArg, 3); + forIn(object, function(value, key, object) { + if (callback(value, key, object)) { + result[key] = value; + } + }); + } + return result; + } + /** * Creates an array composed of the own enumerable property values of `object`. * @@ -9662,6 +9837,60 @@ var JXON = new (function () { */ var pluck = map; + /** + * Reduces a collection to a value which is the accumulated result of running + * each element in the collection through the callback, where each successive + * callback execution consumes the return value of the previous execution. If + * `accumulator` is not provided the first element of the collection will be + * used as the initial `accumulator` value. The callback is bound to `thisArg` + * and invoked with four arguments; (accumulator, value, index|key, collection). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [accumulator] Initial value of the accumulator. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(sum, num) { + * return sum + num; + * }); + * // => 6 + * + * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { + * result[key] = num * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6, 'c': 9 } + */ + function reduce(collection, callback, accumulator, thisArg) { + var noaccum = arguments.length < 3; + callback = lodash.createCallback(callback, thisArg, 4); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + if (noaccum) { + accumulator = collection[++index]; + } + while (++index < length) { + accumulator = callback(accumulator, collection[index], index, collection); + } + } else { + baseEach(collection, function(value, index, collection) { + accumulator = noaccum + ? (noaccum = false, value) + : callback(accumulator, value, index, collection) + }); + } + return accumulator; + } + /** * The opposite of `_.filter` this method returns the elements of a * collection that the callback does **not** return truey for. @@ -10759,6 +10988,7 @@ var JXON = new (function () { lodash.merge = merge; lodash.omit = omit; lodash.pairs = pairs; + lodash.pick = pick; lodash.pluck = pluck; lodash.reject = reject; lodash.throttle = throttle; @@ -10798,6 +11028,7 @@ var JXON = new (function () { lodash.isString = isString; lodash.mixin = mixin; lodash.noop = noop; + lodash.reduce = reduce; lodash.some = some; lodash.sortedIndex = sortedIndex; @@ -10806,7 +11037,9 @@ var JXON = new (function () { lodash.any = some; lodash.detect = find; lodash.findWhere = find; + lodash.foldl = reduce; lodash.include = contains; + lodash.inject = reduce; forOwn(lodash, function(func, methodName) { if (!lodash.prototype[methodName]) { @@ -14486,7 +14719,79 @@ if (typeof define === 'function' && define.amd) { } })(); -toGeoJSON = (function() { +(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; +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= 0 ? 0 : 1], + abs = Math.abs(x), + whole = Math.floor(abs), + fraction = abs - whole, + fractionMinutes = fraction * 60, + minutes = Math.floor(fractionMinutes), + seconds = Math.floor((fractionMinutes - minutes) * 60); + + return whole + '° ' + + (minutes ? minutes + "' " : '') + + (seconds ? seconds + '" ' : '') + dir; +} + +function search(x, dims, r) { + if (!dims) dims = 'NSEW'; + if (typeof x !== 'string') return { val: null, regex: r }; + r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi; + var m = r.exec(x); + if (!m) return { val: null, regex: r }; + else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r }; + else return { + val: (((m[1]) ? parseFloat(m[1]) : 0) + + ((m[2] ? parseFloat(m[2]) / 60 : 0)) + + ((m[3] ? parseFloat(m[3]) / 3600 : 0))) * + ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1), + regex: r, + raw: m[0], + dim: m[4] + }; +} + +function pair(x, dims) { + x = x.trim(); + var one = search(x, dims); + if (one.val === null) return null; + var two = search(x, dims, one.regex); + if (two.val === null) return null; + // null if one/two are not contiguous. + if (one.raw + two.raw !== x) return null; + if (one.dim) return swapdim(one.val, two.val, one.dim); + else return [one.val, two.val]; +} + +function swapdim(a, b, dim) { + if (dim == 'N' || dim == 'S') return [a, b]; + if (dim == 'W' || dim == 'E') return [b, a]; +} + +},{}]},{},[1]) +(1) +}); +;toGeoJSON = (function() { 'use strict'; var removeSpace = (/\s*/g), @@ -16038,52 +16343,7 @@ window.iD = function () { }; /* Projection */ - function rawMercator() { - var project = d3.geo.mercator.raw, - k = 512 / Math.PI, // scale - x = 0, y = 0, // translate - clipExtent = [[0, 0], [0, 0]]; - - function projection(point) { - point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180); - return [point[0] * k + x, y - point[1] * k]; - } - - projection.invert = function(point) { - point = project.invert((point[0] - x) / k, (y - point[1]) / k); - return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI]; - }; - - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return projection; - }; - - projection.translate = function(_) { - if (!arguments.length) return [x, y]; - x = +_[0]; - y = +_[1]; - return projection; - }; - - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - return projection; - }; - - projection.stream = d3.geo.transform({ - point: function(x, y) { - x = projection([x, y]); - this.stream.point(x[0], x[1]); - } - }).stream; - - return projection; - } - - context.projection = rawMercator(); + context.projection = iD.geo.RawMercator(); /* Background */ var background = iD.Background(context); @@ -16151,7 +16411,7 @@ window.iD = function () { return d3.rebind(context, dispatch, 'on'); }; -iD.version = '1.3.9'; +iD.version = '1.5.4'; (function() { var detected = {}; @@ -16185,6 +16445,44 @@ iD.version = '1.3.9'; iD.detect = function() { return detected; }; })(); +iD.countryCode = function() { + var countryCode = {}, + endpoint = 'https://nominatim.openstreetmap.org/reverse?'; + + if (!iD.countryCode.cache) { + iD.countryCode.cache = rbush(); + } + + var cache = iD.countryCode.cache; + + countryCode.search = function(location, callback) { + var countryCodes = cache.search([location[0], location[1], location[0], location[1]]); + + if (countryCodes.length > 0) + return callback(null, countryCodes[0][4]); + + d3.json(endpoint + + iD.util.qsString({ + format: 'json', + addressdetails: 1, + lat: location[1], + lon: location[0] + }), function(err, result) { + if (err) + return callback(err); + else if (result && result.error) + return callback(result.error); + + var extent = iD.geo.Extent(location).padByMeters(1000); + + cache.insert([extent[0][0], extent[0][1], extent[1][0], extent[1][1], result.address.country_code]); + + callback(null, result.address.country_code); + }); + }; + + return countryCode; +}; iD.taginfo = function() { var taginfo = {}, endpoint = 'https://taginfo.openstreetmap.org/api/4/', @@ -16622,16 +16920,50 @@ iD.geo.interp = function(p1, p2, t) { p1[1] + (p2[1] - p1[1]) * t]; }; +// 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product. +// Returns a positive value, if OAB makes a counter-clockwise turn, +// negative for clockwise turn, and zero if the points are collinear. +iD.geo.cross = function(o, a, b) { + return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]); +}; + // http://jsperf.com/id-dist-optimization iD.geo.euclideanDistance = function(a, b) { var x = a[0] - b[0], y = a[1] - b[1]; return Math.sqrt((x * x) + (y * y)); }; + +// using WGS84 polar radius (6356752.314245179 m) +// const = 2 * PI * r / 360 +iD.geo.latToMeters = function(dLat) { + return dLat * 110946.257617; +}; + +// using WGS84 equatorial radius (6378137.0 m) +// const = 2 * PI * r / 360 +iD.geo.lonToMeters = function(dLon, atLat) { + return Math.abs(atLat) >= 90 ? 0 : + dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180))); +}; + +// using WGS84 polar radius (6356752.314245179 m) +// const = 2 * PI * r / 360 +iD.geo.metersToLat = function(m) { + return m / 110946.257617; +}; + +// using WGS84 equatorial radius (6378137.0 m) +// const = 2 * PI * r / 360 +iD.geo.metersToLon = function(m, atLat) { + return Math.abs(atLat) >= 90 ? 0 : + m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180))); +}; + // Equirectangular approximation of spherical distances on Earth iD.geo.sphericalDistance = function(a, b) { - var x = Math.cos(a[1]*Math.PI/180) * (a[0] - b[0]), - y = a[1] - b[1]; - return 6.3710E6 * Math.sqrt((x * x) + (y * y)) * Math.PI/180; + var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2), + y = iD.geo.latToMeters(a[1] - b[1]); + return Math.sqrt((x * x) + (y * y)); }; iD.geo.edgeEqual = function(a, b) { @@ -16639,6 +16971,14 @@ iD.geo.edgeEqual = function(a, b) { (a[0] === b[1] && a[1] === b[0]); }; +// Return the counterclockwise angle in the range (-pi, pi) +// between the positive X axis and the line intersecting a and b. +iD.geo.angle = function(a, b, projection) { + a = projection(a.loc); + b = projection(b.loc); + return Math.atan2(b[1] - a[1], b[0] - a[0]); +}; + // Choose the edge with the minimal distance from `point` to its orthogonal // projection onto that edge, if such a projection exists, or the distance to // the closest vertex on that edge. Returns an object with the `index` of the @@ -16685,6 +17025,39 @@ iD.geo.chooseEdge = function(nodes, point, projection) { }; }; +// Return the intersection point of 2 line segments. +// From https://github.com/pgkelley4/line-segments-intersect +// This uses the vector cross product approach described below: +// http://stackoverflow.com/a/565282/786339 +iD.geo.lineIntersection = function(a, b) { + function subtractPoints(point1, point2) { + return [point1[0] - point2[0], point1[1] - point2[1]]; + } + function crossProduct(point1, point2) { + return point1[0] * point2[1] - point1[1] * point2[0]; + } + + var p = [a[0][0], a[0][1]], + p2 = [a[1][0], a[1][1]], + q = [b[0][0], b[0][1]], + q2 = [b[1][0], b[1][1]], + r = subtractPoints(p2, p), + s = subtractPoints(q2, q), + uNumerator = crossProduct(subtractPoints(q, p), r), + denominator = crossProduct(r, s); + + if (uNumerator && denominator) { + var u = uNumerator / denominator, + t = crossProduct(subtractPoints(q, p), s) / denominator; + + if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) { + return iD.geo.interp(p, p2, t); + } + } + + return null; +}; + // Return whether point is contained in polygon. // // `point` should be a 2-item array of coordinates. @@ -16792,9 +17165,21 @@ _.extend(iD.geo.Extent.prototype, { Math.min(obj[1][1], this[1][1])]); }, + percentContainedIn: function(obj) { + if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj); + var a1 = this.intersection(obj).area(), + a2 = this.area(); + + if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) { + return 0; + } else { + return a1 / a2; + } + }, + padByMeters: function(meters) { - var dLat = meters / 111200, - dLon = meters / 111200 / Math.abs(Math.cos(this.center()[1])); + var dLat = iD.geo.metersToLat(meters), + dLon = iD.geo.metersToLon(meters, this.center()[1]); return iD.geo.Extent( [this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]); @@ -16803,7 +17188,148 @@ _.extend(iD.geo.Extent.prototype, { toParam: function() { return [this[0][0], this[0][1], this[1][0], this[1][1]].join(','); } + }); +iD.geo.Turn = function(turn) { + if (!(this instanceof iD.geo.Turn)) + return new iD.geo.Turn(turn); + _.extend(this, turn); +}; + +iD.geo.Intersection = function(graph, vertexId) { + var vertex = graph.entity(vertexId), + highways = []; + + // Pre-split ways that would need to be split in + // order to add a restriction. The real split will + // happen when the restriction is added. + graph.parentWays(vertex).forEach(function(way) { + if (!way.tags.highway || way.isArea() || way.isDegenerate()) + return; + + if (way.affix(vertexId)) { + highways.push(way); + } else { + var idx = _.indexOf(way.nodes, vertex.id, 1), + wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, idx + 1)}), + wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(idx)}); + + graph = graph.replace(wayA); + graph = graph.replace(wayB); + + highways.push(wayA); + highways.push(wayB); + } + }); + + var intersection = { + highways: highways, + graph: graph + }; + + intersection.turns = function(fromNodeID) { + if (!fromNodeID) + return []; + + var way = _.find(highways, function(way) { return way.contains(fromNodeID); }); + if (way.first() === vertex.id && way.tags.oneway === 'yes') + return []; + if (way.last() === vertex.id && way.tags.oneway === '-1') + return []; + + function withRestriction(turn) { + graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) { + if (relation.tags.type !== 'restriction') + return; + + var f = relation.memberByRole('from'), + t = relation.memberByRole('to'), + v = relation.memberByRole('via'); + + if (f && f.id === turn.from.way && + v && v.id === turn.via.node && + t && t.id === turn.to.way) { + turn.restriction = relation.id; + } else if (/^only_/.test(relation.tags.restriction) && + f && f.id === turn.from.way && + v && v.id === turn.via.node && + t && t.id !== turn.to.way) { + turn.restriction = relation.id; + turn.indirect_restriction = true; + } + }); + + return iD.geo.Turn(turn); + } + + var from = { + node: way.nodes[way.first() === vertex.id ? 1 : way.nodes.length - 2], + way: way.id.split(/-(a|b)/)[0] + }, + via = {node: vertex.id}, + turns = []; + + highways.forEach(function(parent) { + if (parent === way) + return; + + var index = parent.nodes.indexOf(vertex.id); + + // backward + if (parent.first() !== vertex.id && parent.tags.oneway !== 'yes') { + turns.push(withRestriction({ + from: from, + via: via, + to: {node: parent.nodes[index - 1], way: parent.id.split(/-(a|b)/)[0]} + })); + } + + // forward + if (parent.last() !== vertex.id && parent.tags.oneway !== '-1') { + turns.push(withRestriction({ + from: from, + via: via, + to: {node: parent.nodes[index + 1], way: parent.id.split(/-(a|b)/)[0]} + })); + } + }); + + // U-turn + if (way.tags.oneway !== 'yes' && way.tags.oneway !== '-1') { + turns.push(withRestriction({ + from: from, + via: via, + to: from, + u: true + })); + } + + return turns; + }; + + return intersection; +}; + +iD.geo.inferRestriction = function(from, via, to, projection) { + var angle = iD.geo.angle(via, from, projection) - + iD.geo.angle(via, to, projection); + + angle = angle * 180 / Math.PI; + + while (angle < 0) + angle += 360; + + if (angle < 23) + return 'no_u_turn'; + if (angle < 158) + return 'no_right_turn'; + if (angle < 202) + return 'no_straight_on'; + if (angle < 336) + return 'no_left_turn'; + + return 'no_u_turn'; +}; // For fixing up rendering of multipolygons with tags on the outer member. // https://github.com/openstreetmap/iD/issues/613 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) { @@ -16939,67 +17465,55 @@ iD.geo.joinWays = function(array, graph) { return joined; }; -iD.geo.turns = function(graph, entityID) { - var way = graph.entity(entityID); - if (way.type !== 'way' || !way.tags.highway || way.isArea()) - return []; - - function withRestriction(turn) { - graph.parentRelations(turn.from).forEach(function(relation) { - if (relation.tags.type !== 'restriction') - return; - - var f = relation.memberByRole('from'), - t = relation.memberByRole('to'), - v = relation.memberByRole('via'); - - if (f && f.id === turn.from.id && - t && t.id === turn.to.id && - v && v.id === turn.via.id) { - turn.restriction = relation; - } - }); +/* + Bypasses features of D3's default projection stream pipeline that are unnecessary: + * Antimeridian clipping + * Spherical rotation + * Resampling +*/ +iD.geo.RawMercator = function () { + var project = d3.geo.mercator.raw, + k = 512 / Math.PI, // scale + x = 0, y = 0, // translate + clipExtent = [[0, 0], [0, 0]]; - return turn; + function projection(point) { + point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180); + return [point[0] * k + x, y - point[1] * k]; } - var turns = []; + projection.invert = function(point) { + point = project.invert((point[0] - x) / k, (y - point[1]) / k); + return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI]; + }; - [way.first(), way.last()].forEach(function(nodeID) { - var node = graph.entity(nodeID); - graph.parentWays(node).forEach(function(parent) { - if (parent === way || parent.isDegenerate() || !parent.tags.highway) - return; - if (way.first() === node.id && way.tags.oneway === 'yes') - return; - if (way.last() === node.id && way.tags.oneway === '-1') - return; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return projection; + }; - var index = parent.nodes.indexOf(node.id); + projection.translate = function(_) { + if (!arguments.length) return [x, y]; + x = +_[0]; + y = +_[1]; + return projection; + }; - // backward - if (parent.first() !== node.id && parent.tags.oneway !== 'yes') { - turns.push(withRestriction({ - from: way, - to: parent, - via: node, - toward: graph.entity(parent.nodes[index - 1]) - })); - } + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + return projection; + }; - // forward - if (parent.last() !== node.id && parent.tags.oneway !== '-1') { - turns.push(withRestriction({ - from: way, - to: parent, - via: node, - toward: graph.entity(parent.nodes[index + 1]) - })); - } - }); - }); + projection.stream = d3.geo.transform({ + point: function(x, y) { + x = projection([x, y]); + this.stream.point(x[0], x[1]); + } + }).stream; - return turns; + return projection; }; iD.actions = {}; iD.actions.AddEntity = function(way) { @@ -17092,12 +17606,17 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) { maxAngle = (maxAngle || 20) * Math.PI / 180; var action = function(graph) { - var way = graph.entity(wayId), - nodes = _.uniq(graph.childNodes(way)), + var way = graph.entity(wayId); + + if (!way.isConvex(graph)) { + graph = action.makeConvex(graph); + } + + var nodes = _.uniq(graph.childNodes(way)), keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }), points = nodes.map(function(n) { return projection(n.loc); }), keyPoints = keyNodes.map(function(n) { return projection(n.loc); }), - centroid = d3.geom.polygon(points).centroid(), + centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(), radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }), sign = d3.geom.polygon(points).area() > 0 ? 1 : -1, ids; @@ -17118,16 +17637,19 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) { // key points and nodes are those connected to the ways, // they are projected onto the circle, inbetween nodes are moved - // to constant internals between key nodes, extra inbetween nodes are + // to constant intervals between key nodes, extra inbetween nodes are // added if necessary. for (var i = 0; i < keyPoints.length; i++) { var nextKeyNodeIndex = (i + 1) % keyNodes.length, - startNodeIndex = nodes.indexOf(keyNodes[i]), - endNodeIndex = nodes.indexOf(keyNodes[nextKeyNodeIndex]), + startNode = keyNodes[i], + endNode = keyNodes[nextKeyNodeIndex], + startNodeIndex = nodes.indexOf(startNode), + endNodeIndex = nodes.indexOf(endNode), numberNewPoints = -1, indexRange = endNodeIndex - startNodeIndex, distance, totalAngle, eachAngle, startAngle, endAngle, - angle, loc, node, j; + angle, loc, node, j, + inBetweenNodes = []; if (indexRange < 0) { indexRange += nodes.length; @@ -17135,6 +17657,7 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) { // position this key node distance = iD.geo.euclideanDistance(centroid, keyPoints[i]); + if (distance === 0) { distance = 1e-4; } keyPoints[i] = [ centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius]; @@ -17146,7 +17669,7 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) { totalAngle = endAngle - startAngle; // detects looping around -pi/pi - if (totalAngle*sign > 0) { + if (totalAngle * sign > 0) { totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle)); } @@ -17177,7 +17700,40 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) { graph = graph.replace(node); nodes.splice(endNodeIndex + j, 0, node); + inBetweenNodes.push(node.id); } + + // Check for other ways that share these keyNodes.. + // If keyNodes are adjacent in both ways, + // we can add inBetween nodes to that shared way too.. + if (indexRange === 1 && inBetweenNodes.length) { + var startIndex1 = way.nodes.lastIndexOf(startNode.id), + endIndex1 = way.nodes.lastIndexOf(endNode.id), + wayDirection1 = (endIndex1 - startIndex1); + if (wayDirection1 < -1) { wayDirection1 = 1;} + + /*jshint -W083 */ + _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) { + if (sharedWay.areAdjacent(startNode.id, endNode.id)) { + var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id), + endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id), + wayDirection2 = (endIndex2 - startIndex2), + insertAt = endIndex2; + if (wayDirection2 < -1) { wayDirection2 = 1;} + + if (wayDirection1 !== wayDirection2) { + inBetweenNodes.reverse(); + insertAt = startIndex2; + } + for (j = 0; j < inBetweenNodes.length; j++) { + sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j); + } + graph = graph.replace(sharedWay); + } + }); + /*jshint +W083 */ + } + } // update the way to have all the new nodes @@ -17190,6 +17746,38 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) { return graph; }; + action.makeConvex = function(graph) { + var way = graph.entity(wayId), + nodes = _.uniq(graph.childNodes(way)), + points = nodes.map(function(n) { return projection(n.loc); }), + sign = d3.geom.polygon(points).area() > 0 ? 1 : -1, + hull = d3.geom.hull(points); + + // D3 convex hulls go counterclockwise.. + if (sign === -1) { + nodes.reverse(); + points.reverse(); + } + + for (var i = 0; i < hull.length - 1; i++) { + var startIndex = points.indexOf(hull[i]), + endIndex = points.indexOf(hull[i+1]), + indexRange = (endIndex - startIndex); + + if (indexRange < 0) { + indexRange += nodes.length; + } + + // move interior nodes to the surface of the convex hull.. + for (var j = 1; j < indexRange; j++) { + var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange), + node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point)); + graph = graph.replace(node); + } + } + return graph; + }; + action.disabled = function(graph) { if (!graph.entity(wayId).isClosed()) return 'not_closed'; @@ -17241,7 +17829,15 @@ iD.actions.Connect = function(nodeIds) { }; iD.actions.DeleteMember = function(relationId, memberIndex) { return function(graph) { - return graph.replace(graph.entity(relationId).removeMember(memberIndex)); + var relation = graph.entity(relationId) + .removeMember(memberIndex); + + graph = graph.replace(relation); + + if (relation.isDegenerate()) + graph = iD.actions.DeleteRelation(relation.id)(graph); + + return graph; }; }; iD.actions.DeleteMultiple = function(ids) { @@ -17971,6 +18567,95 @@ iD.actions.Orthogonalize = function(wayId, projection) { return action; }; +// Create a restriction relation for `turn`, which must have the following structure: +// +// { +// from: { node: , way: }, +// via: { node: }, +// to: { node: , way: }, +// restriction: <'no_right_turn', 'no_left_turn', etc.> +// } +// +// This specifies a restriction of type `restriction` when traveling from +// `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`. +// (The action does not check that these entities form a valid intersection.) +// +// If `restriction` is not provided, it is automatically determined by the +// angle of the turn: +// +// 0-23 degrees: no_u_turn +// 23-158 degrees: no_right_turn +// 158-202 degrees: no_straight_on +// 202-326 degrees: no_left_turn +// 336-360 degrees: no_u_turn +// +// If necessary, the `from` and `to` ways are split. In these cases, `from.node` +// and `to.node` are used to determine which portion of the split ways become +// members of the restriction. +// +// For testing convenience, accepts an ID to assign to the new relation. +// Normally, this will be undefined and the relation will automatically +// be assigned a new ID. +// +iD.actions.RestrictTurn = function(turn, projection, restrictionId) { + return function(graph) { + var from = graph.entity(turn.from.way), + via = graph.entity(turn.via.node), + to = graph.entity(turn.to.way); + + function split(toOrFrom) { + var newID = toOrFrom.newID || iD.Way().id; + graph = iD.actions.Split(via.id, [newID]) + .limitWays([toOrFrom.way])(graph); + + var a = graph.entity(newID), + b = graph.entity(toOrFrom.way); + + if (a.nodes.indexOf(toOrFrom.node) !== -1) { + return [a, b]; + } else { + return [b, a]; + } + } + + if (!from.affix(via.id)) { + if (turn.from.node === turn.to.node) { + // U-turn + from = to = split(turn.from)[0]; + } else if (turn.from.way === turn.to.way) { + // Straight-on + var s = split(turn.from); + from = s[0]; + to = s[1]; + } else { + // Other + from = split(turn.from)[0]; + } + } + + if (!to.affix(via.id)) { + to = split(turn.to)[0]; + } + + return graph.replace(iD.Relation({ + id: restrictionId, + tags: { + type: 'restriction', + restriction: turn.restriction || + iD.geo.inferRestriction( + graph.entity(turn.from.node), + via, + graph.entity(turn.to.node), + projection) + }, + members: [ + {id: from.id, type: 'way', role: 'from'}, + {id: via.id, type: 'node', role: 'via'}, + {id: to.id, type: 'way', role: 'to'} + ] + })); + }; +}; /* Order the nodes of a way in reverse order and reverse any direction dependent tags other than `oneway`. (We assume that correcting a backwards oneway is the primary @@ -18345,6 +19030,29 @@ iD.actions.Straighten = function(wayId, projection) { return action; }; +// Remove the effects of `turn.restriction` on `turn`, which must have the +// following structure: +// +// { +// from: { node: , way: }, +// via: { node: }, +// to: { node: , way: }, +// restriction: +// } +// +// In the simple case, `restriction` is a reference to a `no_*` restriction +// on the turn itself. In this case, it is simply deleted. +// +// The more complex case is where `restriction` references an `only_*` +// restriction on a different turn in the same intersection. In that case, +// that restriction is also deleted, but at the same time restrictions on +// the turns other than the first two are created. +// +iD.actions.UnrestrictTurn = function(turn) { + return function(graph) { + return iD.actions.DeleteRelation(turn.restriction)(graph); + }; +}; iD.behavior = {}; iD.behavior.AddWay = function(context) { var event = d3.dispatch('start', 'startFromWay', 'startFromNode'), @@ -18475,6 +19183,9 @@ iD.behavior.drag = function() { var p = point(), dx = p[0] - origin_[0], dy = p[1] - origin_[1]; + + if (dx === 0 && dy === 0) + return; if (!started) { started = true; @@ -18951,15 +19662,29 @@ iD.behavior.Hash = function(context) { }; var formatter = function(map) { - var center = map.center(), + var mode = context.mode(), + center = map.center(), zoom = map.zoom(), - precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); - var q = iD.util.stringQs(location.hash.substring(1)); - return '#' + iD.util.qsString(_.assign(q, { - map: zoom.toFixed(2) + - '/' + center[0].toFixed(precision) + - '/' + center[1].toFixed(precision) - }), true); + precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)), + q = iD.util.stringQs(location.hash.substring(1)), + newParams = {}; + + if (mode && mode.id === 'browse') { + delete q.id; + } else { + var selected = context.selectedIDs().filter(function(id) { + return !context.entity(id).isNew(); + }); + if (selected.length) { + newParams.id = selected.join(','); + } + } + + newParams.map = zoom.toFixed(2) + + '/' + center[0].toFixed(precision) + + '/' + center[1].toFixed(precision); + + return '#' + iD.util.qsString(_.assign(q, newParams), true); }; function update() { @@ -18967,7 +19692,7 @@ iD.behavior.Hash = function(context) { if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map! } - var move = _.throttle(update, 500); + var throttledUpdate = _.throttle(update, 500); function hashchange() { if (location.hash === s0) return; // ignore spurious hashchange events @@ -18978,14 +19703,17 @@ iD.behavior.Hash = function(context) { function hash() { context.map() - .on('move.hash', move); + .on('move.hash', throttledUpdate); + + context + .on('enter.hash', throttledUpdate); d3.select(window) .on('hashchange.hash', hashchange); if (location.hash) { var q = iD.util.stringQs(location.hash.substring(1)); - if (q.id) context.loadEntity(q.id, !q.map); + if (q.id) context.loadEntity(q.id.split(',')[0], !q.map); hashchange(); if (q.map) hash.hadHash = true; } @@ -18995,6 +19723,9 @@ iD.behavior.Hash = function(context) { context.map() .on('move.hash', null); + context + .on('enter.hash', null); + d3.select(window) .on('hashchange.hash', null); @@ -19148,8 +19879,6 @@ iD.behavior.Lasso = function(context) { .on('mouseup.lasso', mouseup); d3.event.stopPropagation(); - d3.event.preventDefault(); - } } @@ -19532,8 +20261,7 @@ iD.modes.Browse = function(context) { button: 'browse', id: 'browse', title: t('modes.browse.title'), - description: t('modes.browse.description'), - key: '1' + description: t('modes.browse.description') }, sidebar; var behaviors = [ @@ -20244,17 +20972,6 @@ iD.modes.Select = function(context, selectedIDs) { }); }); - var notNew = selectedIDs.filter(function(id) { - return !context.entity(id).isNew(); - }); - - if (notNew.length) { - var q = iD.util.stringQs(location.hash.substring(1)); - location.replace('#' + iD.util.qsString(_.assign(q, { - id: notNew.join(',') - }), true)); - } - context.ui().sidebar .select(singular() ? singular().id : null, newFeature); @@ -20338,9 +21055,6 @@ iD.modes.Select = function(context, selectedIDs) { context.uninstall(behavior); }); - var q = iD.util.stringQs(location.hash.substring(1)); - location.replace('#' + iD.util.qsString(_.omit(q, 'id'), true)); - keybinding.off(); context.history() @@ -20362,6 +21076,8 @@ iD.modes.Select = function(context, selectedIDs) { iD.operations = {}; iD.operations.Circularize = function(selectedIDs, context) { var entityId = selectedIDs[0], + entity = context.entity(entityId), + extent = entity.extent(context.graph()), geometry = context.geometry(entityId), action = iD.actions.Circularize(entityId, context.projection); @@ -20372,21 +21088,16 @@ iD.operations.Circularize = function(selectedIDs, context) { operation.available = function() { return selectedIDs.length === 1 && - context.entity(entityId).type === 'way'; + entity.type === 'way' && + _.uniq(entity.nodes).length > 1; }; operation.disabled = function() { - var way = context.entity(entityId), - wayExtent = way.extent(context.graph()), - mapExtent = context.extent(), - intersection = mapExtent.intersection(wayExtent), - pctVisible = intersection.area() / wayExtent.area(); - - if (pctVisible < 0.8) { - return 'too_large'; - } else { - return action.disabled(context.graph()); + var reason; + if (extent.percentContainedIn(context.extent()) < 0.8) { + reason = 'too_large'; } + return action.disabled(context.graph()) || reason; }; operation.tooltip = function() { @@ -20489,15 +21200,15 @@ iD.operations.Delete = function(selectedIDs, context) { } } - context.perform( - action, - annotation); - if (nextSelectedID && context.hasEntity(nextSelectedID)) { context.enter(iD.modes.Select(context, [nextSelectedID])); } else { context.enter(iD.modes.Browse(context)); } + + context.perform( + action, + annotation); }; operation.available = function() { @@ -20614,6 +21325,10 @@ iD.operations.Merge = function(selectedIDs, context) { return operation; }; iD.operations.Move = function(selectedIDs, context) { + var extent = selectedIDs.reduce(function(extent, id) { + return extent.extend(context.entity(id).extent(context.graph())); + }, iD.geo.Extent()); + var operation = function() { context.enter(iD.modes.Move(context, selectedIDs)); }; @@ -20624,8 +21339,11 @@ iD.operations.Move = function(selectedIDs, context) { }; operation.disabled = function() { - return iD.actions.Move(selectedIDs) - .disabled(context.graph()); + var reason; + if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) { + reason = 'too_large'; + } + return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason; }; operation.tooltip = function() { @@ -20643,16 +21361,17 @@ iD.operations.Move = function(selectedIDs, context) { }; iD.operations.Orthogonalize = function(selectedIDs, context) { var entityId = selectedIDs[0], + entity = context.entity(entityId), + extent = entity.extent(context.graph()), geometry = context.geometry(entityId), action = iD.actions.Orthogonalize(entityId, context.projection); - function operation() { + var operation = function() { var annotation = t('operations.orthogonalize.annotation.' + geometry); context.perform(action, annotation); - } + }; operation.available = function() { - var entity = context.entity(entityId); return selectedIDs.length === 1 && entity.type === 'way' && entity.isClosed() && @@ -20660,17 +21379,11 @@ iD.operations.Orthogonalize = function(selectedIDs, context) { }; operation.disabled = function() { - var way = context.entity(entityId), - wayExtent = way.extent(context.graph()), - mapExtent = context.extent(), - intersection = mapExtent.intersection(wayExtent), - pctVisible = intersection.area() / wayExtent.area(); - - if (pctVisible < 0.8) { - return 'too_large'; - } else { - return action.disabled(context.graph()); + var reason; + if (extent.percentContainedIn(context.extent()) < 0.8) { + reason = 'too_large'; } + return action.disabled(context.graph()) || reason; }; operation.tooltip = function() { @@ -20715,33 +21428,39 @@ iD.operations.Reverse = function(selectedIDs, context) { return operation; }; iD.operations.Rotate = function(selectedIDs, context) { - var entityId = selectedIDs[0]; + var entityId = selectedIDs[0], + entity = context.entity(entityId), + extent = entity.extent(context.graph()), + geometry = context.geometry(entityId); var operation = function() { context.enter(iD.modes.RotateWay(context, entityId)); }; operation.available = function() { - var graph = context.graph(), - entity = graph.entity(entityId); - - if (selectedIDs.length !== 1 || - entity.type !== 'way') + if (selectedIDs.length !== 1 || entity.type !== 'way') return false; - if (context.geometry(entityId) === 'area') + if (geometry === 'area') return true; if (entity.isClosed() && - graph.parentRelations(entity).some(function(r) { return r.isMultipolygon(); })) + context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); })) return true; return false; }; operation.disabled = function() { - return false; + if (extent.percentContainedIn(context.extent()) < 0.8) { + return 'too_large'; + } else { + return false; + } }; operation.tooltip = function() { - return t('operations.rotate.description'); + var disable = operation.disabled(); + return disable ? + t('operations.rotate.' + disable) : + t('operations.rotate.description'); }; operation.id = 'rotate'; @@ -20850,6 +21569,7 @@ iD.areaKeys = { "bench": true, "clock": true, "drinking_water": true, + "parking_entrance": true, "post_box": true, "telephone": true, "vending_machine": true, @@ -21000,7 +21720,7 @@ iD.areaKeys = { var elems = obj.getElementsByTagName(ndStr), nodes = new Array(elems.length); for (var i = 0, l = elems.length; i < l; i++) { - nodes[i] = 'n' + elems[i].attributes.ref.nodeValue; + nodes[i] = 'n' + elems[i].attributes.ref.value; } return nodes; } @@ -21010,7 +21730,7 @@ iD.areaKeys = { tags = {}; for (var i = 0, l = elems.length; i < l; i++) { var attrs = elems[i].attributes; - tags[attrs.k.nodeValue] = attrs.v.nodeValue; + tags[attrs.k.value] = attrs.v.value; } return tags; } @@ -21021,9 +21741,9 @@ iD.areaKeys = { for (var i = 0, l = elems.length; i < l; i++) { var attrs = elems[i].attributes; members[i] = { - id: attrs.type.nodeValue[0] + attrs.ref.nodeValue, - type: attrs.type.nodeValue, - role: attrs.role.nodeValue + id: attrs.type.value[0] + attrs.ref.value, + type: attrs.type.value, + role: attrs.role.value }; } return members; @@ -21033,10 +21753,10 @@ iD.areaKeys = { node: function nodeData(obj) { var attrs = obj.attributes; return new iD.Node({ - id: iD.Entity.id.fromOSM(nodeStr, attrs.id.nodeValue), - loc: [parseFloat(attrs.lon.nodeValue), parseFloat(attrs.lat.nodeValue)], - version: attrs.version.nodeValue, - user: attrs.user && attrs.user.nodeValue, + id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value), + loc: [parseFloat(attrs.lon.value), parseFloat(attrs.lat.value)], + version: attrs.version.value, + user: attrs.user && attrs.user.value, tags: getTags(obj) }); }, @@ -21044,9 +21764,9 @@ iD.areaKeys = { way: function wayData(obj) { var attrs = obj.attributes; return new iD.Way({ - id: iD.Entity.id.fromOSM(wayStr, attrs.id.nodeValue), - version: attrs.version.nodeValue, - user: attrs.user && attrs.user.nodeValue, + id: iD.Entity.id.fromOSM(wayStr, attrs.id.value), + version: attrs.version.value, + user: attrs.user && attrs.user.value, tags: getTags(obj), nodes: getNodes(obj) }); @@ -21055,9 +21775,9 @@ iD.areaKeys = { relation: function relationData(obj) { var attrs = obj.attributes; return new iD.Relation({ - id: iD.Entity.id.fromOSM(relationStr, attrs.id.nodeValue), - version: attrs.version.nodeValue, - user: attrs.user && attrs.user.nodeValue, + id: iD.Entity.id.fromOSM(relationStr, attrs.id.value), + version: attrs.version.value, + user: attrs.user && attrs.user.value, tags: getTags(obj), members: getMembers(obj) }); @@ -21135,7 +21855,7 @@ iD.areaKeys = { connection.changesetTags = function(comment, imageryUsed) { var tags = { - imagery_used: imageryUsed.join(';'), + imagery_used: imageryUsed.join(';').substr(0, 255), created_by: 'iD ' + iD.version }; @@ -21191,9 +21911,9 @@ iD.areaKeys = { } userDetails = { - display_name: u.attributes.display_name.nodeValue, + display_name: u.attributes.display_name.value, image_url: image_url, - id: u.attributes.id.nodeValue + id: u.attributes.id.value }; callback(undefined, userDetails); @@ -21617,6 +22337,10 @@ iD.Entity.prototype = { }); }, + isHighwayIntersection: function() { + return false; + }, + deprecatedTags: function() { var tags = _.pairs(this.tags); var deprecated = {}; @@ -22265,6 +22989,14 @@ _.extend(iD.Node.prototype, { }); }, + isHighwayIntersection: function(resolver) { + return resolver.transient(this, 'isHighwayIntersection', function() { + return resolver.parentWays(this).filter(function(parent) { + return parent.tags.highway && parent.geometry(resolver) === 'line'; + }).length > 1; + }); + }, + asJXON: function(changeset_id) { var r = { node: { @@ -22288,6 +23020,37 @@ _.extend(iD.Node.prototype, { }; } }); +iD.oneWayTags = { + 'aerialway': { + 'chair_lift': true, + 'mixed_lift': true, + 't-bar': true, + 'j-bar': true, + 'platter': true, + 'rope_tow': true, + 'magic_carpet': true, + 'yes': true + }, + 'highway': { + 'motorway': true, + 'motorway_link': true + }, + 'junction': { + 'roundabout': true + }, + 'man_made': { + 'piste:halfpipe': true + }, + 'piste:type': { + 'downhill': true, + 'sled': true, + 'yes': true + }, + 'waterway': { + 'river': true, + 'stream': true + } +}; iD.Relation = iD.Entity.relation = function iD_Relation() { if (!(this instanceof iD_Relation)) { return (new iD_Relation()).initialize(arguments); @@ -22676,19 +23439,70 @@ _.extend(iD.Way.prototype, { if (this.nodes[this.nodes.length - 1] === node) return 'suffix'; }, + layer: function() { + // explicit layer tag, clamp between -10, 10.. + if (this.tags.layer !== undefined) { + return Math.max(-10, Math.min(+(this.tags.layer), 10)); + } + + // implied layer tag.. + if (this.tags.location === 'overground') return 1; + if (this.tags.location === 'underground') return -1; + if (this.tags.location === 'underwater') return -10; + + if (this.tags.power === 'line') return 10; + if (this.tags.power === 'minor_line') return 10; + if (this.tags.aerialway) return 10; + if (this.tags.bridge) return 1; + if (this.tags.cutting) return -1; + if (this.tags.tunnel) return -1; + if (this.tags.waterway) return -1; + if (this.tags.man_made === 'pipeline') return -10; + if (this.tags.boundary) return -10; + return 0; + }, + isOneWay: function() { - return this.tags.oneway === 'yes' || - this.tags.oneway === '1' || - this.tags.oneway === '-1' || - this.tags.waterway === 'river' || - this.tags.waterway === 'stream' || - this.tags.junction === 'roundabout'; + // explicit oneway tag.. + if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; } + if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; } + + // implied oneway tag.. + for (var key in this.tags) { + if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key])) + return true; + } + return false; }, isClosed: function() { return this.nodes.length > 0 && this.first() === this.last(); }, + isConvex: function(resolver) { + if (!this.isClosed() || this.isDegenerate()) return null; + + var nodes = _.uniq(resolver.childNodes(this)), + coords = _.pluck(nodes, 'loc'), + curr = 0, prev = 0; + + for (var i = 0; i < coords.length; i++) { + var o = coords[(i+1) % coords.length], + a = coords[i], + b = coords[(i+2) % coords.length], + res = iD.geo.cross(o, a, b); + + curr = (res > 0) ? 1 : (res < 0) ? -1 : 0; + if (curr === 0) { + continue; + } else if (prev && curr !== prev) { + return false; + } + prev = curr; + } + return true; + }, + isArea: function() { if (this.tags.area === 'yes') return true; @@ -22974,8 +23788,15 @@ iD.Background = function(context) { background.zoomToGpxLayer = function() { if (background.hasGpxLayer()) { - context.map() - .extent(d3.geo.bounds(gpxLayer.geojson())); + var viewport = context.map().extent().polygon(), + coords = _.reduce(gpxLayer.geojson().features, function(coords, feature) { + var c = feature.geometry.coordinates; + return _.union(coords, feature.geometry.type === 'Point' ? [c] : c); + }, []); + + if (!iD.geo.polygonIntersectsPolygon(viewport, coords)) { + context.map().extent(d3.geo.bounds(gpxLayer.geojson())); + } } }; @@ -23250,7 +24071,7 @@ iD.GpxLayer = function(context) { .append('text') .attr('class', 'gpx') .text(function(d) { - return d.properties.name; + return d.properties.desc || d.properties.name; }) .attr('x', function(d) { var centroid = path.centroid(d); @@ -23387,10 +24208,9 @@ iD.Map = function(context) { if (map.editable() && !transformed) { var all = context.intersects(map.extent()), filter = d3.functor(true), - extent = map.extent(), graph = context.graph(); - surface.call(vertices, graph, all, filter, extent, map.zoom()); - surface.call(midpoints, graph, all, filter, extent); + surface.call(vertices, graph, all, filter, map.extent(), map.zoom()); + surface.call(midpoints, graph, all, filter, map.trimmedExtent()); dispatch.drawn({full: false}); } }); @@ -23409,30 +24229,7 @@ iD.Map = function(context) { if (difference) { var complete = difference.complete(map.extent()); all = _.compact(_.values(complete)); - filter = function(d) { - if (d.type === 'midpoint') { - - var a = d.edge[0], - b = d.edge[1]; - - // redraw a midpoint if it needs to be - // - moved (either edge node moved) - // - deleted (edge nodes not consecutive in any parent way) - if (a in complete || b in complete) return true; - - var parentsWays = graph.parentWays({ id: a }); - for (var i = 0; i < parentsWays.length; i++) { - var nodes = parentsWays[i].nodes; - for (var n = 0; n < nodes.length; n++) { - if (nodes[n] === a && (nodes[n - 1] === b || nodes[n + 1] === b)) return false; - } - } - return true; - - } else { - return d.id in complete; - } - }; + filter = function(d) { return d.id in complete; }; } else if (extent) { all = context.intersects(map.extent().intersection(extent)); @@ -23448,7 +24245,7 @@ iD.Map = function(context) { .call(vertices, graph, all, filter, map.extent(), map.zoom()) .call(lines, graph, all, filter) .call(areas, graph, all, filter) - .call(midpoints, graph, all, filter, map.extent()) + .call(midpoints, graph, all, filter, map.trimmedExtent()) .call(labels, graph, all, filter, dimensions, !difference && !extent); if (points.points(context.intersects(map.extent()), 100).length >= 100) { @@ -23461,8 +24258,12 @@ iD.Map = function(context) { } function editOff() { + var mode = context.mode(); surface.selectAll('.layer *').remove(); dispatch.drawn({full: true}); + if (!(mode && mode.id === 'browse')) { + context.enter(iD.modes.Browse(context)); + } } function zoomPan() { @@ -23700,6 +24501,12 @@ iD.Map = function(context) { } }; + map.trimmedExtent = function() { + var headerY = 60, footerY = 30, pad = 10; + return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]), + projection.invert([dimensions[0] - pad, headerY + pad])); + }; + map.extentZoom = function(_) { var extent = iD.geo.Extent(_), tl = projection([extent[0][0], extent[1][1]]), @@ -23798,6 +24605,7 @@ iD.TileLayer = function() { tile().forEach(function(d) { addSource(d); if (d[3] === '') return; + if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295 requests.push(d); if (cache[d[3]] === false && lookUp(d)) { requests.push(addSource(lookUp(d))); @@ -23936,6 +24744,7 @@ iD.svg = { i = 0, offset = dt, segments = [], + viewport = iD.geo.Extent(projection.clipExtent()), coordinates = graph.childNodes(entity).map(function(n) { return n.loc; }); @@ -23954,9 +24763,10 @@ iD.svg = { b = [x, y]; if (a) { - var span = iD.geo.euclideanDistance(a, b) - offset; + var extent = iD.geo.Extent(a).extend(b), + span = iD.geo.euclideanDistance(a, b) - offset; - if (span >= 0) { + if (extent.intersects(viewport) && span >= 0) { var angle = Math.atan2(b[1] - a[1], b[0] - a[0]), dx = dt * Math.cos(angle), dy = dt * Math.sin(angle), @@ -24008,6 +24818,7 @@ iD.svg.Areas = function(projection) { beach: 'beach', scrub: 'scrub', construction: 'construction', + military: 'construction', cemetery: 'cemetery', grave_yard: 'cemetery', meadow: 'meadow', @@ -24018,6 +24829,12 @@ iD.svg.Areas = function(projection) { var patternKeys = ['landuse', 'natural', 'amenity']; + var clipped = ['residential', 'commercial', 'retail', 'industrial']; + + function clip(entity) { + return clipped.indexOf(entity.tags.landuse) !== -1; + } + function setPattern(d) { for (var i = 0; i < patternKeys.length; i++) { if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) { @@ -24060,13 +24877,39 @@ iD.svg.Areas = function(projection) { }); var data = { + clip: areas.filter(clip), shadow: strokes, stroke: strokes, fill: areas }; - var paths = surface.selectAll('.layer-shadow, .layer-stroke, .layer-fill') - .selectAll('path.area') + var clipPaths = surface.selectAll('defs').selectAll('.clipPath') + .filter(filter) + .data(data.clip, iD.Entity.key); + + clipPaths.enter() + .append('clipPath') + .attr('class', 'clipPath') + .attr('id', function(entity) { return entity.id + '-clippath'; }) + .append('path'); + + clipPaths.selectAll('path') + .attr('d', path); + + clipPaths.exit() + .remove(); + + var areagroup = surface + .select('.layer-areas') + .selectAll('g.areagroup') + .data(['fill', 'shadow', 'stroke']); + + areagroup.enter() + .append('g') + .attr('class', function(d) { return 'layer areagroup area-' + d; }); + + var paths = areagroup + .selectAll('path') .filter(filter) .data(function(layer) { return data[layer]; }, iD.Entity.key); @@ -24075,7 +24918,7 @@ iD.svg.Areas = function(projection) { paths.exit() .remove(); - var fills = surface.selectAll('.layer-fill path.area')[0]; + var fills = surface.selectAll('.area-fill path.area')[0]; var bisect = d3.bisector(function(node) { return -node.__data__.area(graph); @@ -24094,6 +24937,10 @@ iD.svg.Areas = function(projection) { this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id); + if (layer === 'fill' && clip(entity)) { + this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)'); + } + if (layer === 'fill') { setPattern.apply(this, arguments); } @@ -24104,6 +24951,135 @@ iD.svg.Areas = function(projection) { .attr('d', path); }; }; +/* + A standalone SVG element that contains only a `defs` sub-element. To be + used once globally, since defs IDs must be unique within a document. +*/ +iD.svg.Defs = function(context) { + function autosize(image) { + var img = document.createElement('img'); + img.src = image.attr('xlink:href'); + img.onload = function() { + image.attr({ + width: img.width, + height: img.height + }); + }; + } + + function SpriteDefinition(id, href, data) { + return function(defs) { + defs.append('image') + .attr('id', id) + .attr('xlink:href', href) + .call(autosize); + + defs.selectAll() + .data(data) + .enter().append('use') + .attr('id', function(d) { return d.key; }) + .attr('transform', function(d) { return 'translate(-' + d.value[0] + ',-' + d.value[1] + ')'; }) + .attr('xlink:href', '#' + id); + }; + } + + return function (selection) { + var defs = selection.append('defs'); + + defs.append('marker') + .attr({ + id: 'oneway-marker', + viewBox: '0 0 10 10', + refY: 2.5, + refX: 5, + markerWidth: 2, + markerHeight: 2, + orient: 'auto' + }) + .append('path') + .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'); + + var patterns = defs.selectAll('pattern') + .data([ + // pattern name, pattern image name + ['wetland', 'wetland'], + ['construction', 'construction'], + ['cemetery', 'cemetery'], + ['orchard', 'orchard'], + ['farmland', 'farmland'], + ['beach', 'dots'], + ['scrub', 'dots'], + ['meadow', 'dots'] + ]) + .enter() + .append('pattern') + .attr({ + id: function (d) { + return 'pattern-' + d[0]; + }, + width: 32, + height: 32, + patternUnits: 'userSpaceOnUse' + }); + + patterns.append('rect') + .attr({ + x: 0, + y: 0, + width: 32, + height: 32, + 'class': function (d) { + return 'pattern-color-' + d[0]; + } + }); + + patterns.append('image') + .attr({ + x: 0, + y: 0, + width: 32, + height: 32 + }) + .attr('xlink:href', function (d) { + return context.imagePath('pattern/' + d[1] + '.png'); + }); + + defs.selectAll() + .data([12, 18, 20, 32, 45]) + .enter().append('clipPath') + .attr('id', function (d) { + return 'clip-square-' + d; + }) + .append('rect') + .attr('x', 0) + .attr('y', 0) + .attr('width', function (d) { + return d; + }) + .attr('height', function (d) { + return d; + }); + + var maki = []; + _.forEach(iD.data.featureIcons, function (dimensions, name) { + if (dimensions['12'] && dimensions['18'] && dimensions['24']) { + maki.push({key: 'maki-' + name + '-12', value: dimensions['12']}); + maki.push({key: 'maki-' + name + '-18', value: dimensions['18']}); + maki.push({key: 'maki-' + name + '-24', value: dimensions['24']}); + } + }); + + defs.call(SpriteDefinition( + 'sprite', + context.imagePath('sprite.svg'), + d3.entries(iD.data.operations))); + + defs.call(SpriteDefinition( + 'maki-sprite', + context.imagePath('maki-sprite.png'), + maki)); + }; +}; iD.svg.Labels = function(projection, context) { var path = d3.geo.path().projection(projection); @@ -24563,80 +25539,98 @@ iD.svg.Lines = function(projection) { }; function waystack(a, b) { - if (!a || !b || !a.tags || !b.tags) return 0; - if (a.tags.layer !== undefined && b.tags.layer !== undefined) { - return a.tags.layer - b.tags.layer; - } - if (a.tags.bridge) return 1; - if (b.tags.bridge) return -1; - if (a.tags.tunnel) return -1; - if (b.tags.tunnel) return 1; var as = 0, bs = 0; - if (a.tags.highway && b.tags.highway) { - as -= highway_stack[a.tags.highway]; - bs -= highway_stack[b.tags.highway]; - } + + if (a.tags.highway) { as -= highway_stack[a.tags.highway]; } + if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; } return as - bs; } return function drawLines(surface, graph, entities, filter) { - var lines = [], - path = iD.svg.Path(projection, graph); + var ways = [], pathdata = {}, onewaydata = {}, + getPath = iD.svg.Path(projection, graph); for (var i = 0; i < entities.length; i++) { var entity = entities[i], outer = iD.geo.simpleMultipolygonOuterMember(entity, graph); if (outer) { - lines.push(entity.mergeTags(outer.tags)); + ways.push(entity.mergeTags(outer.tags)); } else if (entity.geometry(graph) === 'line') { - lines.push(entity); + ways.push(entity); } } - lines = lines.filter(path); - lines.sort(waystack); + ways = ways.filter(getPath); - function drawPaths(klass) { - var paths = surface.select('.layer-' + klass) - .selectAll('path.line') - .filter(filter) - .data(lines, iD.Entity.key); + pathdata = _.groupBy(ways, function(way) { return way.layer(); }); - var enter = paths.enter() - .append('path') - .attr('class', function(d) { return 'way line ' + klass + ' ' + d.id; }); + _.forOwn(pathdata, function(v, k) { + onewaydata[k] = _(v) + .filter(function(d) { return d.isOneWay(); }) + .map(iD.svg.OneWaySegments(projection, graph, 35)) + .flatten() + .valueOf(); + }); - // Optimization: call simple TagClasses only on enter selection. This - // works because iD.Entity.key is defined to include the entity v attribute. - if (klass !== 'stroke') { - enter.call(iD.svg.TagClasses()); - } else { - paths.call(iD.svg.TagClasses() - .tags(iD.svg.MultipolygonMemberTags(graph))); - } + var layergroup = surface + .select('.layer-lines') + .selectAll('g.layergroup') + .data(d3.range(-10, 11)); - paths - .order() - .attr('d', path); + layergroup.enter() + .append('g') + .attr('class', function(d) { return 'layer layergroup layer' + String(d); }); - paths.exit() - .remove(); - } - drawPaths('shadow'); - drawPaths('casing'); - drawPaths('stroke'); + var linegroup = layergroup + .selectAll('g.linegroup') + .data(['shadow', 'casing', 'stroke']); + + linegroup.enter() + .append('g') + .attr('class', function(d) { return 'layer linegroup line-' + d; }); + + + var lines = linegroup + .selectAll('path') + .filter(filter) + .data( + function() { return pathdata[this.parentNode.parentNode.__data__] || []; }, + iD.Entity.key + ); + + // Optimization: call simple TagClasses only on enter selection. This + // works because iD.Entity.key is defined to include the entity v attribute. + lines.enter() + .append('path') + .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; }) + .call(iD.svg.TagClasses()); + + lines + .sort(waystack) + .attr('d', getPath) + .call(iD.svg.TagClasses().tags(iD.svg.MultipolygonMemberTags(graph))); + + lines.exit() + .remove(); - var segments = _(lines) - .filter(function(d) { return d.isOneWay(); }) - .map(iD.svg.OneWaySegments(projection, graph, 35)) - .flatten() - .valueOf(); - var oneways = surface.select('.layer-oneway') - .selectAll('path.oneway') + var onewaygroup = layergroup + .selectAll('g.onewaygroup') + .data(['oneway']); + + onewaygroup.enter() + .append('g') + .attr('class', 'layer onewaygroup'); + + + var oneways = onewaygroup + .selectAll('path') .filter(filter) - .data(segments, function(d) { return [d.id, d.index]; }); + .data( + function() { return onewaydata[this.parentNode.parentNode.__data__] || []; }, + function(d) { return [d.id, d.index]; } + ); oneways.enter() .append('path') @@ -24644,72 +25638,113 @@ iD.svg.Lines = function(projection) { .attr('marker-mid', 'url(#oneway-marker)'); oneways - .order() .attr('d', function(d) { return d.d; }); oneways.exit() .remove(); + }; }; iD.svg.Midpoints = function(projection, context) { return function drawMidpoints(surface, graph, entities, filter, extent) { - var midpoints = {}; + var poly = extent.polygon(), + midpoints = {}; for (var i = 0; i < entities.length; i++) { var entity = entities[i]; - if (entity.type !== 'way') continue; - if (context.selectedIDs().indexOf(entity.id) < 0) continue; + if (entity.type !== 'way') + continue; + if (!filter(entity)) + continue; + if (context.selectedIDs().indexOf(entity.id) < 0) + continue; var nodes = graph.childNodes(entity); - - // skip the last node because it is always repeated for (var j = 0; j < nodes.length - 1; j++) { var a = nodes[j], b = nodes[j + 1], id = [a.id, b.id].sort().join('-'); - // Redraw midpoints in two cases: - // 1. One of the two endpoint nodes changed (e.g. was moved). - // 2. A node was deleted. The midpoint between the two new - // endpoints needs to be redrawn. In this case only the - // way will be in the diff. - if (!midpoints[id] && (filter(a) || filter(b) || filter(entity))) { - var loc = iD.geo.interp(a.loc, b.loc, 0.5); - if (extent.intersects(loc) && iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) { - midpoints[id] = { - type: 'midpoint', - id: id, - loc: loc, - edge: [a.id, b.id] - }; + if (midpoints[id]) { + midpoints[id].parents.push(entity); + } else { + if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) { + var point = iD.geo.interp(a.loc, b.loc, 0.5), + loc = null; + + if (extent.intersects(point)) { + loc = point; + } else { + for (var k = 0; k < 4; k++) { + point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]); + if (point && + iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 && + iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20) + { + loc = point; + break; + } + } + } + + if (loc) { + midpoints[id] = { + type: 'midpoint', + id: id, + loc: loc, + edge: [a.id, b.id], + parents: [entity] + }; + } } } } } + function midpointFilter(d) { + if (midpoints[d.id]) + return true; + + for (var i = 0; i < d.parents.length; i++) + if (filter(d.parents[i])) + return true; + + return false; + } + var groups = surface.select('.layer-hit').selectAll('g.midpoint') - .filter(filter) + .filter(midpointFilter) .data(_.values(midpoints), function(d) { return d.id; }); - var group = groups.enter() + var enter = groups.enter() .insert('g', ':first-child') .attr('class', 'midpoint'); - group.append('circle') - .attr('r', 7) + enter.append('polygon') + .attr('points', '-6,8 10,0 -6,-8') .attr('class', 'shadow'); - group.append('circle') - .attr('r', 3) + enter.append('polygon') + .attr('points', '-3,4 5,0 -3,-4') .attr('class', 'fill'); - groups.attr('transform', iD.svg.PointTransform(projection)); + groups + .attr('transform', function(d) { + var translate = iD.svg.PointTransform(projection), + a = context.entity(d.edge[0]), + b = context.entity(d.edge[1]), + angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI)); + return translate(d) + ' rotate(' + angle + ')'; + }) + .call(iD.svg.TagClasses().tags( + function(d) { return d.parents[0].tags; } + )); // Propagate data bindings. - groups.select('circle.shadow'); - groups.select('circle.fill'); + groups.select('polygon.shadow'); + groups.select('polygon.fill'); groups.exit() .remove(); @@ -24784,194 +25819,15 @@ iD.svg.Points = function(projection, context) { return drawPoints; }; -iD.svg.Restrictions = function(context) { - var projection = context.projection; - - function drawRestrictions(surface) { - var turns = drawRestrictions.turns(context.graph(), context.selectedIDs()); - - var groups = surface.select('.layer-hit').selectAll('g.restriction') - .data(turns, iD.Entity.key); - - var enter = groups.enter().append('g') - .attr('class', 'restriction'); - - enter.append('circle') - .attr('class', 'restriction') - .attr('r', 4); - - groups - .attr('transform', function(restriction) { - var via = context.entity(restriction.memberByRole('via').id); - return iD.svg.PointTransform(projection)(via); - }); - - groups.exit() - .remove(); - - return this; - } - - drawRestrictions.turns = function (graph, selectedIDs) { - if (selectedIDs.length !== 1) - return []; - - var from = graph.entity(selectedIDs[0]); - if (from.type !== 'way') - return []; - - return graph.parentRelations(from).filter(function(relation) { - var f = relation.memberById(from.id), - t = relation.memberByRole('to'), - v = relation.memberByRole('via'); - - return relation.tags.type === 'restriction' && f.role === 'from' && - t && t.type === 'way' && graph.hasEntity(t.id) && - v && v.type === 'node' && graph.hasEntity(v.id) && - !graph.entity(t.id).isDegenerate() && - !graph.entity(f.id).isDegenerate() && - graph.entity(t.id).affix(v.id) && - graph.entity(f.id).affix(v.id); - }); - }; - - drawRestrictions.datum = function(graph, from, restriction, projection) { - var to = graph.entity(restriction.memberByRole('to').id), - a = graph.entity(restriction.memberByRole('via').id), - b; - - if (to.first() === a.id) { - b = graph.entity(to.nodes[1]); - } else { - b = graph.entity(to.nodes[to.nodes.length - 2]); - } - - a = projection(a.loc); - b = projection(b.loc); - - return { - from: from, - to: to, - restriction: restriction, - angle: Math.atan2(b[1] - a[1], b[0] - a[0]) - }; - }; - - return drawRestrictions; -}; -iD.svg.Surface = function(context) { - function autosize(image) { - var img = document.createElement('img'); - img.src = image.attr('xlink:href'); - img.onload = function() { - image.attr({ - width: img.width, - height: img.height - }); - }; - } - - function SpriteDefinition(id, href, data) { - return function(defs) { - defs.append('image') - .attr('id', id) - .attr('xlink:href', href) - .call(autosize); - - defs.selectAll() - .data(data) - .enter().append('use') - .attr('id', function(d) { return d.key; }) - .attr('transform', function(d) { return 'translate(-' + d.value[0] + ',-' + d.value[1] + ')'; }) - .attr('xlink:href', '#' + id); - }; - } - - return function drawSurface(selection) { - var defs = selection.append('defs'); - - defs.append('marker') - .attr({ - id: 'oneway-marker', - viewBox: '0 0 10 10', - refY: 2.5, - refX: 5, - markerWidth: 2, - markerHeight: 2, - orient: 'auto' - }) - .append('path') - .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'); - - var patterns = defs.selectAll('pattern') - .data([ - // pattern name, pattern image name - ['wetland', 'wetland'], - ['construction', 'construction'], - ['cemetery', 'cemetery'], - ['orchard', 'orchard'], - ['farmland', 'farmland'], - ['beach', 'dots'], - ['scrub', 'dots'], - ['meadow', 'dots']]) +iD.svg.Surface = function() { + return function (selection) { + selection.selectAll('defs') + .data([0]) .enter() - .append('pattern') - .attr({ - id: function(d) { return 'pattern-' + d[0]; }, - width: 32, - height: 32, - patternUnits: 'userSpaceOnUse' - }); - - patterns.append('rect') - .attr({ - x: 0, - y: 0, - width: 32, - height: 32, - 'class': function(d) { return 'pattern-color-' + d[0]; } - }); - - patterns.append('image') - .attr({ - x: 0, - y: 0, - width: 32, - height: 32 - }) - .attr('xlink:href', function(d) { return context.imagePath('pattern/' + d[1] + '.png'); }); - - defs.selectAll() - .data([12, 18, 20]) - .enter().append('clipPath') - .attr('id', function(d) { return 'clip-square-' + d; }) - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', function(d) { return d; }) - .attr('height', function(d) { return d; }); - - var maki = []; - _.forEach(iD.data.featureIcons, function(dimensions, name) { - if (dimensions['12'] && dimensions['18'] && dimensions['24']) { - maki.push({key: 'maki-' + name + '-12', value: dimensions['12']}); - maki.push({key: 'maki-' + name + '-18', value: dimensions['18']}); - maki.push({key: 'maki-' + name + '-24', value: dimensions['24']}); - } - }); - - defs.call(SpriteDefinition( - 'sprite', - context.imagePath('sprite.svg'), - d3.entries(iD.data.operations))); - - defs.call(SpriteDefinition( - 'maki-sprite', - context.imagePath('maki-sprite.png'), - maki)); + .append('defs'); var layers = selection.selectAll('.layer') - .data(['fill', 'shadow', 'casing', 'stroke', 'oneway', 'hit', 'halo', 'label']); + .data(['areas', 'lines', 'hit', 'halo', 'label']); layers.enter().append('g') .attr('class', function(d) { return 'layer layer-' + d; }); @@ -24979,12 +25835,12 @@ iD.svg.Surface = function(context) { }; iD.svg.TagClasses = function() { var primary = [ - 'highway', 'railway', 'waterway', 'aeroway', 'motorway', - 'boundary', 'power', 'amenity', 'natural', 'landuse', - 'building', 'leisure', 'place' + 'building', 'highway', 'railway', 'waterway', 'aeroway', + 'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse', + 'leisure', 'place' ], secondary = [ - 'oneway', 'bridge', 'tunnel', 'construction' + 'oneway', 'bridge', 'tunnel', 'construction', 'embankment', 'cutting' ], tagClassRe = /^tag-/, tags = function(entity) { return entity.tags; }; @@ -25032,6 +25888,77 @@ iD.svg.TagClasses = function() { return tagClasses; }; +iD.svg.Turns = function(projection) { + return function(surface, graph, turns) { + function key(turn) { + return [turn.from.node + turn.via.node + turn.to.node].join('-'); + } + + function icon(turn) { + var u = turn.u ? '-u' : ''; + if (!turn.restriction) + return '#icon-restriction-yes' + u; + var restriction = graph.entity(turn.restriction).tags.restriction; + return '#icon-restriction-' + + (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u; + } + + var groups = surface.select('.layer-hit').selectAll('g.turn') + .data(turns, key); + + // Enter + + var enter = groups.enter().append('g') + .attr('class', 'turn'); + + var nEnter = enter.filter(function (turn) { return !turn.u; }); + + nEnter.append('rect') + .attr('transform', 'translate(-12, -12)') + .attr('width', '45') + .attr('height', '25'); + + nEnter.append('use') + .attr('transform', 'translate(-12, -12)') + .attr('clip-path', 'url(#clip-square-45)'); + + var uEnter = enter.filter(function (turn) { return turn.u; }); + + uEnter.append('circle') + .attr('r', '16'); + + uEnter.append('use') + .attr('transform', 'translate(-16, -16)') + .attr('clip-path', 'url(#clip-square-32)'); + + // Update + + groups + .attr('transform', function (turn) { + var v = graph.entity(turn.via.node), + t = graph.entity(turn.to.node), + a = iD.geo.angle(v, t, projection), + p = projection(v.loc), + r = turn.u ? 0 : 60; + + return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ')' + + 'rotate(' + a * 180 / Math.PI + ')'; + }); + + groups.select('use') + .attr('xlink:href', icon); + + groups.select('rect'); + groups.select('circle'); + + // Exit + + groups.exit() + .remove(); + + return this; + }; +}; iD.svg.Vertices = function(projection, context) { var radiuses = { // z16-, z17, z18+, tagged @@ -25078,49 +26005,74 @@ iD.svg.Vertices = function(projection, context) { return vertices; } - function draw(groups, vertices, klass, graph, zoom) { - groups = groups.data(vertices, function(entity) { - return iD.Entity.key(entity) + ',' + zoom; - }); + function draw(selection, vertices, klass, graph, zoom) { + var icons = {}, + z; if (zoom < 17) { - zoom = 0; + z = 0; } else if (zoom < 18) { - zoom = 1; + z = 1; } else { - zoom = 2; + z = 2; } - var icons = {}; + var groups = selection.data(vertices, function(entity) { + return iD.Entity.key(entity); + }); + function icon(entity) { if (entity.id in icons) return icons[entity.id]; - icons[entity.id] = zoom !== 0 && + icons[entity.id] = entity.hasInterestingTags() && context.presets().match(entity, graph).icon; return icons[entity.id]; } - function circle(klass) { - var rads = radiuses[klass]; + function classCircle(klass) { return function(entity) { - var i = icon(entity), - c = i ? 0.5 : 0, - r = rads[i ? 3 : zoom]; this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id); - this.setAttribute('cx', c); - this.setAttribute('cy', -c); - this.setAttribute('r', r); }; } - var enter = groups.enter().append('g') + function setAttributes(selection) { + ['shadow','stroke','fill'].forEach(function(klass) { + var rads = radiuses[klass]; + selection.selectAll('.' + klass) + .each(function(entity) { + var i = z && icon(entity), + c = i ? 0.5 : 0, + r = rads[i ? 3 : z]; + this.setAttribute('cx', c); + this.setAttribute('cy', -c); + this.setAttribute('r', r); + if (i && klass === 'fill') { + this.setAttribute('visibility', 'hidden'); + } else { + this.removeAttribute('visibility'); + } + }); + }); + + selection.selectAll('use') + .each(function() { + if (z) { + this.removeAttribute('visibility'); + } else { + this.setAttribute('visibility', 'hidden'); + } + }); + } + + var enter = groups.enter() + .append('g') .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; }); enter.append('circle') - .each(circle('shadow')); + .each(classCircle('shadow')); enter.append('circle') - .each(circle('stroke')); + .each(classCircle('stroke')); // Vertices with icons get a `use`. enter.filter(function(d) { return icon(d); }) @@ -25129,14 +26081,15 @@ iD.svg.Vertices = function(projection, context) { .attr('clip-path', 'url(#clip-square-12)') .attr('xlink:href', function(d) { return '#maki-' + icon(d) + '-12'; }); - // Vertices with tags get a `circle`. - enter.filter(function(d) { return !icon(d) && d.hasInterestingTags(); }) + // Vertices with tags get a fill. + enter.filter(function(d) { return d.hasInterestingTags(); }) .append('circle') - .each(circle('fill')); + .each(classCircle('fill')); groups .attr('transform', iD.svg.PointTransform(projection)) - .classed('shared', function(entity) { return graph.isShared(entity); }); + .classed('shared', function(entity) { return graph.isShared(entity); }) + .call(setAttributes); groups.exit() .remove(); @@ -25196,6 +26149,10 @@ iD.ui = function(context) { map.centerZoom([-77.02271, 38.90085], 20); } + container.append('svg') + .attr('id', 'defs') + .call(iD.svg.Defs(context)); + container.append('div') .attr('id', 'sidebar') .attr('class', 'col4') @@ -25260,23 +26217,24 @@ iD.ui = function(context) { .attr('class', 'map-control help-control') .call(iD.ui.Help(context)); - var about = content.append('div') - .attr('class','col12 about-block fillD'); + var footer = content.append('div') + .attr('id', 'footer') + .attr('class', 'fillD'); + + footer.append('div') + .attr('id', 'scale-block') + .call(iD.ui.Scale(context)); - about.append('div') - .attr('class', 'api-status') - .call(iD.ui.Status(context)); + var linkList = footer.append('div') + .attr('id', 'info-block') + .append('ul') + .attr('id', 'about-list') + .attr('class', 'link-list'); if (!context.embed()) { - about.append('div') - .attr('class', 'account') - .call(iD.ui.Account(context)); + linkList.call(iD.ui.Account(context)); } - var linkList = about.append('ul') - .attr('id', 'about') - .attr('class', 'link-list'); - linkList.append('li') .append('a') .attr('target', '_blank') @@ -25303,6 +26261,10 @@ iD.ui = function(context) { .attr('tabindex', -1) .call(iD.ui.Contributors(context)); + footer.append('div') + .attr('class', 'api-status') + .call(iD.ui.Status(context)); + window.onbeforeunload = function() { return context.save(); }; @@ -25373,20 +26335,25 @@ iD.ui.Account = function(context) { function update(selection) { if (!connection.authenticated()) { - selection.html('') + selection.selectAll('#userLink, #logoutLink') .style('display', 'none'); return; } - selection.style('display', 'block'); - connection.userDetails(function(err, details) { - selection.html(''); + var userLink = selection.select('#userLink'), + logoutLink = selection.select('#logoutLink'); + + userLink.html(''); + logoutLink.html(''); if (err) return; + selection.selectAll('#userLink, #logoutLink') + .style('display', 'list-item'); + // Link - var userLink = selection.append('a') + userLink.append('a') .attr('href', connection.userURL(details.display_name)) .attr('target', '_blank'); @@ -25405,7 +26372,7 @@ iD.ui.Account = function(context) { .attr('class', 'label') .text(details.display_name); - selection.append('a') + logoutLink.append('a') .attr('class', 'logout') .attr('href', '#') .text(t('logout')) @@ -25417,7 +26384,15 @@ iD.ui.Account = function(context) { } return function(selection) { - connection.on('auth', function() { update(selection); }); + selection.append('li') + .attr('id', 'logoutLink') + .style('display', 'none'); + + selection.append('li') + .attr('id', 'userLink') + .style('display', 'none'); + + connection.on('auth.account', function() { update(selection); }); update(selection); }; }; @@ -25511,7 +26486,7 @@ iD.ui.Background = function(context) { ['bottom', [0, 1]]], opacityDefault = (context.storage('background-opacity') !== null) ? (+context.storage('background-opacity')) : 0.5, - customTemplate; + customTemplate = ''; // Can be 0 from <1.3.0 use or due to issue #1923. if (opacityDefault === 0) opacityDefault = 0.5; @@ -26258,6 +27233,8 @@ iD.ui.EntityEditor = function(context) { preset, reference; + var presetEditor = iD.ui.preset(context) + .on('change', changeTags); var rawTagEditor = iD.ui.RawTagEditor(context) .on('change', changeTags); @@ -26344,12 +27321,11 @@ iD.ui.EntityEditor = function(context) { .text(preset.name()); $body.select('.inspector-preset') - .call(iD.ui.preset(context) + .call(presetEditor .preset(preset) .entityID(id) .tags(tags) - .state(state) - .on('change', changeTags)); + .state(state)); $body.select('.raw-tag-editor') .call(rawTagEditor @@ -26386,11 +27362,13 @@ iD.ui.EntityEditor = function(context) { function clean(o) { var out = {}, k, v; + /*jshint -W083 */ for (k in o) { if (k && (v = o[k]) !== undefined) { - out[k] = v.trim(); + out[k] = v.split(';').map(function(s) { return s.trim(); }).join(';'); } } + /*jshint +W083 */ return out; } @@ -26499,15 +27477,16 @@ iD.ui.FeatureList = function(context) { }); } - var locationMatch = q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/); + var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/); if (locationMatch) { + var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])]; result.push({ id: -1, geometry: 'point', type: t('inspector.location'), - name: locationMatch[0], - location: [parseFloat(locationMatch[1]), parseFloat(locationMatch[2])] + name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6), + location: loc }); } @@ -26908,7 +27887,12 @@ iD.ui.Inspector = function(context) { var $presetPane = $wrap.select('.preset-list-pane'); var $editorPane = $wrap.select('.entity-editor-pane'); - var showEditor = state === 'hover' || context.entity(entityID).isUsed(context.graph()); + var graph = context.graph(), + entity = context.entity(entityID), + showEditor = state === 'hover' || + entity.isUsed(graph) || + entity.isHighwayIntersection(graph); + if (showEditor) { $wrap.style('right', '0%'); $editorPane.call(entityEditor); @@ -27436,6 +28420,10 @@ iD.ui.preset = function(context) { } }); + if (entity.isHighwayIntersection(context.graph())) { + fields.push(UIField(context.presets().field('restrictions'), entity, true)); + } + context.presets().universal().forEach(function(field) { if (preset.fields.indexOf(field) < 0) { fields.push(UIField(field, entity)); @@ -27498,7 +28486,7 @@ iD.ui.preset = function(context) { return field.present(); }) .each(function(field) { - var reference = iD.ui.TagReference({key: field.key}); + var reference = iD.ui.TagReference(field.reference || {key: field.key}); if (state === 'hover') { reference.showing(false); @@ -27560,6 +28548,7 @@ iD.ui.preset = function(context) { presets.preset = function(_) { if (!arguments.length) return preset; + if (preset && preset.id === _.id) return presets; preset = _; fields = null; return presets; @@ -27580,6 +28569,7 @@ iD.ui.preset = function(context) { presets.entityID = function(_) { if (!arguments.length) return id; + if (id === _) return presets; id = _; fields = null; return presets; @@ -27605,7 +28595,7 @@ iD.ui.PresetIcon = function() { $fill.enter().append('div'); $fill.attr('class', function() { - var s = 'preset-icon-fill icon-' + geom; + var s = 'preset-icon-fill preset-icon-fill-' + geom; for (var i in p.tags) { s += ' tag-' + i + ' tag-' + i + '-' + p.tags[i]; } @@ -28037,6 +29027,10 @@ iD.ui.RawMemberEditor = function(context) { context.perform( iD.actions.DeleteMember(d.relation.id, d.index), t('operations.delete_member.annotation')); + + if (!context.hasEntity(d.relation.id)) { + context.enter(iD.modes.Browse(context)); + } } function rawMemberEditor(selection) { @@ -28503,10 +29497,22 @@ iD.ui.RawTagEditor = function(context) { } function keyChange(d) { - var tag = {}; - tag[d.key] = undefined; - tag[this.value] = d.value; - d.key = this.value; // Maintain DOM identity through the subsequent update. + var kOld = d.key, + kNew = this.value.trim(), + tag = {}; + + if (kNew && kNew !== kOld) { + var match = kNew.match(/^(.*?)(?:_(\d+))?$/), + base = match[1], + suffix = +(match[2] || 1); + while (tags[kNew]) { // rename key if already in use + kNew = base + '_' + suffix++; + } + } + tag[kOld] = undefined; + tag[kNew] = d.value; + d.key = kNew; // Maintain DOM identity through the subsequent update. + this.value = kNew; event.change(tag); } @@ -28672,6 +29678,88 @@ iD.ui.Save = function(context) { }); }; }; +iD.ui.Scale = function(context) { + var projection = context.projection, + imperial = (iD.detect().locale.toLowerCase() === 'en-us'), + maxLength = 180, + tickHeight = 8; + + function scaleDefs(loc1, loc2) { + var lat = (loc2[1] + loc1[1]) / 2, + conversion = (imperial ? 3.28084 : 1), + dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion, + scale = { dist: 0, px: 0, text: '' }, + buckets, i, val, dLon; + + if (imperial) { + buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1]; + } else { + buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1]; + } + + // determine a user-friendly endpoint for the scale + for (i = 0; i < buckets.length; i++) { + val = buckets[i]; + if (dist >= val) { + scale.dist = Math.floor(dist / val) * val; + break; + } + } + + dLon = iD.geo.metersToLon(scale.dist / conversion, lat); + scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]); + + if (imperial) { + if (scale.dist >= 5280) { + scale.dist /= 5280; + scale.text = String(scale.dist) + ' mi'; + } else { + scale.text = String(scale.dist) + ' ft'; + } + } else { + if (scale.dist >= 1000) { + scale.dist /= 1000; + scale.text = String(scale.dist) + ' km'; + } else { + scale.text = String(scale.dist) + ' m'; + } + } + + return scale; + } + + function update(selection) { + // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn) + var dims = context.map().dimensions(), + loc1 = projection.invert([0, dims[1]]), + loc2 = projection.invert([maxLength, dims[1]]), + scale = scaleDefs(loc1, loc2); + + selection.select('#scalepath') + .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight); + + selection.select('#scaletext') + .attr('x', scale.px + 8) + .attr('y', tickHeight) + .text(scale.text); + } + + return function(selection) { + var g = selection.append('svg') + .attr('id', 'scale') + .append('g') + .attr('transform', 'translate(10,11)'); + + g.append('path').attr('id', 'scalepath'); + g.append('text').attr('id', 'scaletext'); + + update(selection); + + context.map().on('move.scale', function() { + update(selection); + }); + }; +}; iD.ui.SelectionList = function(context, selectedIDs) { function selectionList(selection) { @@ -28809,6 +29897,7 @@ iD.ui.Sidebar = function(context) { sidebar.hide = function() { featureListWrap.classed('inspector-hidden', false); + inspectorWrap.classed('inspector-hidden', true); if (current) current.remove(); current = null; }; @@ -29369,10 +30458,11 @@ iD.ui.preset.access = function(field) { } access.options = function(type) { - var options = ['no', 'permissive', 'private', 'designated', 'destination']; + var options = ['no', 'permissive', 'private', 'destination']; if (type !== 'access') { options.unshift('yes'); + options.push('designated'); } return options.map(function(option) { @@ -29385,65 +30475,104 @@ iD.ui.preset.access = function(field) { var placeholders = { footway: { - foot: 'yes', + foot: 'designated', motor_vehicle: 'no' }, steps: { foot: 'yes', - motor_vehicle: 'no' + motor_vehicle: 'no', + bicycle: 'no', + horse: 'no' }, pedestrian: { foot: 'yes', motor_vehicle: 'no' }, cycleway: { - bicycle: 'yes', - motor_vehicle: 'no' + motor_vehicle: 'no', + bicycle: 'designated' }, bridleway: { - horse: 'yes' + motor_vehicle: 'no', + horse: 'designated' }, path: { - motor_vehicle: 'no' + foot: 'yes', + motor_vehicle: 'no', + bicycle: 'yes', + horse: 'yes' }, motorway: { - motor_vehicle: 'yes' + foot: 'no', + motor_vehicle: 'yes', + bicycle: 'no', + horse: 'no' }, trunk: { motor_vehicle: 'yes' }, primary: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, secondary: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, tertiary: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, residential: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, unclassified: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, service: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, motorway_link: { - motor_vehicle: 'yes' + foot: 'no', + motor_vehicle: 'yes', + bicycle: 'no', + horse: 'no' }, trunk_link: { motor_vehicle: 'yes' }, primary_link: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, secondary_link: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' }, tertiary_link: { - motor_vehicle: 'yes' + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' } }; @@ -29459,7 +30588,9 @@ iD.ui.preset.access = function(field) { _.forEach(placeholders[tags.highway], function(value, key) { items.selectAll('#preset-input-access-' + key) - .attr('placeholder', value); + .attr('placeholder', function() { + return (tags.access && (value === 'yes' || value === 'designated')) ? tags.access : value; + }); }); }; @@ -29471,12 +30602,17 @@ iD.ui.preset.access = function(field) { return d3.rebind(access, event, 'on'); }; iD.ui.preset.address = function(field, context) { - var event = d3.dispatch('change'), - housenumber, - street, - city, - postcode, - entity; + var event = d3.dispatch('init', 'change'), + wrap, + entity, + isInitialized; + + var widths = { + housenumber: 1/3, + street: 2/3, + city: 2/3, + postcode: 1/3 + }; function getStreets() { var extent = entity.extent(context.graph()), @@ -29561,71 +30697,95 @@ iD.ui.preset.address = function(field, context) { } function address(selection) { - var wrap = selection.selectAll('.preset-input-wrap') - .data([0]); + selection.selectAll('.preset-input-wrap') + .remove(); + + var center = entity.extent(context.graph()).center(), + addressFormat; // Enter - var enter = wrap.enter().append('div') + wrap = selection.append('div') .attr('class', 'preset-input-wrap'); - enter.append('input') - .property('type', 'text') - .attr('placeholder', field.t('placeholders.number')) - .attr('class', 'addr-number'); + iD.countryCode().search(center, function (err, countryCode) { + addressFormat = _.find(iD.data.addressFormats, function (a) { + return a && a.countryCodes && _.contains(a.countryCodes, countryCode); + }) || _.first(iD.data.addressFormats); - enter.append('input') - .property('type', 'text') - .attr('placeholder', field.t('placeholders.street')) - .attr('class', 'addr-street'); + function row(r) { + // Normalize widths. + var total = _.reduce(r, function(sum, field) { + return sum + (widths[field] || 0.5); + }, 0); - enter.append('input') - .property('type', 'text') - .attr('placeholder', field.t('placeholders.city')) - .attr('class', 'addr-city'); + return r.map(function (field) { + return { + id: field, + width: (widths[field] || 0.5) / total + }; + }); + } - enter.append('input') - .property('type', 'text') - .attr('placeholder', field.t('placeholders.postcode')) - .attr('class', 'addr-postcode'); + wrap.selectAll('div') + .data(addressFormat.format) + .enter() + .append('div') + .attr('class', 'addr-row') + .selectAll('input') + .data(row) + .enter() + .append('input') + .property('type', 'text') + .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); }) + .attr('class', function (d) { return 'addr-' + d.id; }) + .style('width', function (d) { return d.width * 100 + '%'; }); - // Update + // Update - housenumber = wrap.select('.addr-number'); - street = wrap.select('.addr-street'); - city = wrap.select('.addr-city'); - postcode = wrap.select('.addr-postcode'); + wrap.selectAll('.addr-street') + .call(d3.combobox() + .fetcher(function(value, callback) { + callback(getStreets()); + })); - wrap.selectAll('input') - .on('blur', change) - .on('change', change); + wrap.selectAll('.addr-city') + .call(d3.combobox() + .fetcher(function(value, callback) { + callback(getCities()); + })); - street - .call(d3.combobox() - .fetcher(function(value, callback) { - callback(getStreets()); - })); + wrap.selectAll('.addr-postcode') + .call(d3.combobox() + .fetcher(function(value, callback) { + callback(getPostCodes()); + })); - city - .call(d3.combobox() - .fetcher(function(value, callback) { - callback(getCities()); - })); + wrap.selectAll('input') + .on('blur', change) + .on('change', change); - postcode - .call(d3.combobox() - .fetcher(function(value, callback) { - callback(getPostCodes()); - })); + event.init(); + isInitialized = true; + }); } function change() { - event.change({ - 'addr:housenumber': housenumber.value() || undefined, - 'addr:street': street.value() || undefined, - 'addr:city': city.value() || undefined, - 'addr:postcode': postcode.value() || undefined - }); + var tags = {}; + + wrap.selectAll('input') + .each(function (field) { + tags['addr:' + field.id] = this.value || undefined; + }); + + event.change(tags); + } + + function updateTags(tags) { + wrap.selectAll('input') + .value(function (field) { + return tags['addr:' + field.id] || ''; + }); } address.entity = function(_) { @@ -29635,27 +30795,55 @@ iD.ui.preset.address = function(field, context) { }; address.tags = function(tags) { - housenumber.value(tags['addr:housenumber'] || ''); - street.value(tags['addr:street'] || ''); - city.value(tags['addr:city'] || ''); - postcode.value(tags['addr:postcode'] || ''); + if (isInitialized) { + updateTags(tags); + } else { + event.on('init', function () { + updateTags(tags); + }); + } }; address.focus = function() { - housenumber.node().focus(); + wrap.selectAll('input').node().focus(); }; return d3.rebind(address, event, 'on'); }; -iD.ui.preset.check = function(field) { +iD.ui.preset.check = +iD.ui.preset.defaultcheck = function(field) { var event = d3.dispatch('change'), - values = [undefined, 'yes', 'no'], - value, - box, - text, - label; + options = field.strings && field.strings.options, + values = [], + texts = [], + entity, value, box, text, label; + + if (options) { + for (var k in options) { + values.push(k === 'undefined' ? undefined : k); + texts.push(field.t('options.' + k, { 'default': options[k] })); + } + } else { + values = [undefined, 'yes']; + texts = [t('inspector.unknown'), t('inspector.check.yes')]; + if (field.type === 'check') { + values.push('no'); + texts.push(t('inspector.check.no')); + } + } var check = function(selection) { + // hack: pretend oneway field is a oneway_yes field + // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841 + if (field.id === 'oneway') { + for (var key in entity.tags) { + if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) { + texts[0] = t('presets.fields.oneway_yes.options.undefined'); + break; + } + } + } + selection.classed('checkselect', 'true'); label = selection.selectAll('.preset-input-wrap') @@ -29665,18 +30853,18 @@ iD.ui.preset.check = function(field) { .attr('class', 'preset-input-wrap'); enter.append('input') - .property('indeterminate', true) + .property('indeterminate', field.type === 'check') .attr('type', 'checkbox') .attr('id', 'preset-input-' + field.id); enter.append('span') - .text(t('inspector.unknown')) + .text(texts[0]) .attr('class', 'value'); box = label.select('input') .on('click', function() { var t = {}; - t[field.key] = values[(values.indexOf(value) + 1) % 3]; + t[field.key] = values[(values.indexOf(value) + 1) % values.length]; event.change(t); d3.event.stopPropagation(); }); @@ -29684,11 +30872,17 @@ iD.ui.preset.check = function(field) { text = label.select('span.value'); }; + check.entity = function(_) { + if (!arguments.length) return entity; + entity = _; + return check; + }; + check.tags = function(tags) { value = tags[field.key]; - box.property('indeterminate', !value); + box.property('indeterminate', field.type === 'check' && !value); box.property('checked', value === 'yes'); - text.text(value ? t('inspector.check.' + value, {default: value}) : t('inspector.unknown')); + text.text(texts[values.indexOf(value)]); label.classed('set', !!value); }; @@ -29701,6 +30895,9 @@ iD.ui.preset.check = function(field) { iD.ui.preset.combo = iD.ui.preset.typeCombo = function(field) { var event = d3.dispatch('change'), + optstrings = field.strings && field.strings.options, + optarray = field.options, + strings = {}, input; function combo(selection) { @@ -29709,42 +30906,67 @@ iD.ui.preset.typeCombo = function(field) { input = selection.selectAll('input') .data([0]); - input.enter().append('input') + var enter = input.enter() + .append('input') .attr('type', 'text') .attr('id', 'preset-input-' + field.id); + if (optstrings) { enter.attr('readonly', 'readonly'); } + input + .call(combobox) .on('change', change) .on('blur', change) .each(function() { - if (field.options) { - options(field.options); + if (optstrings) { + _.each(optstrings, function(v, k) { + strings[k] = field.t('options.' + k, { 'default': v }); + }); + stringsLoaded(); + } else if (optarray) { + _.each(optarray, function(k) { + strings[k] = k.replace(/_+/g, ' '); + }); + stringsLoaded(); } else { - iD.taginfo().values({ - key: field.key - }, function(err, data) { - if (!err) options(_.pluck(data, 'value')); + iD.taginfo().values({key: field.key}, function(err, data) { + if (!err) { + _.each(_.pluck(data, 'value'), function(k) { + strings[k] = k.replace(/_+/g, ' '); + }); + stringsLoaded(); + } }); } - }) - .call(combobox); + }); + + function stringsLoaded() { + var keys = _.keys(strings), + strs = [], + placeholders; - function options(opts) { - combobox.data(opts.map(function(d) { - var o = {}; - o.title = o.value = d.replace('_', ' '); + combobox.data(keys.map(function(k) { + var s = strings[k], + o = {}; + o.title = o.value = s; + if (s.length < 20) { strs.push(s); } return o; })); - input.attr('placeholder', function() { - if (opts.length < 3) return ''; - return opts.slice(0, 3).join(', ') + '...'; - }); + placeholders = strs.length > 1 ? strs : keys; + input.attr('placeholder', field.placeholder() || + (placeholders.slice(0, 3).join(', ') + '...')); } } function change() { - var value = input.value().replace(' ', '_'); + var optstring = _.find(_.keys(strings), function(k) { return strings[k] === input.value(); }), + value = optstring || (input.value() + .split(';') + .map(function(s) { return s.trim(); }) + .join(';') + .replace(/\s+/g, '_')); + if (field.type === 'typeCombo' && !value) value = 'yes'; var t = {}; @@ -29753,8 +30975,9 @@ iD.ui.preset.typeCombo = function(field) { } combo.tags = function(tags) { - var value = tags[field.key] || ''; - if (field.type === 'typeCombo' && value === 'yes') value = ''; + var key = tags[field.key], + value = strings[key] || key || ''; + if (field.type === 'typeCombo' && value.toLowerCase() === 'yes') value = ''; input.value(value); }; @@ -29764,36 +30987,6 @@ iD.ui.preset.typeCombo = function(field) { return d3.rebind(combo, event, 'on'); }; -iD.ui.preset.defaultcheck = function(field) { - var event = d3.dispatch('change'), - input; - - function check(selection) { - input = selection.selectAll('input') - .data([0]); - - input.enter().append('input') - .attr('type', 'checkbox') - .attr('id', 'preset-input-' + field.id); - - input - .on('change', function() { - var t = {}; - t[field.key] = input.property('checked') ? field.value || 'yes' : undefined; - event.change(t); - }); - } - - check.tags = function(tags) { - input.property('checked', !!tags[field.key] && tags[field.key] !== 'no'); - }; - - check.focus = function() { - input.node().focus(); - }; - - return d3.rebind(check, event, 'on'); -}; iD.ui.preset.text = iD.ui.preset.number = iD.ui.preset.tel = @@ -29876,10 +31069,6 @@ iD.ui.preset.localized = function(field, context) { .attr('class', 'localized-main') .attr('placeholder', field.placeholder()); - input - .on('blur', change) - .on('change', change); - if (field.id === 'name') { var preset = context.presets().match(entity, context.graph()); input.call(d3.combobox().fetcher( @@ -29887,6 +31076,10 @@ iD.ui.preset.localized = function(field, context) { )); } + input + .on('blur', change) + .on('change', change); + var translateButton = selection.selectAll('.localized-add') .data([0]); @@ -30116,9 +31309,9 @@ iD.ui.preset.maxspeed = function(field, context) { .attr('placeholder', field.placeholder()); input + .call(combobox) .on('change', change) - .on('blur', change) - .call(combobox); + .on('blur', change); var childNodes = context.graph().childNodes(context.entity(entity.id)), loc = childNodes[~~(childNodes.length/2)].loc; @@ -30278,6 +31471,145 @@ iD.ui.preset.radio = function(field) { return d3.rebind(radio, event, 'on'); }; +iD.ui.preset.restrictions = function(field, context) { + var event = d3.dispatch('change'), + vertexID, + fromNodeID; + + function restrictions(selection) { + var wrap = selection.selectAll('.preset-input-wrap') + .data([0]); + + var enter = wrap.enter().append('div') + .attr('class', 'preset-input-wrap'); + + enter.append('div') + .attr('class', 'restriction-help'); + + enter.append('svg') + .call(iD.svg.Surface(context)) + .call(iD.behavior.Hover(context)); + + var intersection = iD.geo.Intersection(context.graph(), vertexID), + graph = intersection.graph, + vertex = graph.entity(vertexID), + surface = wrap.selectAll('svg'), + filter = function () { return true; }, + extent = iD.geo.Extent(), + projection = iD.geo.RawMercator(), + lines = iD.svg.Lines(projection, context), + vertices = iD.svg.Vertices(projection, context), + turns = iD.svg.Turns(projection, context); + + var d = wrap.dimensions(), + c = [d[0] / 2, d[1] / 2], + z = 21; + + projection + .scale(256 * Math.pow(2, z) / (2 * Math.PI)); + + var s = projection(vertex.loc); + + projection + .translate([c[0] - s[0], c[1] - s[1]]) + .clipExtent([[0, 0], d]); + + surface + .call(vertices, graph, [vertex], filter, extent, z) + .call(lines, graph, intersection.highways, filter) + .call(turns, graph, intersection.turns(fromNodeID)); + + surface + .on('click.restrictions', click) + .on('mouseover.restrictions', mouseover) + .on('mouseout.restrictions', mouseout); + + surface + .selectAll('.selected') + .classed('selected', false); + + if (fromNodeID) { + surface + .selectAll('.' + _.find(intersection.highways, function(way) { return way.contains(fromNodeID); }).id) + .classed('selected', true); + } + + mouseout(); + + context.history() + .on('change.restrictions', render); + + d3.select(window) + .on('resize.restrictions', render); + + function click() { + var datum = d3.event.target.__data__; + if (datum instanceof iD.Entity) { + fromNodeID = datum.nodes[(datum.first() === vertexID) ? 1 : datum.nodes.length - 2]; + render(); + } else if (datum instanceof iD.geo.Turn) { + if (datum.restriction) { + context.perform( + iD.actions.UnrestrictTurn(datum, projection), + t('operations.restriction.annotation.delete')); + } else { + context.perform( + iD.actions.RestrictTurn(datum, projection), + t('operations.restriction.annotation.create')); + } + } + } + + function mouseover() { + var datum = d3.event.target.__data__; + if (datum instanceof iD.geo.Turn) { + var graph = context.graph(), + presets = context.presets(), + preset; + + if (datum.restriction) { + preset = presets.match(graph.entity(datum.restriction), graph); + } else { + preset = presets.item('type/restriction/' + + iD.geo.inferRestriction( + graph.entity(datum.from.node), + graph.entity(datum.via.node), + graph.entity(datum.to.node), + projection)); + } + + wrap.selectAll('.restriction-help') + .text(t('operations.restriction.help.' + + (datum.restriction ? 'toggle_off' : 'toggle_on'), + {restriction: preset.name()})); + } + } + + function mouseout() { + wrap.selectAll('.restriction-help') + .text(t('operations.restriction.help.' + + (fromNodeID ? 'toggle' : 'select'))); + } + + function render() { + if (context.hasEntity(vertexID)) { + restrictions(selection); + } + } + } + + restrictions.entity = function(_) { + if (!vertexID || vertexID !== _.id) { + fromNodeID = null; + vertexID = _.id; + } + }; + + restrictions.tags = function() {}; + restrictions.focus = function() {}; + + return d3.rebind(restrictions, event, 'on'); +}; iD.ui.preset.textarea = function(field) { var event = d3.dispatch('change'), @@ -30356,9 +31688,9 @@ iD.ui.preset.wikipedia = function(field, context) { .value('English'); lang + .call(langcombo) .on('blur', changeLang) - .on('change', changeLang) - .call(langcombo); + .on('change', changeLang); title = selection.selectAll('input.wiki-title') .data([0]); @@ -30369,9 +31701,9 @@ iD.ui.preset.wikipedia = function(field, context) { .attr('id', 'preset-input-' + field.id); title + .call(titlecombo) .on('blur', change) - .on('change', change) - .call(titlecombo); + .on('change', change); link = selection.selectAll('a.wiki-link') .data([0]); @@ -31222,7 +32554,7 @@ iD.presets.Field = function(id, field) { field.id = id; field.matchGeometry = function(geometry) { - return !field.geometry || field.geometry.indexOf(geometry) >= 0; + return !field.geometry || field.geometry === geometry; }; field.t = function(scope, options) { @@ -31384,7 +32716,7 @@ iD.validate = function(changes, graph) { if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !change.isUsed(graph)) { warnings.push({ message: t('validations.untagged_' + geometry), - tooltip: t('validations.untagged_tooltip', {geometry: geometry}), + tooltip: t('validations.untagged_' + geometry + '_tooltip'), entity: change }); } @@ -38283,6 +39615,42 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms_url": "http://geo.nls.uk/maps/", "terms_text": "National Library of Scotland Historic Maps" }, + { + "name": "Ireland British War Office 1:25k GSGS 3906", + "type": "tms", + "template": "http://mapwarper.net/layers/tile/101/{zoom}/{x}/{y}.png", + "scaleExtent": [ + 0, + 18 + ], + "polygon": [ + [ + [ + -10.71, + 51.32 + ], + [ + -10.71, + 55.46 + ], + [ + -5.37, + 55.46 + ], + [ + -5.37, + 51.32 + ], + [ + -10.71, + 51.32 + ] + ] + ], + "terms_url": "http://wiki.openstreetmap.org/wiki/WikiProject_Ireland#Trinity_College_Dublin", + "terms_text": "Glucksman Map Library, Trinity College Dublin", + "id": "GSGS3906" + }, { "name": "Ireland British War Office One-Inch 1941-43 GSGS 4136", "type": "tms", @@ -38512,7 +39880,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ] ], "terms_url": "http://geo.nls.uk/maps/", - "terms_text": "National Library of Scotland Historic Maps" + "terms_text": "National Library of Scotland Historic Maps", + "id": "GSGS4136" }, { "name": "Ireland EEA CORINE 2006", @@ -41680,7 +43049,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Locator Overlay", "type": "tms", "description": "Shows major features to help orient you.", - "template": "http://{switch:a,b,c}.tiles.mapbox.com/v3/openstreetmap.map-btyhiati/{zoom}/{x}/{y}.png", + "template": "http://{switch:a,b,c}.tiles.mapbox.com/v4/openstreetmap.map-inh76ba2/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJhNVlHd29ZIn0.ti6wATGDWOmCnCYen-Ip7Q", "scaleExtent": [ 0, 16 @@ -41700,7 +43069,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Mapbox Satellite", "type": "tms", "description": "Satellite and aerial imagery.", - "template": "http://{switch:a,b,c}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/{zoom}/{x}/{y}.png", + "template": "http://{switch:a,b,c}.tiles.mapbox.com/v4/openstreetmap.map-inh7ifmo/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJhNVlHd29ZIn0.ti6wATGDWOmCnCYen-Ip7Q", "scaleExtent": [ 0, 19 @@ -62978,7 +64347,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "fields": [ "ref", - "surface" + "surface", + "length", + "width" ], "tags": { "aeroway": "runway" @@ -63204,6 +64575,19 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "name": "Boat Rental" }, + "amenity/bus_station": { + "geometry": [ + "point", + "area" + ], + "tags": { + "amenity": "bus_station" + }, + "fields": [ + "operator" + ], + "name": "Bus Station" + }, "amenity/cafe": { "icon": "cafe", "fields": [ @@ -63259,6 +64643,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Car Sharing" }, "amenity/car_wash": { + "icon": "car", "geometry": [ "point", "area" @@ -63271,6 +64656,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "name": "Car Wash" }, + "amenity/charging_station": { + "icon": "car", + "geometry": [ + "point", + "area" + ], + "tags": { + "amenity": "charging_station" + }, + "fields": [ + "operator" + ], + "terms": [ + "EV", + "Electric Vehicle", + "Supercharger" + ], + "name": "Charging Station" + }, "amenity/childcare": { "icon": "school", "fields": [ @@ -63373,6 +64777,17 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": [], "name": "College" }, + "amenity/compressed_air": { + "icon": "car", + "geometry": [ + "point", + "area" + ], + "tags": { + "amenity": "compressed_air" + }, + "name": "Compressed Air" + }, "amenity/courthouse": { "fields": [ "operator", @@ -63429,6 +64844,26 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "opening_hours" ] }, + "amenity/dojo": { + "icon": "pitch", + "geometry": [ + "point", + "area" + ], + "terms": [ + "martial arts", + "dojo", + "dojang" + ], + "tags": { + "amenity": "dojo" + }, + "fields": [ + "address", + "sport" + ], + "name": "Dojo / Martial Arts Academy" + }, "amenity/drinking_water": { "icon": "water", "geometry": [ @@ -63676,6 +65111,20 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": [], "name": "Car Parking" }, + "amenity/parking_entrance": { + "icon": "entrance", + "geometry": [ + "vertex" + ], + "tags": { + "amenity": "parking_entrance" + }, + "fields": [ + "access_simple", + "ref" + ], + "name": "Parking Garage Entrance/Exit" + }, "amenity/pharmacy": { "icon": "pharmacy", "fields": [ @@ -64292,8 +65741,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "toilets/disposal", "operator", "building_area", - "fee", - "access_simple" + "access_toilets" ], "geometry": [ "point", @@ -64632,8 +66080,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "fields": [ "building", "levels", - "address", - "smoking" + "address" ], "geometry": [ "area" @@ -64829,7 +66276,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "tags": { "building": "entrance" }, - "name": "Entrance", + "name": "Entrance/Exit", "searchable": false }, "building/garage": { @@ -65984,7 +67431,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "address", "operator", "opening_hours" - ] + ], + "searchable": false }, "craft/tiler": { "name": "Tiler", @@ -66154,13 +67602,15 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "access_simple", "address" ], - "name": "Entrance" + "name": "Entrance/Exit" }, "footway/crossing": { "fields": [ "crossing", "access", - "surface" + "surface", + "sloped_curb", + "tactile_paving" ], "geometry": [ "line" @@ -66169,16 +67619,37 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "highway": "footway", "footway": "crossing" }, + "terms": [], + "name": "Crossing" + }, + "footway/crosswalk": { + "fields": [ + "crossing", + "access", + "surface", + "sloped_curb", + "tactile_paving" + ], + "geometry": [ + "line" + ], + "tags": { + "highway": "footway", + "footway": "crossing", + "crossing": "zebra" + }, "terms": [ "crosswalk", "zebra crossing" ], - "name": "Crossing" + "name": "Crosswalk" }, "footway/sidewalk": { "fields": [ "surface", "lit", + "width", + "structure", "access" ], "geometry": [ @@ -66191,6 +67662,15 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": [], "name": "Sidewalk" }, + "ford": { + "geometry": [ + "vertex" + ], + "tags": { + "ford": "yes" + }, + "name": "Ford" + }, "golf/bunker": { "icon": "golf", "geometry": [ @@ -66314,9 +67794,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "highway/bridleway": { "fields": [ - "access", "surface", - "structure" + "width", + "structure", + "access" ], "icon": "highway-bridleway", "geometry": [ @@ -66352,7 +67833,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "highway/crossing": { "fields": [ - "crossing" + "crossing", + "sloped_curb", + "tactile_paving" ], "geometry": [ "vertex" @@ -66360,20 +67843,37 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "tags": { "highway": "crossing" }, + "terms": [], + "name": "Crossing" + }, + "highway/crosswalk": { + "fields": [ + "crossing", + "sloped_curb", + "tactile_paving" + ], + "geometry": [ + "vertex" + ], + "tags": { + "highway": "crossing", + "crossing": "zebra" + }, "terms": [ "crosswalk", "zebra crossing" ], - "name": "Crossing" + "name": "Crosswalk" }, "highway/cycleway": { "icon": "highway-cycleway", "fields": [ "surface", "lit", + "width", + "oneway", "structure", - "access", - "oneway" + "access" ], "geometry": [ "line" @@ -66387,9 +67887,11 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "highway/footway": { "icon": "highway-footway", "fields": [ + "surface", + "lit", + "width", "structure", - "access", - "surface" + "access" ], "geometry": [ "line", @@ -66458,7 +67960,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "highway/motorway": { "icon": "highway-motorway", "fields": [ - "oneway", + "oneway_yes", "maxspeed", "structure", "access", @@ -66485,7 +67987,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "fields": [ "ref" ], - "name": "Motorway Junction" + "name": "Motorway Junction / Exit" }, "highway/motorway_link": { "icon": "highway-motorway-link", @@ -66513,12 +68015,16 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "highway/path": { "icon": "highway-path", "fields": [ + "surface", + "width", "structure", "access", - "sac_scale", - "surface", "incline", + "sac_scale", "trail_visibility", + "mtb/scale", + "mtb/scale/uphill", + "mtb/scale/imba", "ref" ], "geometry": [ @@ -66532,9 +68038,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "highway/pedestrian": { "fields": [ - "access", + "surface", + "lit", + "width", "oneway", - "surface" + "structure", + "access" ], "geometry": [ "line", @@ -66801,8 +68310,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "highway/steps": { "fields": [ - "access", - "surface" + "surface", + "lit", + "width", + "access" ], "icon": "highway-steps", "geometry": [ @@ -66829,6 +68340,27 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "name": "Stop Sign" }, + "highway/street_lamp": { + "geometry": [ + "point", + "vertex" + ], + "tags": { + "highway": "street_lamp" + }, + "fields": [ + "lamp_type", + "ref" + ], + "terms": [ + "streetlight", + "street light", + "lamp", + "light", + "gaslight" + ], + "name": "Street Lamp" + }, "highway/tertiary": { "icon": "highway-tertiary", "fields": [ @@ -66875,12 +68407,16 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "highway/track": { "icon": "highway-track", "fields": [ - "tracktype", - "oneway", - "maxspeed", + "surface", + "width", "structure", "access", - "surface" + "incline", + "tracktype", + "smoothness", + "mtb/scale", + "mtb/scale/uphill", + "mtb/scale/imba" ], "geometry": [ "line" @@ -67133,7 +68669,22 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": [], "name": "Cemetery" }, + "landuse/churchyard": { + "fields": [ + "religion", + "denomination" + ], + "geometry": [ + "area" + ], + "tags": { + "landuse": "churchyard" + }, + "terms": [], + "name": "Churchyard" + }, "landuse/commercial": { + "icon": "commercial", "geometry": [ "point", "area" @@ -67266,6 +68817,16 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": [], "name": "Meadow" }, + "landuse/military": { + "geometry": [ + "area" + ], + "tags": { + "landuse": "military" + }, + "terms": [], + "name": "Military" + }, "landuse/orchard": { "fields": [ "trees" @@ -67293,6 +68854,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Quarry" }, "landuse/residential": { + "icon": "building", "geometry": [ "point", "area" @@ -67491,7 +69053,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "icon": "pitch", "fields": [ "sport", - "surface" + "surface", + "lit" ], "geometry": [ "point", @@ -67506,7 +69069,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure/pitch/american_football": { "icon": "america-football", "fields": [ - "surface" + "surface", + "lit" ], "geometry": [ "point", @@ -67521,6 +69085,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "leisure/pitch/baseball": { "icon": "baseball", + "fields": [ + "lit" + ], "geometry": [ "point", "area" @@ -67536,7 +69103,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "icon": "basketball", "fields": [ "surface", - "hoops" + "hoops", + "lit" ], "geometry": [ "point", @@ -67552,7 +69120,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure/pitch/skateboard": { "icon": "pitch", "fields": [ - "surface" + "surface", + "lit" ], "geometry": [ "point", @@ -67568,7 +69137,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure/pitch/soccer": { "icon": "soccer", "fields": [ - "surface" + "surface", + "lit" ], "geometry": [ "point", @@ -67584,7 +69154,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure/pitch/tennis": { "icon": "tennis", "fields": [ - "surface" + "surface", + "lit" ], "geometry": [ "point", @@ -67600,7 +69171,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure/pitch/volleyball": { "icon": "pitch", "fields": [ - "surface" + "surface", + "lit" ], "geometry": [ "point", @@ -67639,6 +69211,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Slipway" }, "leisure/sports_center": { + "icon": "pitch", "geometry": [ "point", "area" @@ -67649,10 +69222,13 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": [ "gym" ], - "icon": "sports", - "name": "Sports Center" + "fields": [ + "sport" + ], + "name": "Sports Center / Gym" }, "leisure/stadium": { + "icon": "pitch", "geometry": [ "point", "area" @@ -67683,7 +69259,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure/track": { "icon": "pitch", "fields": [ - "surface" + "surface", + "lit", + "width" ], "geometry": [ "point", @@ -68609,6 +70187,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/city": { "icon": "city", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -68620,6 +70201,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/hamlet": { "icon": "triangle-stroked", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -68661,6 +70245,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/locality": { "icon": "marker", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -68672,6 +70259,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/neighbourhood": { "icon": "triangle-stroked", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -68686,6 +70276,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/suburb": { "icon": "triangle-stroked", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -68701,6 +70294,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/town": { "icon": "town", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -68712,6 +70308,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "place/village": { "icon": "village", + "fields": [ + "population" + ], "geometry": [ "point", "area" @@ -69321,7 +70920,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Car Dealership" }, "shop/car_parts": { - "icon": "shop", + "icon": "car", "fields": [ "address", "building_area", @@ -69338,7 +70937,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Car Parts Store" }, "shop/car_repair": { - "icon": "shop", + "icon": "car", "fields": [ "address", "building_area", @@ -69355,7 +70954,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Car Repair Shop" }, "shop/chemist": { - "icon": "shop", + "icon": "chemist", "fields": [ "address", "building_area", @@ -69676,7 +71275,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Greengrocer" }, "shop/hairdresser": { - "icon": "shop", + "icon": "hairdresser", "fields": [ "address", "building_area", @@ -69832,7 +71431,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Mall" }, "shop/mobile_phone": { - "icon": "shop", + "icon": "mobilephone", "fields": [ "address", "building_area", @@ -69849,7 +71448,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Mobile Phone Store" }, "shop/motorcycle": { - "icon": "shop", + "icon": "scooter", "fields": [ "address", "building_area", @@ -70079,6 +71678,27 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "name": "Supermarket" }, + "shop/tailor": { + "name": "Tailor", + "geometry": [ + "point", + "area" + ], + "terms": [ + "tailor", + "clothes" + ], + "tags": { + "shop": "tailor" + }, + "icon": "clothing-store", + "fields": [ + "building_area", + "address", + "operator", + "opening_hours" + ] + }, "shop/toys": { "icon": "shop", "fields": [ @@ -70181,6 +71801,26 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "name": "Video Store" }, + "shop/wine": { + "icon": "alcohol-shop", + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "geometry": [ + "point", + "vertex", + "area" + ], + "tags": { + "shop": "wine" + }, + "terms": [ + "winery" + ], + "name": "Wine Shop" + }, "tourism": { "fields": [ "tourism" @@ -70538,9 +72178,108 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Restriction", "icon": "restriction", "fields": [ - "restriction" + "restriction", + "except" ] }, + "type/restriction/no_left_turn": { + "name": "No Left Turn", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "no_left_turn" + }, + "fields": [ + "except" + ], + "icon": "restriction-no-left-turn" + }, + "type/restriction/no_right_turn": { + "name": "No Right Turn", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "no_right_turn" + }, + "fields": [ + "except" + ], + "icon": "restriction-no-right-turn" + }, + "type/restriction/no_straight_on": { + "name": "No Straight On", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "no_straight_on" + }, + "fields": [ + "except" + ], + "icon": "restriction-no-straight-on" + }, + "type/restriction/no_u_turn": { + "name": "No U-turn", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "no_u_turn" + }, + "fields": [ + "except" + ], + "icon": "restriction-no-u-turn" + }, + "type/restriction/only_left_turn": { + "name": "Left Turn Only", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "only_left_turn" + }, + "fields": [ + "except" + ], + "icon": "restriction-only-left-turn" + }, + "type/restriction/only_right_turn": { + "name": "Right Turn Only", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "only_right_turn" + }, + "fields": [ + "except" + ], + "icon": "restriction-only-right-turn" + }, + "type/restriction/only_straight_on": { + "name": "No Turns", + "geometry": [ + "relation" + ], + "tags": { + "type": "restriction", + "restriction": "only_straight_on" + }, + "fields": [ + "except" + ], + "icon": "restriction-only-straight-on" + }, "type/route": { "geometry": [ "relation" @@ -70763,6 +72502,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "waterway/canal": { "icon": "waterway-canal", + "fields": [ + "width" + ], "geometry": [ "line" ], @@ -70813,7 +72555,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "waterway/river": { "icon": "waterway-river", "fields": [ - "tunnel" + "tunnel", + "width" ], "geometry": [ "line" @@ -70851,7 +72594,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "waterway/stream": { "icon": "waterway-stream", "fields": [ - "tunnel" + "tunnel", + "width" ], "geometry": [ "line" @@ -71146,25 +72890,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Sainsbury's": { - "tags": { - "name": "Sainsbury's", - "amenity": "fuel" - }, - "name": "Sainsbury's", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/OMV": { "tags": { "name": "OMV", @@ -71203,25 +72928,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Tesco": { - "tags": { - "name": "Tesco", - "amenity": "fuel" - }, - "name": "Tesco", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/JET": { "tags": { "name": "JET", @@ -71241,25 +72947,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Morrisons": { - "tags": { - "name": "Morrisons", - "amenity": "fuel" - }, - "name": "Morrisons", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/United": { "tags": { "name": "United", @@ -71279,25 +72966,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Canadian Tire": { - "tags": { - "name": "Canadian Tire", - "amenity": "fuel" - }, - "name": "Canadian Tire", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Mobil": { "tags": { "name": "Mobil", @@ -71374,25 +73042,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/ABC": { - "tags": { - "name": "ABC", - "amenity": "fuel" - }, - "name": "ABC", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/ARAL": { "tags": { "name": "ARAL", @@ -71469,25 +73118,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Intermarché": { - "tags": { - "name": "Intermarché", - "amenity": "fuel" - }, - "name": "Intermarché", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Total Access": { "tags": { "name": "Total Access", @@ -71507,44 +73137,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Super U": { - "tags": { - "name": "Super U", - "amenity": "fuel" - }, - "name": "Super U", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, - "amenity/fuel/Auchan": { - "tags": { - "name": "Auchan", - "amenity": "fuel" - }, - "name": "Auchan", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Elf": { "tags": { "name": "Elf", @@ -71564,25 +73156,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Carrefour": { - "tags": { - "name": "Carrefour", - "amenity": "fuel" - }, - "name": "Carrefour", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Station Service E. Leclerc": { "tags": { "name": "Station Service E. Leclerc", @@ -71925,25 +73498,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Raiffeisenbank": { - "tags": { - "name": "Raiffeisenbank", - "amenity": "fuel" - }, - "name": "Raiffeisenbank", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Tamoil": { "tags": { "name": "Tamoil", @@ -72039,25 +73593,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Coop": { - "tags": { - "name": "Coop", - "amenity": "fuel" - }, - "name": "Coop", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Orlen": { "tags": { "name": "Orlen", @@ -72267,25 +73802,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/7-Eleven": { - "tags": { - "name": "7-Eleven", - "amenity": "fuel" - }, - "name": "7-Eleven", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Agrola": { "tags": { "name": "Agrola", @@ -73407,25 +74923,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Wawa": { - "tags": { - "name": "Wawa", - "amenity": "fuel" - }, - "name": "Wawa", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Pertamina": { "tags": { "name": "Pertamina", @@ -73921,25 +75418,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Circle K": { - "tags": { - "name": "Circle K", - "amenity": "fuel" - }, - "name": "Circle K", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Posto Ipiranga": { "tags": { "name": "Posto Ipiranga", @@ -74092,25 +75570,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/Stewart's": { - "tags": { - "name": "Stewart's", - "amenity": "fuel" - }, - "name": "Stewart's", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Posto BR": { "tags": { "name": "Posto BR", @@ -74282,25 +75741,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/fuel/H-E-B": { - "tags": { - "name": "H-E-B", - "amenity": "fuel" - }, - "name": "H-E-B", - "icon": "fuel", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "address", - "building_area" - ], - "suggestion": true - }, "amenity/fuel/Укрнафта": { "tags": { "name": "Укрнафта", @@ -75377,6 +76817,28 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "amenity/fast_food/Subway": { + "tags": { + "name": "Subway", + "cuisine": "sandwich", + "amenity": "fast_food" + }, + "name": "Subway", + "icon": "fast-food", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "cuisine", + "building_area", + "address", + "opening_hours", + "smoking" + ], + "suggestion": true + }, "amenity/fast_food/Burger King": { "tags": { "name": "Burger King", @@ -80084,6 +81546,26 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "amenity/bank/Raiffeisenbank": { + "tags": { + "name": "Raiffeisenbank", + "amenity": "bank" + }, + "name": "Raiffeisenbank", + "icon": "bank", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "atm", + "building_area", + "address", + "opening_hours" + ], + "suggestion": true + }, "amenity/bank/Yorkshire Bank": { "tags": { "name": "Yorkshire Bank", @@ -85803,26 +87285,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "amenity/pharmacy/Радуга": { - "tags": { - "name": "Радуга", - "amenity": "pharmacy" - }, - "name": "Радуга", - "icon": "pharmacy", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address", - "opening_hours" - ], - "suggestion": true - }, "amenity/pharmacy/サンドラッグ": { "tags": { "name": "サンドラッグ", @@ -86983,6 +88445,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/supermarket/Morrisons": { + "tags": { + "name": "Morrisons", + "shop": "supermarket" + }, + "name": "Morrisons", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, "shop/supermarket/Interspar": { "tags": { "name": "Interspar", @@ -87021,6 +88502,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/supermarket/Sainsbury's": { + "tags": { + "name": "Sainsbury's", + "shop": "supermarket" + }, + "name": "Sainsbury's", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, "shop/supermarket/Lidl": { "tags": { "name": "Lidl", @@ -87097,6 +88597,44 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/supermarket/Coop": { + "tags": { + "name": "Coop", + "shop": "supermarket" + }, + "name": "Coop", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, + "shop/supermarket/Tesco": { + "tags": { + "name": "Tesco", + "shop": "supermarket" + }, + "name": "Tesco", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, "shop/supermarket/Woolworths": { "tags": { "name": "Woolworths", @@ -87439,25 +88977,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Tesco Express": { - "tags": { - "name": "Tesco Express", - "shop": "supermarket" - }, - "name": "Tesco Express", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/King Soopers": { "tags": { "name": "King Soopers", @@ -87572,6 +89091,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/supermarket/Carrefour": { + "tags": { + "name": "Carrefour", + "shop": "supermarket" + }, + "name": "Carrefour", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, "shop/supermarket/Waitrose": { "tags": { "name": "Waitrose", @@ -87971,6 +89509,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/supermarket/Super U": { + "tags": { + "name": "Super U", + "shop": "supermarket" + }, + "name": "Super U", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, "shop/supermarket/Metro": { "tags": { "name": "Metro", @@ -88199,25 +89756,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/COOP Jednota": { - "tags": { - "name": "COOP Jednota", - "shop": "supermarket" - }, - "name": "COOP Jednota", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Rema 1000": { "tags": { "name": "Rema 1000", @@ -88845,6 +90383,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/supermarket/Auchan": { + "tags": { + "name": "Auchan", + "shop": "supermarket" + }, + "name": "Auchan", + "icon": "grocery", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "operator", + "building_area", + "address" + ], + "suggestion": true + }, "shop/supermarket/Mercadona": { "tags": { "name": "Mercadona", @@ -88997,12 +90554,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Costcutter": { + "shop/supermarket/Maxi": { "tags": { - "name": "Costcutter", + "name": "Maxi", "shop": "supermarket" }, - "name": "Costcutter", + "name": "Maxi", "icon": "grocery", "geometry": [ "point", @@ -89016,12 +90573,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Maxi": { + "shop/supermarket/Colruyt": { "tags": { - "name": "Maxi", + "name": "Colruyt", "shop": "supermarket" }, - "name": "Maxi", + "name": "Colruyt", "icon": "grocery", "geometry": [ "point", @@ -89035,12 +90592,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Colruyt": { + "shop/supermarket/The Co-operative": { "tags": { - "name": "Colruyt", + "name": "The Co-operative", "shop": "supermarket" }, - "name": "Colruyt", + "name": "The Co-operative", "icon": "grocery", "geometry": [ "point", @@ -89054,12 +90611,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/The Co-operative": { + "shop/supermarket/Intermarché": { "tags": { - "name": "The Co-operative", + "name": "Intermarché", "shop": "supermarket" }, - "name": "The Co-operative", + "name": "Intermarché", "icon": "grocery", "geometry": [ "point", @@ -89244,25 +90801,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/dm": { - "tags": { - "name": "dm", - "shop": "supermarket" - }, - "name": "dm", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Kvickly": { "tags": { "name": "Kvickly", @@ -89510,25 +91048,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Petit Casino": { - "tags": { - "name": "Petit Casino", - "shop": "supermarket" - }, - "name": "Petit Casino", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Wasgau": { "tags": { "name": "Wasgau", @@ -90118,25 +91637,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Fressnapf": { - "tags": { - "name": "Fressnapf", - "shop": "supermarket" - }, - "name": "Fressnapf", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Coop Konsum": { "tags": { "name": "Coop Konsum", @@ -90384,25 +91884,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Centra": { - "tags": { - "name": "Centra", - "shop": "supermarket" - }, - "name": "Centra", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Квартал": { "tags": { "name": "Квартал", @@ -90992,12 +92473,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Атак": { + "shop/supermarket/H-E-B": { "tags": { - "name": "Атак", + "name": "H-E-B", "shop": "supermarket" }, - "name": "Атак", + "name": "H-E-B", "icon": "grocery", "geometry": [ "point", @@ -91011,12 +92492,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Полушка": { + "shop/supermarket/Атак": { "tags": { - "name": "Полушка", + "name": "Атак", "shop": "supermarket" }, - "name": "Полушка", + "name": "Атак", "icon": "grocery", "geometry": [ "point", @@ -91030,12 +92511,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Extra": { + "shop/supermarket/Полушка": { "tags": { - "name": "Extra", + "name": "Полушка", "shop": "supermarket" }, - "name": "Extra", + "name": "Полушка", "icon": "grocery", "geometry": [ "point", @@ -91049,12 +92530,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Lewiatan": { + "shop/supermarket/Extra": { "tags": { - "name": "Lewiatan", + "name": "Extra", "shop": "supermarket" }, - "name": "Lewiatan", + "name": "Extra", "icon": "grocery", "geometry": [ "point", @@ -91106,25 +92587,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Społem": { - "tags": { - "name": "Społem", - "shop": "supermarket" - }, - "name": "Społem", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Bodega Aurrera": { "tags": { "name": "Bodega Aurrera", @@ -91201,25 +92663,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/supermarket/Магазин": { - "tags": { - "name": "Магазин", - "shop": "supermarket" - }, - "name": "Магазин", - "icon": "grocery", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "operator", - "building_area", - "address" - ], - "suggestion": true - }, "shop/supermarket/Монетка": { "tags": { "name": "Монетка", @@ -91657,6 +93100,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Tesco Express": { + "tags": { + "name": "Tesco Express", + "shop": "convenience" + }, + "name": "Tesco Express", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/One Stop": { "tags": { "name": "One Stop", @@ -91695,6 +93157,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/7-Eleven": { + "tags": { + "name": "7-Eleven", + "shop": "convenience" + }, + "name": "7-Eleven", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Sale": { "tags": { "name": "Sale", @@ -91733,6 +93214,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/COOP Jednota": { + "tags": { + "name": "COOP Jednota", + "shop": "convenience" + }, + "name": "COOP Jednota", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Mac's": { "tags": { "name": "Mac's", @@ -91809,6 +93309,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Costcutter": { + "tags": { + "name": "Costcutter", + "shop": "convenience" + }, + "name": "Costcutter", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Valintatalo": { "tags": { "name": "Valintatalo", @@ -91828,6 +93347,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Circle K": { + "tags": { + "name": "Circle K", + "shop": "convenience" + }, + "name": "Circle K", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/セブンイレブン": { "tags": { "name": "セブンイレブン", @@ -91868,6 +93406,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Petit Casino": { + "tags": { + "name": "Petit Casino", + "shop": "convenience" + }, + "name": "Petit Casino", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Mace": { "tags": { "name": "Mace", @@ -92077,6 +93634,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/ABC": { + "tags": { + "name": "ABC", + "shop": "convenience" + }, + "name": "ABC", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/ミニストップ": { "tags": { "name": "ミニストップ", @@ -92308,6 +93884,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Магазин": { + "tags": { + "name": "Магазин", + "shop": "convenience" + }, + "name": "Магазин", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Гастроном": { "tags": { "name": "Гастроном", @@ -92346,6 +93941,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Centra": { + "tags": { + "name": "Centra", + "shop": "convenience" + }, + "name": "Centra", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/サークルK": { "tags": { "name": "サークルK", @@ -92366,6 +93980,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Wawa": { + "tags": { + "name": "Wawa", + "shop": "convenience" + }, + "name": "Wawa", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Proxi": { "tags": { "name": "Proxi", @@ -92537,6 +94170,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Społem": { + "tags": { + "name": "Społem", + "shop": "convenience" + }, + "name": "Społem", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Cumberland Farms": { "tags": { "name": "Cumberland Farms", @@ -92575,6 +94227,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Kiosk": { + "tags": { + "name": "Kiosk", + "shop": "convenience" + }, + "name": "Kiosk", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/24 часа": { "tags": { "name": "24 часа", @@ -92670,6 +94341,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Stewart's": { + "tags": { + "name": "Stewart's", + "shop": "convenience" + }, + "name": "Stewart's", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Продукти": { "tags": { "name": "Продукти", @@ -92708,6 +94398,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Радуга": { + "tags": { + "name": "Радуга", + "shop": "convenience" + }, + "name": "Радуга", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/ローソンストア100": { "tags": { "name": "ローソンストア100", @@ -93088,6 +94797,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Lewiatan": { + "tags": { + "name": "Lewiatan", + "shop": "convenience" + }, + "name": "Lewiatan", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/Продуктовый магазин": { "tags": { "name": "Продуктовый магазин", @@ -93316,6 +95044,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/convenience/Boutique": { + "tags": { + "name": "Boutique", + "shop": "convenience" + }, + "name": "Boutique", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/convenience/მარკეტი (Market)": { "tags": { "name": "მარკეტი (Market)", @@ -93354,13 +95101,32 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/chemist/dm": { + "tags": { + "name": "dm", + "shop": "chemist" + }, + "name": "dm", + "icon": "chemist", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/chemist/Müller": { "tags": { "name": "Müller", "shop": "chemist" }, "name": "Müller", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93379,7 +95145,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Schlecker", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93398,7 +95164,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Etos", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93417,7 +95183,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Bipa", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93436,7 +95202,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Rossmann", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93455,7 +95221,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "DM Drogeriemarkt", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93474,7 +95240,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Ihr Platz", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93493,7 +95259,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Douglas", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93512,26 +95278,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "chemist" }, "name": "Kruidvat", - "icon": "shop", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "address", - "building_area", - "opening_hours" - ], - "suggestion": true - }, - "shop/car_repair/Peugeot": { - "tags": { - "name": "Peugeot", - "shop": "car_repair" - }, - "name": "Peugeot", - "icon": "shop", + "icon": "chemist", "geometry": [ "point", "vertex", @@ -93550,7 +95297,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Kwik Fit", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93569,7 +95316,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "ATU", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93588,7 +95335,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Kwik-Fit", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93607,7 +95354,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Midas", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93626,7 +95373,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Feu Vert", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93645,7 +95392,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Norauto", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93664,45 +95411,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Speedy", - "icon": "shop", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "address", - "building_area", - "opening_hours" - ], - "suggestion": true - }, - "shop/car_repair/Автозапчасти": { - "tags": { - "name": "Автозапчасти", - "shop": "car_repair" - }, - "name": "Автозапчасти", - "icon": "shop", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "address", - "building_area", - "opening_hours" - ], - "suggestion": true - }, - "shop/car_repair/Renault": { - "tags": { - "name": "Renault", - "shop": "car_repair" - }, - "name": "Renault", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93721,7 +95430,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Pit Stop", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93740,7 +95449,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Jiffy Lube", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93759,7 +95468,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Шиномонтаж", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93778,7 +95487,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "СТО", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93797,7 +95506,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "O'Reilly Auto Parts", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93816,7 +95525,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Carglass", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93835,26 +95544,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "шиномонтаж", - "icon": "shop", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "address", - "building_area", - "opening_hours" - ], - "suggestion": true - }, - "shop/car_repair/Citroen": { - "tags": { - "name": "Citroen", - "shop": "car_repair" - }, - "name": "Citroen", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93873,7 +95563,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Euromaster", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93892,7 +95582,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Firestone", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93911,7 +95601,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "AutoZone", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93930,7 +95620,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Автосервис", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93949,7 +95639,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Advance Auto Parts", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -93968,7 +95658,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "car_repair" }, "name": "Roady", - "icon": "shop", + "icon": "car", "geometry": [ "point", "vertex", @@ -94475,6 +96165,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/doityourself/Canadian Tire": { + "tags": { + "name": "Canadian Tire", + "shop": "doityourself" + }, + "name": "Canadian Tire", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/doityourself/Leroy Merlin": { "tags": { "name": "Leroy Merlin", @@ -94703,25 +96412,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/doityourself/Хозтовары": { - "tags": { - "name": "Хозтовары", - "shop": "doityourself" - }, - "name": "Хозтовары", - "icon": "shop", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "address", - "building_area", - "opening_hours" - ], - "suggestion": true - }, "shop/doityourself/Стройматериалы": { "tags": { "name": "Стройматериалы", @@ -94891,6 +96581,42 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/car/Citroen": { + "tags": { + "name": "Citroen", + "shop": "car" + }, + "name": "Citroen", + "icon": "car", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "opening_hours" + ], + "suggestion": true + }, + "shop/car/Renault": { + "tags": { + "name": "Renault", + "shop": "car" + }, + "name": "Renault", + "icon": "car", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "opening_hours" + ], + "suggestion": true + }, "shop/car/Mercedes-Benz": { "tags": { "name": "Mercedes-Benz", @@ -95017,6 +96743,24 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/car/Автозапчасти": { + "tags": { + "name": "Автозапчасти", + "shop": "car" + }, + "name": "Автозапчасти", + "icon": "car", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "opening_hours" + ], + "suggestion": true + }, "shop/car/Opel": { "tags": { "name": "Opel", @@ -95125,6 +96869,24 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/car/Peugeot": { + "tags": { + "name": "Peugeot", + "shop": "car" + }, + "name": "Peugeot", + "icon": "car", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "opening_hours" + ], + "suggestion": true + }, "shop/car/Hyundai": { "tags": { "name": "Hyundai", @@ -95672,25 +97434,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, - "shop/clothes/Deichmann": { - "tags": { - "name": "Deichmann", - "shop": "clothes" - }, - "name": "Deichmann", - "icon": "clothing-store", - "geometry": [ - "point", - "vertex", - "area" - ], - "fields": [ - "address", - "building_area", - "opening_hours" - ], - "suggestion": true - }, "shop/clothes/Lindex": { "tags": { "name": "Lindex", @@ -97971,6 +99714,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/pet/Fressnapf": { + "tags": { + "name": "Fressnapf", + "shop": "pet" + }, + "name": "Fressnapf", + "icon": "dog-park", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/pet/PetSmart": { "tags": { "name": "PetSmart", @@ -98066,6 +99828,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/shoes/Deichmann": { + "tags": { + "name": "Deichmann", + "shop": "shoes" + }, + "name": "Deichmann", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/shoes/Reno": { "tags": { "name": "Reno", @@ -98756,7 +100537,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Билайн", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98775,7 +100556,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "ソフトバンクショップ (SoftBank shop)", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98794,7 +100575,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Vodafone", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98813,7 +100594,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "O2", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98832,7 +100613,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Carphone Warehouse", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98851,7 +100632,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Orange", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98870,7 +100651,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Verizon Wireless", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98889,7 +100670,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Sprint", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98908,7 +100689,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "T-Mobile", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98927,7 +100708,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "МТС", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98946,7 +100727,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Евросеть", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98965,7 +100746,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Bell", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -98984,7 +100765,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "The Phone House", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99003,7 +100784,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "SFR", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99022,7 +100803,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Связной", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99041,7 +100822,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Мегафон", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99060,7 +100841,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "AT&T", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99079,7 +100860,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "ドコモショップ (docomo shop)", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99098,7 +100879,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "au", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99117,7 +100898,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Movistar", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99136,7 +100917,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "mobile_phone" }, "name": "Bitė", - "icon": "shop", + "icon": "mobilephone", "geometry": [ "point", "vertex", @@ -99193,7 +100974,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Klier", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99212,7 +100993,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Supercuts", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99231,7 +101012,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Hairkiller", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99250,7 +101031,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Great Clips", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99269,7 +101050,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Парикмахерская", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99288,7 +101069,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Стиль", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99307,7 +101088,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Fryzjer", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99326,7 +101107,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Franck Provost", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99345,7 +101126,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": "hairdresser" }, "name": "Салон красоты", - "icon": "shop", + "icon": "hairdresser", "geometry": [ "point", "vertex", @@ -99377,13 +101158,32 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "suggestion": true }, + "shop/hardware/Хозтовары": { + "tags": { + "name": "Хозтовары", + "shop": "hardware" + }, + "name": "Хозтовары", + "icon": "shop", + "geometry": [ + "point", + "vertex", + "area" + ], + "fields": [ + "address", + "building_area", + "opening_hours" + ], + "suggestion": true + }, "shop/motorcycle/Yamaha": { "tags": { "name": "Yamaha", "shop": "motorcycle" }, "name": "Yamaha", - "icon": "shop", + "icon": "scooter", "geometry": [ "point", "vertex", @@ -99429,7 +101229,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "point" ], "vertex": [ - "highway/crossing", + "highway/crosswalk", "railway/level_crossing", "highway/traffic_signals", "highway/turning_circle", @@ -99439,8 +101239,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," ], "relation": [ "category-route", + "category-restriction", "type/boundary", - "type/restriction", "type/multipolygon", "relation" ] @@ -99486,7 +101286,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "landuse/farmyard", "landuse/forest", "landuse/meadow", - "landuse/cemetery" + "landuse/cemetery", + "landuse/military" ] }, "category-path": { @@ -99514,6 +101315,21 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "railway/abandoned" ] }, + "category-restriction": { + "geometry": "relation", + "name": "Restriction", + "icon": "restriction", + "members": [ + "type/restriction/no_left_turn", + "type/restriction/no_right_turn", + "type/restriction/no_straight_on", + "type/restriction/no_u_turn", + "type/restriction/only_left_turn", + "type/restriction/only_right_turn", + "type/restriction/only_straight_on", + "type/restriction" + ] + }, "category-road": { "geometry": "line", "name": "Road", @@ -99589,6 +101405,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "bicycle", "horse" ], + "reference": { + "key": "access" + }, "type": "access", "label": "Access", "placeholder": "Unknown", @@ -99629,6 +101448,18 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," } }, "access_simple": { + "key": "access", + "type": "combo", + "label": "Access", + "placeholder": "yes", + "options": [ + "permissive", + "private", + "customers", + "no" + ] + }, + "access_toilets": { "key": "access", "type": "combo", "label": "Access", @@ -99642,20 +101473,33 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "address": { "type": "address", "keys": [ + "addr:housename", "addr:housenumber", "addr:street", "addr:city", "addr:postcode" ], + "reference": { + "key": "addr" + }, "icon": "address", "universal": true, "label": "Address", "strings": { "placeholders": { - "number": "123", + "housename": "Housename", + "housenumber": "123", "street": "Street", "city": "City", - "postcode": "Postal code" + "postcode": "Postcode", + "place": "Place", + "hamlet": "Hamlet", + "suburb": "Suburb", + "subdistrict": "Subdistrict", + "district": "District", + "province": "Province", + "state": "State", + "country": "Country" } } }, @@ -99672,12 +101516,14 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "aerialway/access": { "key": "aerialway:access", "type": "combo", - "options": [ - "entry", - "exit", - "both" - ], - "label": "Access" + "label": "Access", + "strings": { + "options": { + "entry": "Entry", + "exit": "Exit", + "both": "Both" + } + } }, "aerialway/bubble": { "key": "aerialway:bubble", @@ -99710,12 +101556,14 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "aerialway/summer/access": { "key": "aerialway:summer:access", "type": "combo", - "options": [ - "entry", - "exit", - "both" - ], - "label": "Access (summer)" + "label": "Access (summer)", + "strings": { + "options": { + "entry": "Entry", + "exit": "Exit", + "both": "Both" + } + } }, "aeroway": { "key": "aeroway", @@ -99769,7 +101617,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "building_area": { "key": "building", - "type": "check", + "type": "defaultcheck", "default": "yes", "geometry": "area", "label": "Building" @@ -99783,32 +101631,31 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "cardinal_direction": { "key": "direction", "type": "combo", - "options": [ - "N", - "E", - "S", - "W", - "NE", - "SE", - "SW", - "NNE", - "ENE", - "ESE", - "SSE", - "SSW", - "WSW", - "WNW", - "NNW" - ], - "label": "Direction" + "label": "Direction", + "strings": { + "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": { "key": "direction", "type": "combo", - "options": [ - "clockwise", - "anticlockwise" - ], "label": "Direction", "strings": { "options": { @@ -99850,7 +101697,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "cuisine": { "key": "cuisine", "type": "combo", - "indexed": true, "label": "Cuisine" }, "denomination": { @@ -99872,12 +101718,15 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "key": "electrified", "type": "combo", "label": "Electrification", - "options": [ - "contact_line", - "rail", - "yes", - "no" - ] + "placeholder": "Contact Line, Electrified Rail...", + "strings": { + "options": { + "contact_line": "Contact Line", + "rail": "Electrified Rail", + "yes": "Yes (unspecified)", + "no": "No" + } + } }, "elevation": { "key": "ele", @@ -99896,6 +101745,11 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "type": "typeCombo", "label": "Type" }, + "except": { + "key": "except", + "type": "combo", + "label": "Exceptions" + }, "fax": { "key": "fax", "type": "tel", @@ -99910,13 +101764,15 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "fire_hydrant/type": { "key": "fire_hydrant:type", "type": "combo", - "options": [ - "pillar", - "pond", - "underground", - "wall" - ], - "label": "Type" + "label": "Type", + "strings": { + "options": { + "pillar": "Pillar/Aboveground", + "underground": "Underground", + "wall": "Wall", + "pond": "Pond" + } + } }, "fixme": { "key": "fixme", @@ -99928,6 +101784,51 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "type": "combo", "label": "Fuel" }, + "fuel/biodiesel": { + "key": "fuel:biodiesel", + "type": "check", + "label": "Sells Biodiesel" + }, + "fuel/diesel": { + "key": "fuel:diesel", + "type": "check", + "label": "Sells Diesel" + }, + "fuel/e10": { + "key": "fuel:e10", + "type": "check", + "label": "Sells E10" + }, + "fuel/e85": { + "key": "fuel:e85", + "type": "check", + "label": "Sells E85" + }, + "fuel/lpg": { + "key": "fuel:lpg", + "type": "check", + "label": "Sells Propane" + }, + "fuel/octane_100": { + "key": "fuel:octane_100", + "type": "check", + "label": "Sells Racing Gasoline" + }, + "fuel/octane_91": { + "key": "fuel:octane_91", + "type": "check", + "label": "Sells Regular Gasoline" + }, + "fuel/octane_95": { + "key": "fuel:octane_95", + "type": "check", + "label": "Sells Midgrade Gasoline" + }, + "fuel/octane_98": { + "key": "fuel:octane_98", + "type": "check", + "label": "Sells Premium Gasoline" + }, "gauge": { "key": "gauge", "type": "combo", @@ -99999,13 +101900,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "internet_access": { "key": "internet_access", "type": "combo", - "options": [ - "yes", - "no", - "wlan", - "wired", - "terminal" - ], "label": "Internet Access", "strings": { "options": { @@ -100017,6 +101911,11 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," } } }, + "lamp_type": { + "key": "lamp_type", + "type": "combo", + "label": "Type" + }, "landuse": { "key": "landuse", "type": "typeCombo", @@ -100038,6 +101937,11 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "type": "typeCombo", "label": "Type" }, + "length": { + "key": "length", + "type": "number", + "label": "Length (Meters)" + }, "levels": { "key": "building:levels", "type": "number", @@ -100065,6 +101969,54 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Speed Limit", "placeholder": "40, 50, 60..." }, + "mtb/scale": { + "key": "mtb:scale", + "type": "combo", + "label": "Mountain Biking Difficulty", + "placeholder": "0, 1, 2, 3...", + "strings": { + "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": { + "key": "mtb:scale:imba", + "type": "combo", + "label": "IMBA Trail Difficulty", + "placeholder": "Easy, Medium, Difficult...", + "strings": { + "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": { + "key": "mtb:scale:uphill", + "type": "combo", + "label": "Mountain Biking Uphill Difficulty", + "placeholder": "0, 1, 2, 3...", + "strings": { + "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": { "key": "name", "type": "localized", @@ -100096,13 +102048,26 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "oneway": { "key": "oneway", "type": "check", - "label": "One Way" + "label": "One Way", + "strings": { + "options": { + "undefined": "Assumed to be No", + "yes": "Yes", + "no": "No" + } + } }, "oneway_yes": { "key": "oneway", "type": "check", - "default": "yes", - "label": "One Way" + "label": "One Way", + "strings": { + "options": { + "undefined": "Assumed to be Yes", + "yes": "Yes", + "no": "No" + } + } }, "opening_hours": { "key": "opening_hours", @@ -100128,16 +102093,18 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "parking": { "key": "parking", "type": "combo", - "options": [ - "surface", - "multi-storey", - "underground", - "sheds", - "carports", - "garage_boxes", - "lane" - ], - "label": "Type" + "label": "Type", + "strings": { + "options": { + "surface": "Surface", + "multi-storey": "Multilevel", + "underground": "Underground", + "sheds": "Sheds", + "carports": "Carports", + "garage_boxes": "Garage Boxes", + "lane": "Roadside Lane" + } + } }, "phone": { "key": "phone", @@ -100150,23 +102117,63 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "piste/difficulty": { "key": "piste:difficulty", "type": "combo", - "label": "Difficulty" + "label": "Difficulty", + "placeholder": "Easy, Intermediate, Advanced...", + "strings": { + "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": { "key": "piste:grooming", "type": "combo", - "label": "Grooming" + "label": "Grooming", + "strings": { + "options": { + "classic": "Classic", + "mogul": "Mogul", + "backcountry": "Backcountry", + "classic+skating": "Classic and Skating", + "scooter": "Scooter/Snowmobile", + "skating": "Skating" + } + } }, "piste/type": { "key": "piste:type", "type": "typeCombo", - "label": "Type" + "label": "Type", + "strings": { + "options": { + "downhill": "Downhill", + "nordic": "Nordic", + "skitour": "Skitour", + "sled": "Sled", + "hike": "Hike", + "sleigh": "Sleigh", + "ice_skate": "Ice Skate", + "snow_park": "Snow Park", + "playground": "Playground" + } + } }, "place": { "key": "place", "type": "typeCombo", "label": "Type" }, + "population": { + "key": "population", + "type": "text", + "label": "Population" + }, "power": { "key": "power", "type": "typeCombo", @@ -100210,33 +102217,22 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "religion": { "key": "religion", "type": "combo", - "options": [ - "christian", - "muslim", - "buddhist", - "jewish", - "hindu", - "shinto", - "taoist" - ], - "label": "Religion", - "strings": { - "options": { - "christian": "Christian", - "muslim": "Muslim", - "buddhist": "Buddhist", - "jewish": "Jewish", - "hindu": "Hindu", - "shinto": "Shinto", - "taoist": "Taoist" - } - } + "label": "Religion" }, "restriction": { "key": "restriction", "type": "combo", "label": "Type" }, + "restrictions": { + "type": "restrictions", + "geometry": "vertex", + "icon": "restrictions", + "reference": { + "rtype": "restriction" + }, + "label": "Turn Restrictions" + }, "route": { "key": "route", "type": "combo", @@ -100250,7 +102246,18 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "sac_scale": { "key": "sac_scale", "type": "combo", - "label": "Path Difficulty" + "label": "Hiking Difficulty", + "placeholder": "Mountain Hiking, Alpine Hiking...", + "strings": { + "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" + } + } }, "seasonal": { "key": "seasonal", @@ -100260,14 +102267,14 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "service": { "key": "service", "type": "combo", + "label": "Type", "options": [ "parking_aisle", "driveway", "alley", - "drive-through", - "emergency_access" - ], - "label": "Type" + "emergency_access", + "drive-through" + ] }, "shelter": { "key": "shelter", @@ -100277,15 +102284,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shelter_type": { "key": "shelter_type", "type": "combo", - "options": [ - "public_transport", - "picnic_shelter", - "weather_shelter", - "lean_to", - "basic_hut", - "field_shelter", - "rock_shelter" - ], "label": "Type" }, "shop": { @@ -100293,16 +102291,44 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "type": "typeCombo", "label": "Type" }, + "sloped_curb": { + "key": "sloped_curb", + "type": "combo", + "label": "Sloped Curb" + }, "smoking": { "key": "smoking", "type": "combo", - "options": [ - "no", - "outside", - "separated", - "yes" - ], - "label": "Smoking" + "label": "Smoking", + "placeholder": "No, Separated, Yes...", + "strings": { + "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": { + "key": "smoothness", + "type": "combo", + "label": "Smoothness", + "placeholder": "Thin Rollers, Wheels, Off-Road...", + "strings": { + "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", + "impassible": "Impassible / No wheeled vehicle" + } + } }, "social_facility_for": { "key": "social_facility:for", @@ -100341,14 +102367,14 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "sport_ice": { "key": "sport", "type": "combo", + "label": "Sport", "options": [ "skating", "hockey", "multi", "curling", "ice_stock" - ], - "label": "Sport" + ] }, "structure": { "type": "radio", @@ -100356,7 +102382,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "bridge", "tunnel", "embankment", - "cutting" + "cutting", + "ford" ], "label": "Structure", "placeholder": "Unknown", @@ -100365,18 +102392,19 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "bridge": "Bridge", "tunnel": "Tunnel", "embankment": "Embankment", - "cutting": "Cutting" + "cutting": "Cutting", + "ford": "Ford" } } }, "studio_type": { "key": "type", "type": "combo", + "label": "Type", "options": [ "audio", "video" - ], - "label": "Type" + ] }, "supervised": { "key": "supervised", @@ -100388,10 +102416,23 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "type": "combo", "label": "Surface" }, + "tactile_paving": { + "key": "tactile_paving", + "type": "check", + "label": "Tactile Paving" + }, "toilets/disposal": { "key": "toilets:disposal", "type": "combo", - "label": "Disposal" + "label": "Disposal", + "strings": { + "options": { + "flush": "Flush", + "pitlatrine": "Pit/Latrine", + "chemical": "Chemical", + "bucket": "Bucket" + } + } }, "tourism": { "key": "tourism", @@ -100406,22 +102447,43 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "tracktype": { "key": "tracktype", "type": "combo", - "label": "Type" + "label": "Track Type", + "placeholder": "Solid, Mostly Solid, Soft...", + "strings": { + "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" + } + } }, "trail_visibility": { "key": "trail_visibility", "type": "combo", - "label": "Trail Visibility" + "label": "Trail Visibility", + "placeholder": "Excellent, Good, Bad...", + "strings": { + "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" + } + } }, "tree_type": { "key": "type", "type": "combo", + "label": "Type", "options": [ "broad_leaved", "conifer", "palm" - ], - "label": "Type" + ] }, "trees": { "key": "trees", @@ -100473,6 +102535,11 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "universal": true, "label": "Wheelchair Access" }, + "width": { + "key": "width", + "type": "number", + "label": "Width (Meters)" + }, "wikipedia": { "key": "wikipedia", "type": "wikipedia", @@ -111903,6 +113970,62 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," 504 ] }, + "hairdresser": { + "12": [ + 42, + 528 + ], + "18": [ + 24, + 528 + ], + "24": [ + 0, + 528 + ] + }, + "chemist": { + "12": [ + 96, + 528 + ], + "18": [ + 78, + 528 + ], + "24": [ + 54, + 528 + ] + }, + "mobilephone": { + "12": [ + 150, + 528 + ], + "18": [ + 132, + 528 + ], + "24": [ + 108, + 528 + ] + }, + "scooter": { + "12": [ + 204, + 528 + ], + "18": [ + 186, + 528 + ], + "24": [ + 162, + 528 + ] + }, "highway-motorway": { "line": [ 20, @@ -112226,6 +114349,48 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," 920, 25 ] + }, + "restriction-no-straight-on": { + "relation": [ + 980, + 25 + ] + }, + "restriction-no-u-turn": { + "relation": [ + 1040, + 25 + ] + }, + "restriction-no-left-turn": { + "relation": [ + 1100, + 25 + ] + }, + "restriction-no-right-turn": { + "relation": [ + 1160, + 25 + ] + }, + "restriction-only-straight-on": { + "relation": [ + 1220, + 25 + ] + }, + "restriction-only-left-turn": { + "relation": [ + 1280, + 25 + ] + }, + "restriction-only-right-turn": { + "relation": [ + 1340, + 25 + ] } }, "operations": { @@ -112324,6 +114489,30 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "icon-operation-disabled-continue": [ 220, 160 + ], + "icon-restriction-yes": [ + 50, + 80 + ], + "icon-restriction-no": [ + 95, + 80 + ], + "icon-restriction-only": [ + 140, + 80 + ], + "icon-restriction-yes-u": [ + 185, + 80 + ], + "icon-restriction-no-u": [ + 230, + 80 + ], + "icon-restriction-only-u": [ + 275, + 80 ] }, "locales": [ @@ -112350,7 +114539,6 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "nl", "en-GB", "et", - "fil", "fi", "fr", "gl", @@ -112382,6 +114570,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "sl", "es", "sv", + "tl", "ta", "te", "tr", @@ -112487,7 +114676,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "delete": { "title": "Delete", - "description": "Remove this from the map.", + "description": "Delete object permanently.", "annotation": { "point": "Deleted a point.", "vertex": "Deleted a node from a way.", @@ -112540,7 +114729,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "area": "Moved an area.", "multiple": "Moved multiple objects." }, - "incomplete_relation": "This feature can't be moved because it hasn't been fully downloaded." + "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." }, "rotate": { "title": "Rotate", @@ -112549,7 +114739,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "annotation": { "line": "Rotated a line.", "area": "Rotated an area." - } + }, + "too_large": "This can't be rotated because not enough of it is currently visible." }, "reverse": { "title": "Reverse", @@ -112572,6 +114763,18 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "not_eligible": "Lines can't be split at their beginning or end.", "multiple_ways": "There are too many lines here to split." + }, + "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": { @@ -112714,7 +114917,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "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_tooltip": "Select a feature type that describes what this {geometry} is.", + "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": { @@ -112806,6 +115011,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "category-rail": { "name": "Rail" }, + "category-restriction": { + "name": "Restriction" + }, "category-road": { "name": "Road" }, @@ -112858,15 +115066,28 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," } }, "access_simple": { + "label": "Access", + "placeholder": "yes" + }, + "access_toilets": { "label": "Access" }, "address": { "label": "Address", "placeholders": { - "number": "123", + "housename": "Housename", + "housenumber": "123", "street": "Street", "city": "City", - "postcode": "Postal code" + "postcode": "Postcode", + "place": "Place", + "hamlet": "Hamlet", + "suburb": "Suburb", + "subdistrict": "Subdistrict", + "district": "District", + "province": "Province", + "state": "State", + "country": "Country" } }, "admin_level": { @@ -112876,7 +115097,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Type" }, "aerialway/access": { - "label": "Access" + "label": "Access", + "options": { + "entry": "Entry", + "exit": "Exit", + "both": "Both" + } }, "aerialway/bubble": { "label": "Bubble" @@ -112897,7 +115123,12 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "placeholder": "2, 4, 8..." }, "aerialway/summer/access": { - "label": "Access (summer)" + "label": "Access (summer)", + "options": { + "entry": "Entry", + "exit": "Exit", + "both": "Both" + } }, "aeroway": { "label": "Type" @@ -112937,7 +115168,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "placeholder": "50, 100, 200..." }, "cardinal_direction": { - "label": "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", @@ -112977,7 +115226,14 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Description" }, "electrified": { - "label": "Electrification" + "label": "Electrification", + "placeholder": "Contact Line, Electrified Rail...", + "options": { + "contact_line": "Contact Line", + "rail": "Electrified Rail", + "yes": "Yes (unspecified)", + "no": "No" + } }, "elevation": { "label": "Elevation" @@ -112988,6 +115244,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "entrance": { "label": "Type" }, + "except": { + "label": "Exceptions" + }, "fax": { "label": "Fax", "placeholder": "+31 42 123 4567" @@ -112996,7 +115255,13 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Fee" }, "fire_hydrant/type": { - "label": "Type" + "label": "Type", + "options": { + "pillar": "Pillar/Aboveground", + "underground": "Underground", + "wall": "Wall", + "pond": "Pond" + } }, "fixme": { "label": "Fix Me" @@ -113004,6 +115269,33 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "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" }, @@ -113056,6 +115348,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terminal": "Terminal" } }, + "lamp_type": { + "label": "Type" + }, "landuse": { "label": "Type" }, @@ -113069,6 +115364,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "leisure": { "label": "Type" }, + "length": { + "label": "Length (Meters)" + }, "levels": { "label": "Levels", "placeholder": "2, 4, 6..." @@ -113086,6 +115384,42 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Speed Limit", "placeholder": "40, 50, 60..." }, + "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)" @@ -113103,10 +115437,20 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Type" }, "oneway": { - "label": "One Way" + "label": "One Way", + "options": { + "undefined": "Assumed to be No", + "yes": "Yes", + "no": "No" + } }, "oneway_yes": { - "label": "One Way" + "label": "One Way", + "options": { + "undefined": "Assumed to be Yes", + "yes": "Yes", + "no": "No" + } }, "opening_hours": { "label": "Hours" @@ -113122,24 +115466,65 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Park and Ride" }, "parking": { - "label": "Type" + "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" + "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" + "label": "Grooming", + "options": { + "classic": "Classic", + "mogul": "Mogul", + "backcountry": "Backcountry", + "classic+skating": "Classic and Skating", + "scooter": "Scooter/Snowmobile", + "skating": "Skating" + } }, "piste/type": { - "label": "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" }, @@ -113165,20 +115550,14 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Type" }, "religion": { - "label": "Religion", - "options": { - "christian": "Christian", - "muslim": "Muslim", - "buddhist": "Buddhist", - "jewish": "Jewish", - "hindu": "Hindu", - "shinto": "Shinto", - "taoist": "Taoist" - } + "label": "Religion" }, "restriction": { "label": "Type" }, + "restrictions": { + "label": "Turn Restrictions" + }, "route": { "label": "Type" }, @@ -113186,7 +115565,16 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Type" }, "sac_scale": { - "label": "Path Difficulty" + "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" + } }, "seasonal": { "label": "Seasonal" @@ -113203,8 +115591,34 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "shop": { "label": "Type" }, + "sloped_curb": { + "label": "Sloped Curb" + }, "smoking": { - "label": "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", + "impassible": "Impassible / No wheeled vehicle" + } }, "social_facility_for": { "label": "People served", @@ -113226,7 +115640,8 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "bridge": "Bridge", "tunnel": "Tunnel", "embankment": "Embankment", - "cutting": "Cutting" + "cutting": "Cutting", + "ford": "Ford" } }, "studio_type": { @@ -113238,8 +115653,17 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "surface": { "label": "Surface" }, + "tactile_paving": { + "label": "Tactile Paving" + }, "toilets/disposal": { - "label": "Disposal" + "label": "Disposal", + "options": { + "flush": "Flush", + "pitlatrine": "Pit/Latrine", + "chemical": "Chemical", + "bucket": "Bucket" + } }, "tourism": { "label": "Type" @@ -113248,10 +115672,27 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "label": "Tower type" }, "tracktype": { - "label": "Type" + "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" + } }, "trail_visibility": { - "label": "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" + } }, "tree_type": { "label": "Type" @@ -113281,6 +115722,9 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "wheelchair": { "label": "Wheelchair Access" }, + "width": { + "label": "Width (Meters)" + }, "wikipedia": { "label": "Wikipedia" }, @@ -113409,6 +115853,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Boat Rental", "terms": "" }, + "amenity/bus_station": { + "name": "Bus Station", + "terms": "" + }, "amenity/cafe": { "name": "Cafe", "terms": "coffee,tea,coffee shop" @@ -113425,6 +115873,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Car Wash", "terms": "" }, + "amenity/charging_station": { + "name": "Charging Station", + "terms": "EV,Electric Vehicle,Supercharger" + }, "amenity/childcare": { "name": "Childcare", "terms": "nursery,orphanage,playgroup" @@ -113445,6 +115897,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "College", "terms": "" }, + "amenity/compressed_air": { + "name": "Compressed Air", + "terms": "" + }, "amenity/courthouse": { "name": "Courthouse", "terms": "" @@ -113457,6 +115913,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Doctor", "terms": "doctor,doctor's office" }, + "amenity/dojo": { + "name": "Dojo / Martial Arts Academy", + "terms": "martial arts,dojo,dojang" + }, "amenity/drinking_water": { "name": "Drinking Water", "terms": "water fountain,potable water" @@ -113509,6 +115969,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Car Parking", "terms": "" }, + "amenity/parking_entrance": { + "name": "Parking Garage Entrance/Exit", + "terms": "" + }, "amenity/pharmacy": { "name": "Pharmacy", "terms": "" @@ -113754,7 +116218,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": "" }, "building/entrance": { - "name": "Entrance", + "name": "Entrance/Exit", "terms": "" }, "building/garage": { @@ -114034,17 +116498,25 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": "" }, "entrance": { - "name": "Entrance", + "name": "Entrance/Exit", "terms": "" }, "footway/crossing": { "name": "Crossing", + "terms": "" + }, + "footway/crosswalk": { + "name": "Crosswalk", "terms": "crosswalk,zebra crossing" }, "footway/sidewalk": { "name": "Sidewalk", "terms": "" }, + "ford": { + "name": "Ford", + "terms": "" + }, "golf/bunker": { "name": "Sand Trap", "terms": "hazard,bunker" @@ -114091,6 +116563,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," }, "highway/crossing": { "name": "Crossing", + "terms": "" + }, + "highway/crosswalk": { + "name": "Crosswalk", "terms": "crosswalk,zebra crossing" }, "highway/cycleway": { @@ -114114,7 +116590,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": "" }, "highway/motorway_junction": { - "name": "Motorway Junction", + "name": "Motorway Junction / Exit", "terms": "" }, "highway/motorway_link": { @@ -114193,6 +116669,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "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": "" @@ -114277,6 +116757,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Cemetery", "terms": "" }, + "landuse/churchyard": { + "name": "Churchyard", + "terms": "" + }, "landuse/commercial": { "name": "Commercial", "terms": "" @@ -114317,6 +116801,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Meadow", "terms": "" }, + "landuse/military": { + "name": "Military", + "terms": "" + }, "landuse/orchard": { "name": "Orchard", "terms": "" @@ -114418,7 +116906,7 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "terms": "" }, "leisure/sports_center": { - "name": "Sports Center", + "name": "Sports Center / Gym", "terms": "gym" }, "leisure/stadium": { @@ -115045,6 +117533,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Supermarket", "terms": "bazaar,boutique,chain,co-op,cut-rate store,discount store,five-and-dime,flea market,galleria,grocery store,mall,mart,outlet,outlet store,shop,shopping center,shopping centre,shopping plaza,stand,store,supermarket,thrift shop" }, + "shop/tailor": { + "name": "Tailor", + "terms": "tailor,clothes" + }, "shop/toys": { "name": "Toy Store", "terms": "" @@ -115069,6 +117561,10 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "name": "Video Store", "terms": "" }, + "shop/wine": { + "name": "Wine Shop", + "terms": "winery" + }, "tourism": { "name": "Tourism", "terms": "" @@ -115153,6 +117649,34 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," "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": "" @@ -120083,5 +122607,151 @@ iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]," } } } - } + }, + "addressFormats": [ + { + "format": [ + [ + "housenumber", + "street" + ], + [ + "city", + "postcode" + ] + ] + }, + { + "countryCodes": [ + "gb" + ], + "format": [ + [ + "housename" + ], + [ + "housenumber", + "street" + ], + [ + "city", + "postcode" + ] + ] + }, + { + "countryCodes": [ + "ie" + ], + "format": [ + [ + "housename" + ], + [ + "housenumber", + "street" + ], + [ + "city" + ] + ] + }, + { + "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" + ] + ] + } + ] }; \ No newline at end of file