/**
* @license
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
- * 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" 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
*/
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.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),
return d3.rebind(context, dispatch, 'on');
};
-iD.version = '1.4.0';
+iD.version = '1.5.2';
(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/',
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) {
};
};
+// 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))
};
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) {
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);
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() {
- var entity = context.entity(entityId);
return selectedIDs.length === 1 &&
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() {
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';
'roundabout': true
},
'man_made': {
- 'piste:halfpipe': true,
- 'pipeline': true
+ 'piste:halfpipe': true
},
'piste:type': {
'downhill': true,
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() {
// explicit oneway tag..
if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
.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});
}
});
.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]]),
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),
fill: areas
};
- var paths = surface.selectAll('.layer-shadow, .layer-stroke, .layer-fill')
- .selectAll('path.area')
+ 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);
};
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']);
- var segments = _(lines)
- .filter(function(d) { return d.isOneWay(); })
- .map(iD.svg.OneWaySegments(projection, graph, 35))
- .flatten()
- .valueOf();
+ linegroup.enter()
+ .append('g')
+ .attr('class', function(d) { return 'layer linegroup line-' + d; });
- var oneways = surface.select('.layer-oneway')
- .selectAll('path.oneway')
+
+ var lines = linegroup
+ .selectAll('path')
.filter(filter)
- .data(segments, function(d) { return [d.id, d.index]; });
+ .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 onewaygroup = layergroup
+ .selectAll('g.onewaygroup')
+ .data(['oneway']);
+
+ onewaygroup.enter()
+ .append('g')
+ .attr('class', 'layer onewaygroup');
+
+
+ var oneways = onewaygroup
+ .selectAll('path')
+ .filter(filter)
+ .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 (midpoints[id]) {
midpoints[id].parents.push(entity);
} else {
- 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],
- parents: [entity]
- };
+ 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]
+ };
+ }
}
}
}
iD.svg.Surface = function() {
return function (selection) {
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; });
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();
.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');
- about.append('div')
- .attr('class', 'api-status')
- .call(iD.ui.Status(context));
+ footer.append('div')
+ .attr('id', 'scale-block')
+ .call(iD.ui.Scale(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);
};
};
});
}
- 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
});
}
function show(field) {
field.show = true;
- context.presets()(selection);
+ presets(selection);
field.input.focus();
}
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) {
});
};
};
+iD.ui.Scale = function(context) {
+ var projection = context.projection,
+ imperial = (iD.detect().locale === '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) {
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());
+ }));
- street
- .call(d3.combobox()
- .fetcher(function(value, callback) {
- callback(getStreets());
- }));
+ wrap.selectAll('.addr-city')
+ .call(d3.combobox()
+ .fetcher(function(value, callback) {
+ callback(getCities());
+ }));
- city
- .call(d3.combobox()
- .fetcher(function(value, callback) {
- callback(getCities());
- }));
+ wrap.selectAll('.addr-postcode')
+ .call(d3.combobox()
+ .fetcher(function(value, callback) {
+ callback(getPostCodes());
+ }));
- postcode
- .call(d3.combobox()
- .fetcher(function(value, callback) {
- callback(getPostCodes());
- }));
+ wrap.selectAll('input')
+ .on('blur', change)
+ .on('change', change);
- wrap.selectAll('input')
- .on('blur', change)
- .on('change', change);
+ 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');
if (options) {
for (var k in options) {
values.push(k === 'undefined' ? undefined : k);
- texts.push(field.t('check.' + k, { 'default': options[k] }));
+ texts.push(field.t('options.' + k, { 'default': options[k] }));
}
} else {
values = [undefined, 'yes'];
if (field.id === 'oneway') {
for (var key in entity.tags) {
if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
- texts.shift();
- texts.unshift(t('presets.fields.oneway_yes.check.undefined', { 'default': 'Assumed to be Yes' }));
+ texts[0] = t('presets.fields.oneway_yes.options.undefined');
break;
}
}
return o;
}));
- input.attr('placeholder', function() {
- if (opts.length < 3) return '';
- return opts.slice(0, 3).join(', ') + '...';
- });
+ input.attr('placeholder', field.placeholder() ||
+ (opts.length < 3 ? '' : opts.slice(0, 3).join(', ') + '...'));
}
}
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
});
}
"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
"toilets/disposal",
"operator",
"building_area",
- "fee",
- "access_simple"
+ "access_toilets"
],
"geometry": [
"point",
"address",
"operator",
"opening_hours"
- ]
+ ],
+ "searchable": false
},
"craft/tiler": {
"name": "Tiler",
],
"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": [
},
"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": [
}
},
"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",
"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"
}
}
},
}
}
},
+ "lamp_type": {
+ "key": "lamp_type",
+ "type": "combo",
+ "label": "Type"
+ },
"landuse": {
"key": "landuse",
"type": "typeCombo",
"oneway_yes": {
"key": "oneway",
"type": "check",
- "default": "yes",
"label": "One Way",
"strings": {
"options": {
"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",
"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": {
}
},
"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": {
"terminal": "Terminal"
}
},
+ "lamp_type": {
+ "label": "Type"
+ },
"landuse": {
"label": "Type"
},
"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": "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": ""
}
}
}
- }
+ },
+ "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