}
})(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;
};
}
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 <http://en.wikipedia.org/wiki/Quantile>
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;
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) {
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); });
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 = [],
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;
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 = {};
// 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;
}
// 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;
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() {
};
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;
};
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];
}
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);
}
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);
}
});
}
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);
scaleTo(view.k * 2);
translateTo(p, l);
d3_eventPreventDefault();
- zoomed(event_);
+ zoomed(dispatch);
}
touchtime = now;
} else if (touches.length > 1) {
}
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) {
touchtime = null;
translateTo(p0, l0);
- zoomed(event_);
+ zoomed(dispatch);
}
function ended() {
}
}
// 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() {
}
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");
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?
clip.lineEnd = ringEnd;
segments = [];
polygon = [];
- listener.polygonStart();
},
polygonEnd: function() {
clip.point = point;
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() {
var buffer = d3_geo_clipBufferListener(),
ringListener = clipLine(buffer),
+ polygonStarted = false,
polygon,
ring;
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;
}
// 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.
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?
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;
}
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 ||
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; };
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);
}
/* 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);
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;
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);
}
];
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; }));
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; }));
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 = [],
// on mousedown
d3.event.stopPropagation();
d3.event.preventDefault();
- input.node().focus();
- fetch('', render);
+ if (!shown) {
+ input.node().focus();
+ fetch('', render);
+ } else {
+ hide();
+ }
});
});
/**
* @license
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
- * 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 <http://dojofoundation.org/>
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
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`.
*
*/
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.
lodash.merge = merge;
lodash.omit = omit;
lodash.pairs = pairs;
+ lodash.pick = pick;
lodash.pluck = pluck;
lodash.reject = reject;
lodash.throttle = throttle;
lodash.isString = isString;
lodash.mixin = mixin;
lodash.noop = noop;
+ lodash.reduce = reduce;
lodash.some = some;
lodash.sortedIndex = sortedIndex;
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]) {
}
})();
-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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+module.exports = element;
+module.exports.pair = pair;
+module.exports.format = format;
+module.exports.formatPair = formatPair;
+
+function element(x, dims) {
+ return search(x, dims).val;
+}
+
+function formatPair(x) {
+ return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
+}
+
+// Is 0 North or South?
+function format(x, dim) {
+ var dirs = {
+ lat: ['N', 'S'],
+ lon: ['E', 'W']
+ }[dim] || '',
+ dir = dirs[x >= 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),
};
/* 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);
return d3.rebind(context, dispatch, 'on');
};
-iD.version = '1.3.9';
+iD.version = '1.5.4';
(function() {
var detected = {};
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/',
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) {
(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
};
};
+// 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.
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]);
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) {
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) {
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;
// 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;
// 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];
totalAngle = endAngle - startAngle;
// detects looping around -pi/pi
- if (totalAngle*sign > 0) {
+ if (totalAngle * sign > 0) {
totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
}
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
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';
};
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) {
return action;
};
+// Create a restriction relation for `turn`, which must have the following structure:
+//
+// {
+// from: { node: <node ID>, way: <way ID> },
+// via: { node: <node ID> },
+// to: { node: <node ID>, way: <way ID> },
+// 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
return action;
};
+// Remove the effects of `turn.restriction` on `turn`, which must have the
+// following structure:
+//
+// {
+// from: { node: <node ID>, way: <way ID> },
+// via: { node: <node ID> },
+// to: { node: <node ID>, way: <way ID> },
+// restriction: <relation ID>
+// }
+//
+// 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'),
var p = point(),
dx = p[0] - origin_[0],
dy = p[1] - origin_[1];
+
+ if (dx === 0 && dy === 0)
+ return;
if (!started) {
started = true;
};
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() {
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
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;
}
context.map()
.on('move.hash', null);
+ context
+ .on('enter.hash', null);
+
d3.select(window)
.on('hashchange.hash', null);
.on('mouseup.lasso', mouseup);
d3.event.stopPropagation();
- d3.event.preventDefault();
-
}
}
button: 'browse',
id: 'browse',
title: t('modes.browse.title'),
- description: t('modes.browse.description'),
- key: '1'
+ description: t('modes.browse.description')
}, sidebar;
var behaviors = [
});
});
- 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);
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()
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);
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() {
}
}
- 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() {
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));
};
};
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() {
};
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() &&
};
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() {
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';
"bench": true,
"clock": true,
"drinking_water": true,
+ "parking_entrance": true,
"post_box": true,
"telephone": true,
"vending_machine": true,
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;
}
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;
}
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;
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)
});
},
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)
});
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)
});
connection.changesetTags = function(comment, imageryUsed) {
var tags = {
- imagery_used: imageryUsed.join(';'),
+ imagery_used: imageryUsed.join(';').substr(0, 255),
created_by: 'iD ' + iD.version
};
}
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);
});
},
+ isHighwayIntersection: function() {
+ return false;
+ },
+
deprecatedTags: function() {
var tags = _.pairs(this.tags);
var deprecated = {};
});
},
+ 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: {
};
}
});
+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);
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;
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()));
+ }
}
};
.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);
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});
}
});
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));
.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) {
}
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() {
}
};
+ 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]]),
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)));
i = 0,
offset = dt,
segments = [],
+ viewport = iD.geo.Extent(projection.clipExtent()),
coordinates = graph.childNodes(entity).map(function(n) {
return n.loc;
});
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),
beach: 'beach',
scrub: 'scrub',
construction: 'construction',
+ military: 'construction',
cemetery: 'cemetery',
grave_yard: 'cemetery',
meadow: 'meadow',
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]])) {
});
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);
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);
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);
}
.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);
};
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')
.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();
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; });
};
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; };
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
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); })
.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();
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')
.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')
.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();
};
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');
.attr('class', 'label')
.text(details.display_name);
- selection.append('a')
+ logoutLink.append('a')
.attr('class', 'logout')
.attr('href', '#')
.text(t('logout'))
}
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);
};
};
['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;
preset,
reference;
+ var presetEditor = iD.ui.preset(context)
+ .on('change', changeTags);
var rawTagEditor = iD.ui.RawTagEditor(context)
.on('change', changeTags);
.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
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;
}
});
}
- 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
});
}
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);
}
});
+ 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));
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);
presets.preset = function(_) {
if (!arguments.length) return preset;
+ if (preset && preset.id === _.id) return presets;
preset = _;
fields = null;
return presets;
presets.entityID = function(_) {
if (!arguments.length) return id;
+ if (id === _) return presets;
id = _;
fields = null;
return presets;
$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];
}
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) {
}
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);
}
});
};
};
+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) {
sidebar.hide = function() {
featureListWrap.classed('inspector-hidden', false);
+ inspectorWrap.classed('inspector-hidden', true);
if (current) current.remove();
current = null;
};
}
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) {
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'
}
};
_.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;
+ });
});
};
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()),
}
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(_) {
};
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')
.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();
});
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);
};
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) {
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 = {};
}
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);
};
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 =
.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(
));
}
+ input
+ .on('blur', change)
+ .on('change', change);
+
var translateButton = selection.selectAll('.localized-add')
.data([0]);
.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;
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'),
.value('English');
lang
+ .call(langcombo)
.on('blur', changeLang)
- .on('change', changeLang)
- .call(langcombo);
+ .on('change', changeLang);
title = selection.selectAll('input.wiki-title')
.data([0]);
.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]);
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) {
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
});
}
"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",
]
],
"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",
"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
"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
],
"fields": [
"ref",
- "surface"
+ "surface",
+ "length",
+ "width"
],
"tags": {
"aeroway": "runway"
],
"name": "Boat Rental"
},
+ "amenity/bus_station": {
+ "geometry": [
+ "point",
+ "area"
+ ],
+ "tags": {
+ "amenity": "bus_station"
+ },
+ "fields": [
+ "operator"
+ ],
+ "name": "Bus Station"
+ },
"amenity/cafe": {
"icon": "cafe",
"fields": [
"name": "Car Sharing"
},
"amenity/car_wash": {
+ "icon": "car",
"geometry": [
"point",
"area"
],
"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": [
"terms": [],
"name": "College"
},
+ "amenity/compressed_air": {
+ "icon": "car",
+ "geometry": [
+ "point",
+ "area"
+ ],
+ "tags": {
+ "amenity": "compressed_air"
+ },
+ "name": "Compressed Air"
+ },
"amenity/courthouse": {
"fields": [
"operator",
"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": [
"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": [
"toilets/disposal",
"operator",
"building_area",
- "fee",
- "access_simple"
+ "access_toilets"
],
"geometry": [
"point",
"fields": [
"building",
"levels",
- "address",
- "smoking"
+ "address"
],
"geometry": [
"area"
"tags": {
"building": "entrance"
},
- "name": "Entrance",
+ "name": "Entrance/Exit",
"searchable": false
},
"building/garage": {
"address",
"operator",
"opening_hours"
- ]
+ ],
+ "searchable": false
},
"craft/tiler": {
"name": "Tiler",
"access_simple",
"address"
],
- "name": "Entrance"
+ "name": "Entrance/Exit"
},
"footway/crossing": {
"fields": [
"crossing",
"access",
- "surface"
+ "surface",
+ "sloped_curb",
+ "tactile_paving"
],
"geometry": [
"line"
"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": [
"terms": [],
"name": "Sidewalk"
},
+ "ford": {
+ "geometry": [
+ "vertex"
+ ],
+ "tags": {
+ "ford": "yes"
+ },
+ "name": "Ford"
+ },
"golf/bunker": {
"icon": "golf",
"geometry": [
},
"highway/bridleway": {
"fields": [
- "access",
"surface",
- "structure"
+ "width",
+ "structure",
+ "access"
],
"icon": "highway-bridleway",
"geometry": [
},
"highway/crossing": {
"fields": [
- "crossing"
+ "crossing",
+ "sloped_curb",
+ "tactile_paving"
],
"geometry": [
"vertex"
"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"
"highway/footway": {
"icon": "highway-footway",
"fields": [
+ "surface",
+ "lit",
+ "width",
"structure",
- "access",
- "surface"
+ "access"
],
"geometry": [
"line",
"highway/motorway": {
"icon": "highway-motorway",
"fields": [
- "oneway",
+ "oneway_yes",
"maxspeed",
"structure",
"access",
"fields": [
"ref"
],
- "name": "Motorway Junction"
+ "name": "Motorway Junction / Exit"
},
"highway/motorway_link": {
"icon": "highway-motorway-link",
"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": [
},
"highway/pedestrian": {
"fields": [
- "access",
+ "surface",
+ "lit",
+ "width",
"oneway",
- "surface"
+ "structure",
+ "access"
],
"geometry": [
"line",
},
"highway/steps": {
"fields": [
- "access",
- "surface"
+ "surface",
+ "lit",
+ "width",
+ "access"
],
"icon": "highway-steps",
"geometry": [
],
"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": [
"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"
"terms": [],
"name": "Cemetery"
},
+ "landuse/churchyard": {
+ "fields": [
+ "religion",
+ "denomination"
+ ],
+ "geometry": [
+ "area"
+ ],
+ "tags": {
+ "landuse": "churchyard"
+ },
+ "terms": [],
+ "name": "Churchyard"
+ },
"landuse/commercial": {
+ "icon": "commercial",
"geometry": [
"point",
"area"
"terms": [],
"name": "Meadow"
},
+ "landuse/military": {
+ "geometry": [
+ "area"
+ ],
+ "tags": {
+ "landuse": "military"
+ },
+ "terms": [],
+ "name": "Military"
+ },
"landuse/orchard": {
"fields": [
"trees"
"name": "Quarry"
},
"landuse/residential": {
+ "icon": "building",
"geometry": [
"point",
"area"
"icon": "pitch",
"fields": [
"sport",
- "surface"
+ "surface",
+ "lit"
],
"geometry": [
"point",
"leisure/pitch/american_football": {
"icon": "america-football",
"fields": [
- "surface"
+ "surface",
+ "lit"
],
"geometry": [
"point",
},
"leisure/pitch/baseball": {
"icon": "baseball",
+ "fields": [
+ "lit"
+ ],
"geometry": [
"point",
"area"
"icon": "basketball",
"fields": [
"surface",
- "hoops"
+ "hoops",
+ "lit"
],
"geometry": [
"point",
"leisure/pitch/skateboard": {
"icon": "pitch",
"fields": [
- "surface"
+ "surface",
+ "lit"
],
"geometry": [
"point",
"leisure/pitch/soccer": {
"icon": "soccer",
"fields": [
- "surface"
+ "surface",
+ "lit"
],
"geometry": [
"point",
"leisure/pitch/tennis": {
"icon": "tennis",
"fields": [
- "surface"
+ "surface",
+ "lit"
],
"geometry": [
"point",
"leisure/pitch/volleyball": {
"icon": "pitch",
"fields": [
- "surface"
+ "surface",
+ "lit"
],
"geometry": [
"point",
"name": "Slipway"
},
"leisure/sports_center": {
+ "icon": "pitch",
"geometry": [
"point",
"area"
"terms": [
"gym"
],
- "icon": "sports",
- "name": "Sports Center"
+ "fields": [
+ "sport"
+ ],
+ "name": "Sports Center / Gym"
},
"leisure/stadium": {
+ "icon": "pitch",
"geometry": [
"point",
"area"
"leisure/track": {
"icon": "pitch",
"fields": [
- "surface"
+ "surface",
+ "lit",
+ "width"
],
"geometry": [
"point",
},
"place/city": {
"icon": "city",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
},
"place/hamlet": {
"icon": "triangle-stroked",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
},
"place/locality": {
"icon": "marker",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
},
"place/neighbourhood": {
"icon": "triangle-stroked",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
},
"place/suburb": {
"icon": "triangle-stroked",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
},
"place/town": {
"icon": "town",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
},
"place/village": {
"icon": "village",
+ "fields": [
+ "population"
+ ],
"geometry": [
"point",
"area"
"name": "Car Dealership"
},
"shop/car_parts": {
- "icon": "shop",
+ "icon": "car",
"fields": [
"address",
"building_area",
"name": "Car Parts Store"
},
"shop/car_repair": {
- "icon": "shop",
+ "icon": "car",
"fields": [
"address",
"building_area",
"name": "Car Repair Shop"
},
"shop/chemist": {
- "icon": "shop",
+ "icon": "chemist",
"fields": [
"address",
"building_area",
"name": "Greengrocer"
},
"shop/hairdresser": {
- "icon": "shop",
+ "icon": "hairdresser",
"fields": [
"address",
"building_area",
"name": "Mall"
},
"shop/mobile_phone": {
- "icon": "shop",
+ "icon": "mobilephone",
"fields": [
"address",
"building_area",
"name": "Mobile Phone Store"
},
"shop/motorcycle": {
- "icon": "shop",
+ "icon": "scooter",
"fields": [
"address",
"building_area",
},
"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": [
},
"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"
"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"
},
"waterway/canal": {
"icon": "waterway-canal",
+ "fields": [
+ "width"
+ ],
"geometry": [
"line"
],
"waterway/river": {
"icon": "waterway-river",
"fields": [
- "tunnel"
+ "tunnel",
+ "width"
],
"geometry": [
"line"
"waterway/stream": {
"icon": "waterway-stream",
"fields": [
- "tunnel"
+ "tunnel",
+ "width"
],
"geometry": [
"line"
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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": "Укрнафта",
],
"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",
],
"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",
],
"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": "サンドラッグ",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"suggestion": true
},
- "shop/supermarket/Costcutter": {
+ "shop/supermarket/Maxi": {
"tags": {
- "name": "Costcutter",
+ "name": "Maxi",
"shop": "supermarket"
},
- "name": "Costcutter",
+ "name": "Maxi",
"icon": "grocery",
"geometry": [
"point",
],
"suggestion": true
},
- "shop/supermarket/Maxi": {
+ "shop/supermarket/Colruyt": {
"tags": {
- "name": "Maxi",
+ "name": "Colruyt",
"shop": "supermarket"
},
- "name": "Maxi",
+ "name": "Colruyt",
"icon": "grocery",
"geometry": [
"point",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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": "Квартал",
],
"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",
],
"suggestion": true
},
- "shop/supermarket/Ð\9fолÑ\83Ñ\88ка": {
+ "shop/supermarket/Ð\90Ñ\82ак": {
"tags": {
- "name": "Ð\9fолÑ\83Ñ\88ка",
+ "name": "Ð\90Ñ\82ак",
"shop": "supermarket"
},
- "name": "Ð\9fолÑ\83Ñ\88ка",
+ "name": "Ð\90Ñ\82ак",
"icon": "grocery",
"geometry": [
"point",
],
"suggestion": true
},
- "shop/supermarket/Extra": {
+ "shop/supermarket/Полушка": {
"tags": {
- "name": "Extra",
+ "name": "Полушка",
"shop": "supermarket"
},
- "name": "Extra",
+ "name": "Полушка",
"icon": "grocery",
"geometry": [
"point",
],
"suggestion": true
},
- "shop/supermarket/Lewiatan": {
+ "shop/supermarket/Extra": {
"tags": {
- "name": "Lewiatan",
+ "name": "Extra",
"shop": "supermarket"
},
- "name": "Lewiatan",
+ "name": "Extra",
"icon": "grocery",
"geometry": [
"point",
],
"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",
],
"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": "Монетка",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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": "セブンイレブン",
],
"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",
],
"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": "ミニストップ",
],
"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": "Гастроном",
],
"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",
],
"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",
],
"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",
],
"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 часа",
],
"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": "Продукти",
],
"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",
],
"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": "Продуктовый магазин",
],
"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)",
],
"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",
"shop": "chemist"
},
"name": "Schlecker",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"shop": "chemist"
},
"name": "Etos",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"shop": "chemist"
},
"name": "Bipa",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"shop": "chemist"
},
"name": "Rossmann",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"shop": "chemist"
},
"name": "DM Drogeriemarkt",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"shop": "chemist"
},
"name": "Ihr Platz",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"shop": "chemist"
},
"name": "Douglas",
- "icon": "shop",
+ "icon": "chemist",
"geometry": [
"point",
"vertex",
"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",
"shop": "car_repair"
},
"name": "Kwik Fit",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "ATU",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Kwik-Fit",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Midas",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Feu Vert",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Norauto",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"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",
"shop": "car_repair"
},
"name": "Pit Stop",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Jiffy Lube",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Шиномонтаж",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "СТО",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "O'Reilly Auto Parts",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Carglass",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"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",
"shop": "car_repair"
},
"name": "Euromaster",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Firestone",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "AutoZone",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Автосервис",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Advance Auto Parts",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
"shop": "car_repair"
},
"name": "Roady",
- "icon": "shop",
+ "icon": "car",
"geometry": [
"point",
"vertex",
],
"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",
],
"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": "Стройматериалы",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
],
"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",
"shop": "mobile_phone"
},
"name": "Билайн",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "ソフトバンクショップ (SoftBank shop)",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Vodafone",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "O2",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Carphone Warehouse",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Orange",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Verizon Wireless",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Sprint",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "T-Mobile",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "МТС",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Евросеть",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Bell",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "The Phone House",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "SFR",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Связной",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Мегафон",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "AT&T",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "ドコモショップ (docomo shop)",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "au",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Movistar",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "mobile_phone"
},
"name": "Bitė",
- "icon": "shop",
+ "icon": "mobilephone",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Klier",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Supercuts",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Hairkiller",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Great Clips",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Парикмахерская",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Стиль",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Fryzjer",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Franck Provost",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
"shop": "hairdresser"
},
"name": "Салон красоты",
- "icon": "shop",
+ "icon": "hairdresser",
"geometry": [
"point",
"vertex",
],
"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",
"point"
],
"vertex": [
- "highway/crossing",
+ "highway/crosswalk",
"railway/level_crossing",
"highway/traffic_signals",
"highway/turning_circle",
],
"relation": [
"category-route",
+ "category-restriction",
"type/boundary",
- "type/restriction",
"type/multipolygon",
"relation"
]
"landuse/farmyard",
"landuse/forest",
"landuse/meadow",
- "landuse/cemetery"
+ "landuse/cemetery",
+ "landuse/military"
]
},
"category-path": {
"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",
"bicycle",
"horse"
],
+ "reference": {
+ "key": "access"
+ },
"type": "access",
"label": "Access",
"placeholder": "Unknown",
}
},
"access_simple": {
+ "key": "access",
+ "type": "combo",
+ "label": "Access",
+ "placeholder": "yes",
+ "options": [
+ "permissive",
+ "private",
+ "customers",
+ "no"
+ ]
+ },
+ "access_toilets": {
"key": "access",
"type": "combo",
"label": "Access",
"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"
}
}
},
"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",
"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",
},
"building_area": {
"key": "building",
- "type": "check",
+ "type": "defaultcheck",
"default": "yes",
"geometry": "area",
"label": "Building"
"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": {
"cuisine": {
"key": "cuisine",
"type": "combo",
- "indexed": true,
"label": "Cuisine"
},
"denomination": {
"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",
"type": "typeCombo",
"label": "Type"
},
+ "except": {
+ "key": "except",
+ "type": "combo",
+ "label": "Exceptions"
+ },
"fax": {
"key": "fax",
"type": "tel",
"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",
"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",
"internet_access": {
"key": "internet_access",
"type": "combo",
- "options": [
- "yes",
- "no",
- "wlan",
- "wired",
- "terminal"
- ],
"label": "Internet Access",
"strings": {
"options": {
}
}
},
+ "lamp_type": {
+ "key": "lamp_type",
+ "type": "combo",
+ "label": "Type"
+ },
"landuse": {
"key": "landuse",
"type": "typeCombo",
"type": "typeCombo",
"label": "Type"
},
+ "length": {
+ "key": "length",
+ "type": "number",
+ "label": "Length (Meters)"
+ },
"levels": {
"key": "building:levels",
"type": "number",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"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": {
"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",
"sport_ice": {
"key": "sport",
"type": "combo",
+ "label": "Sport",
"options": [
"skating",
"hockey",
"multi",
"curling",
"ice_stock"
- ],
- "label": "Sport"
+ ]
},
"structure": {
"type": "radio",
"bridge",
"tunnel",
"embankment",
- "cutting"
+ "cutting",
+ "ford"
],
"label": "Structure",
"placeholder": "Unknown",
"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",
"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",
"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",
"universal": true,
"label": "Wheelchair Access"
},
+ "width": {
+ "key": "width",
+ "type": "number",
+ "label": "Width (Meters)"
+ },
"wikipedia": {
"key": "wikipedia",
"type": "wikipedia",
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,
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": {
"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": [
"nl",
"en-GB",
"et",
- "fil",
"fi",
"fr",
"gl",
"sl",
"es",
"sv",
+ "tl",
"ta",
"te",
"tr",
},
"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.",
"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",
"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",
},
"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": {
"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": {
"category-rail": {
"name": "Rail"
},
+ "category-restriction": {
+ "name": "Restriction"
+ },
"category-road": {
"name": "Road"
},
}
},
"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": {
"label": "Type"
},
"aerialway/access": {
- "label": "Access"
+ "label": "Access",
+ "options": {
+ "entry": "Entry",
+ "exit": "Exit",
+ "both": "Both"
+ }
},
"aerialway/bubble": {
"label": "Bubble"
"placeholder": "2, 4, 8..."
},
"aerialway/summer/access": {
- "label": "Access (summer)"
+ "label": "Access (summer)",
+ "options": {
+ "entry": "Entry",
+ "exit": "Exit",
+ "both": "Both"
+ }
},
"aeroway": {
"label": "Type"
"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",
"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"
"entrance": {
"label": "Type"
},
+ "except": {
+ "label": "Exceptions"
+ },
"fax": {
"label": "Fax",
"placeholder": "+31 42 123 4567"
"label": "Fee"
},
"fire_hydrant/type": {
- "label": "Type"
+ "label": "Type",
+ "options": {
+ "pillar": "Pillar/Aboveground",
+ "underground": "Underground",
+ "wall": "Wall",
+ "pond": "Pond"
+ }
},
"fixme": {
"label": "Fix Me"
"fuel": {
"label": "Fuel"
},
+ "fuel/biodiesel": {
+ "label": "Sells Biodiesel"
+ },
+ "fuel/diesel": {
+ "label": "Sells Diesel"
+ },
+ "fuel/e10": {
+ "label": "Sells E10"
+ },
+ "fuel/e85": {
+ "label": "Sells E85"
+ },
+ "fuel/lpg": {
+ "label": "Sells Propane"
+ },
+ "fuel/octane_100": {
+ "label": "Sells Racing Gasoline"
+ },
+ "fuel/octane_91": {
+ "label": "Sells Regular Gasoline"
+ },
+ "fuel/octane_95": {
+ "label": "Sells Midgrade Gasoline"
+ },
+ "fuel/octane_98": {
+ "label": "Sells Premium Gasoline"
+ },
"gauge": {
"label": "Gauge"
},
"terminal": "Terminal"
}
},
+ "lamp_type": {
+ "label": "Type"
+ },
"landuse": {
"label": "Type"
},
"leisure": {
"label": "Type"
},
+ "length": {
+ "label": "Length (Meters)"
+ },
"levels": {
"label": "Levels",
"placeholder": "2, 4, 6..."
"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)"
"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"
"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"
},
"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"
},
"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"
"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",
"bridge": "Bridge",
"tunnel": "Tunnel",
"embankment": "Embankment",
- "cutting": "Cutting"
+ "cutting": "Cutting",
+ "ford": "Ford"
}
},
"studio_type": {
"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"
"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"
"wheelchair": {
"label": "Wheelchair Access"
},
+ "width": {
+ "label": "Width (Meters)"
+ },
"wikipedia": {
"label": "Wikipedia"
},
"name": "Boat Rental",
"terms": ""
},
+ "amenity/bus_station": {
+ "name": "Bus Station",
+ "terms": ""
+ },
"amenity/cafe": {
"name": "Cafe",
"terms": "coffee,tea,coffee shop"
"name": "Car Wash",
"terms": ""
},
+ "amenity/charging_station": {
+ "name": "Charging Station",
+ "terms": "EV,Electric Vehicle,Supercharger"
+ },
"amenity/childcare": {
"name": "Childcare",
"terms": "nursery,orphanage,playgroup"
"name": "College",
"terms": ""
},
+ "amenity/compressed_air": {
+ "name": "Compressed Air",
+ "terms": ""
+ },
"amenity/courthouse": {
"name": "Courthouse",
"terms": ""
"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"
"name": "Car Parking",
"terms": ""
},
+ "amenity/parking_entrance": {
+ "name": "Parking Garage Entrance/Exit",
+ "terms": ""
+ },
"amenity/pharmacy": {
"name": "Pharmacy",
"terms": ""
"terms": ""
},
"building/entrance": {
- "name": "Entrance",
+ "name": "Entrance/Exit",
"terms": ""
},
"building/garage": {
"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"
},
"highway/crossing": {
"name": "Crossing",
+ "terms": ""
+ },
+ "highway/crosswalk": {
+ "name": "Crosswalk",
"terms": "crosswalk,zebra crossing"
},
"highway/cycleway": {
"terms": ""
},
"highway/motorway_junction": {
- "name": "Motorway Junction",
+ "name": "Motorway Junction / Exit",
"terms": ""
},
"highway/motorway_link": {
"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": ""
"name": "Cemetery",
"terms": ""
},
+ "landuse/churchyard": {
+ "name": "Churchyard",
+ "terms": ""
+ },
"landuse/commercial": {
"name": "Commercial",
"terms": ""
"name": "Meadow",
"terms": ""
},
+ "landuse/military": {
+ "name": "Military",
+ "terms": ""
+ },
"landuse/orchard": {
"name": "Orchard",
"terms": ""
"terms": ""
},
"leisure/sports_center": {
- "name": "Sports Center",
+ "name": "Sports Center / Gym",
"terms": "gym"
},
"leisure/stadium": {
"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": ""
"name": "Video Store",
"terms": ""
},
+ "shop/wine": {
+ "name": "Wine Shop",
+ "terms": "winery"
+ },
"tourism": {
"name": "Tourism",
"terms": ""
"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": ""
}
}
}
- }
+ },
+ "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