Merge branch 'master' into quick-translate

This commit is contained in:
Bryan Housel
2017-01-17 18:39:49 +05:30
21 changed files with 851 additions and 149 deletions
+5 -2
View File
@@ -31,6 +31,9 @@ export function actionDisconnect(nodeId, newNodeId) {
if (connection.index === 0 && way.isArea()) {
// replace shared node with shared node..
graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
} else if (way.isClosed() && connection.index === way.nodes.length - 1) {
// replace closing node with new new node..
graph = graph.replace(way.unclose().addNode(newNode.id));
} else {
// replace shared node with multiple new nodes..
graph = graph.replace(way.updateNode(newNode.id, connection.index));
@@ -52,11 +55,11 @@ export function actionDisconnect(nodeId, newNodeId) {
return;
}
if (way.isArea() && (way.nodes[0] === nodeId)) {
candidates.push({wayID: way.id, index: 0});
candidates.push({ wayID: way.id, index: 0 });
} else {
way.nodes.forEach(function(waynode, index) {
if (waynode === nodeId) {
candidates.push({wayID: way.id, index: index});
candidates.push({ wayID: way.id, index: index });
}
});
}
+47 -28
View File
@@ -41,20 +41,27 @@ export function behaviorDrawWay(context, wayId, index, mode, baseGraph) {
annotation = t((way.isDegenerate() ?
'operations.start.annotation.' :
'operations.continue.annotation.') + context.geometry(wayId)),
draw = behaviorDraw(context);
draw = behaviorDraw(context),
startIndex, start, end, segment;
var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
start = osmNode({loc: context.graph().entity(way.nodes[startIndex]).loc}),
end = osmNode({loc: context.map().mouseCoordinates()}),
segment = osmWay({
if (!isArea) {
startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0;
start = osmNode({ id: 'nStart', loc: context.entity(way.nodes[startIndex]).loc });
end = osmNode({ id: 'nEnd', loc: context.map().mouseCoordinates() });
segment = osmWay({ id: 'wTemp',
nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
tags: _.clone(way.tags)
});
} else {
end = osmNode({ loc: context.map().mouseCoordinates() });
}
var fn = context[way.isDegenerate() ? 'replace' : 'perform'];
if (isArea) {
fn(actionAddEntity(end),
actionAddVertex(wayId, end.id, index)
actionAddVertex(wayId, end.id)
);
} else {
fn(actionAddEntity(start),
@@ -70,7 +77,7 @@ export function behaviorDrawWay(context, wayId, index, mode, baseGraph) {
if (datum.type === 'node' && datum.id !== end.id) {
loc = datum.loc;
} else if (datum.type === 'way' && datum.id !== segment.id) {
} else if (datum.type === 'way') { // && (segment || datum.id !== segment.id)) {
var dims = context.map().dimensions(),
mouse = context.mouse(),
pad = 5,
@@ -145,9 +152,8 @@ export function behaviorDrawWay(context, wayId, index, mode, baseGraph) {
return function(graph) {
if (isArea) {
return graph
.replace(way.addNode(newNode.id, index))
.replace(way.addNode(newNode.id))
.remove(end);
} else {
return graph
.replace(graph.entity(wayId).addNode(newNode.id, index))
@@ -165,13 +171,19 @@ export function behaviorDrawWay(context, wayId, index, mode, baseGraph) {
var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
var newNode = osmNode({loc: loc});
context.replace(
actionAddEntity(newNode),
ReplaceTemporaryNode(newNode),
annotation
);
if (isArea) {
context.replace(
actionMoveNode(end.id, loc),
annotation
);
} else {
var newNode = osmNode({loc: loc});
context.replace(
actionAddEntity(newNode),
ReplaceTemporaryNode(newNode),
annotation
);
}
finished = true;
context.enter(mode);
@@ -180,21 +192,28 @@ export function behaviorDrawWay(context, wayId, index, mode, baseGraph) {
// Connect the way to an existing way.
drawWay.addWay = function(loc, edge) {
var previousEdge = startIndex ?
[way.nodes[startIndex], way.nodes[startIndex - 1]] :
[way.nodes[0], way.nodes[1]];
// Avoid creating duplicate segments
if (!isArea && geoEdgeEqual(edge, previousEdge))
return;
if (isArea) {
context.perform(
actionAddMidpoint({ loc: loc, edge: edge}, end),
annotation
);
} else {
var previousEdge = startIndex ?
[way.nodes[startIndex], way.nodes[startIndex - 1]] :
[way.nodes[0], way.nodes[1]];
var newNode = osmNode({ loc: loc });
// Avoid creating duplicate segments
if (geoEdgeEqual(edge, previousEdge))
return;
context.perform(
actionAddMidpoint({ loc: loc, edge: edge}, newNode),
ReplaceTemporaryNode(newNode),
annotation
);
var newNode = osmNode({ loc: loc });
context.perform(
actionAddMidpoint({ loc: loc, edge: edge}, newNode),
ReplaceTemporaryNode(newNode),
annotation
);
}
finished = true;
context.enter(mode);
+10 -3
View File
@@ -27,6 +27,13 @@ export function modeAddArea(context) {
defaultTags = { area: 'yes' };
function actionClose(wayId) {
return function (graph) {
return graph.replace(graph.entity(wayId).close());
};
}
function start(loc) {
var graph = context.graph(),
node = osmNode({ loc: loc }),
@@ -36,7 +43,7 @@ export function modeAddArea(context) {
actionAddEntity(node),
actionAddEntity(way),
actionAddVertex(way.id, node.id),
actionAddVertex(way.id, node.id)
actionClose(way.id)
);
context.enter(modeDrawArea(context, way.id, graph));
@@ -52,7 +59,7 @@ export function modeAddArea(context) {
actionAddEntity(node),
actionAddEntity(way),
actionAddVertex(way.id, node.id),
actionAddVertex(way.id, node.id),
actionClose(way.id),
actionAddMidpoint({ loc: loc, edge: edge }, node)
);
@@ -67,7 +74,7 @@ export function modeAddArea(context) {
context.perform(
actionAddEntity(way),
actionAddVertex(way.id, node.id),
actionAddVertex(way.id, node.id)
actionClose(way.id)
);
context.enter(modeDrawArea(context, way.id, graph));
+1 -4
View File
@@ -7,8 +7,6 @@ import {
behaviorSelect
} from '../behavior/index';
import { modeDragNode } from './index';
export function modeBrowse(context) {
var mode = {
@@ -22,8 +20,7 @@ export function modeBrowse(context) {
behaviorPaste(context),
behaviorHover(context).on('hover', context.ui().sidebar.hover),
behaviorSelect(context),
behaviorLasso(context),
modeDragNode(context).behavior
behaviorLasso(context)
];
+9 -3
View File
@@ -79,12 +79,16 @@ export function modeDragNode(context) {
function start(entity) {
activeIDs = _.map(context.graph().parentWays(entity), 'id');
activeIDs.push(entity.id);
wasMidpoint = entity.type === 'midpoint';
isCancelled = d3.event.sourceEvent.shiftKey ||
!(wasMidpoint || _.some(activeIDs, function (activeID) { return selectedIDs.indexOf(activeID) !== -1; })) ||
context.features().hasHiddenConnections(entity, context.graph());
if (isCancelled) return behavior.cancel();
wasMidpoint = entity.type === 'midpoint';
if (wasMidpoint) {
var midpoint = entity;
entity = osmNode();
@@ -93,12 +97,14 @@ export function modeDragNode(context) {
var vertex = context.surface().selectAll('.' + entity.id);
behavior.target(vertex.node(), entity);
activeIDs = _.map(context.graph().parentWays(entity), 'id');
activeIDs.push(entity.id);
} else {
context.perform(actionNoop());
}
activeIDs = _.map(context.graph().parentWays(entity), 'id');
activeIDs.push(entity.id);
setActiveElements();
context.enter(mode);
}
+6 -5
View File
@@ -11,17 +11,18 @@ export function modeDrawArea(context, wayId, baseGraph) {
mode.enter = function() {
var way = context.entity(wayId),
headId = way.nodes[way.nodes.length - 2],
tailId = way.first();
var way = context.entity(wayId);
behavior = behaviorDrawWay(context, wayId, -1, mode, baseGraph)
behavior = behaviorDrawWay(context, wayId, undefined, mode, baseGraph)
.tail(t('modes.draw_area.tail'));
var addNode = behavior.addNode;
behavior.addNode = function(node) {
if (node.id === headId || node.id === tailId) {
var length = way.nodes.length,
penultimate = length > 2 ? way.nodes[length - 2] : null;
if (node.id === way.first() || node.id === penultimate) {
behavior.finish();
} else {
addNode(node);
+1
View File
@@ -15,6 +15,7 @@ export function operationContinue(selectedIDs, context) {
function candidateWays() {
return graph.parentWays(vertex).filter(function(parent) {
return parent.geometry(graph) === 'line' &&
!parent.isClosed() &&
parent.affix(vertex.id) &&
(geometries.line.length === 0 || geometries.line[0] === parent);
});
+149 -18
View File
@@ -122,7 +122,7 @@ _.extend(osmWay.prototype, {
isClosed: function() {
return this.nodes.length > 0 && this.first() === this.last();
return this.nodes.length > 1 && this.first() === this.last();
},
@@ -188,46 +188,169 @@ _.extend(osmWay.prototype, {
},
// If this way is not closed, append the beginning node to the end of the nodelist to close it.
close: function() {
if (this.isClosed() || !this.nodes.length) return this;
var nodes = this.nodes.slice();
nodes = nodes.filter(noRepeatNodes);
nodes.push(nodes[0]);
return this.update({ nodes: nodes });
},
// If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
unclose: function() {
if (!this.isClosed()) return this;
var nodes = this.nodes.slice(),
connector = this.first(),
i = nodes.length - 1;
// remove trailing connectors..
while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
nodes.splice(i, 1);
i = nodes.length - 1;
}
nodes = nodes.filter(noRepeatNodes);
return this.update({ nodes: nodes });
},
// Adds a node (id) in front of the node which is currently at position index.
// If index is undefined, the node will be added to the end of the way for linear ways,
// or just before the final connecting node for circular ways.
// Consecutive duplicates are eliminated including existing ones.
// Circularity is always preserved when adding a node.
addNode: function(id, index) {
var nodes = this.nodes.slice();
nodes.splice(index === undefined ? nodes.length : index, 0, id);
return this.update({nodes: nodes});
var nodes = this.nodes.slice(),
isClosed = this.isClosed(),
max = isClosed ? nodes.length - 1 : nodes.length;
if (index === undefined) {
index = max;
}
if (index < 0 || index > max) {
throw new RangeError('index ' + index + ' out of range 0..' + max);
}
// If this is a closed way, remove all connector nodes except the first one
// (there may be duplicates) and adjust index if necessary..
if (isClosed) {
var connector = this.first();
// leading connectors..
var i = 1;
while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
nodes.splice(i, 1);
if (index > i) index--;
}
// trailing connectors..
i = nodes.length - 1;
while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
nodes.splice(i, 1);
if (index > i) index--;
i = nodes.length - 1;
}
}
nodes.splice(index, 0, id);
nodes = nodes.filter(noRepeatNodes);
// If the way was closed before, append a connector node to keep it closed..
if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
nodes.push(nodes[0]);
}
return this.update({ nodes: nodes });
},
// Replaces the node which is currently at position index with the given node (id).
// Consecutive duplicates are eliminated including existing ones.
// Circularity is preserved when updating a node.
updateNode: function(id, index) {
var nodes = this.nodes.slice();
var nodes = this.nodes.slice(),
isClosed = this.isClosed(),
max = nodes.length - 1;
if (index === undefined || index < 0 || index > max) {
throw new RangeError('index ' + index + ' out of range 0..' + max);
}
// If this is a closed way, remove all connector nodes except the first one
// (there may be duplicates) and adjust index if necessary..
if (isClosed) {
var connector = this.first();
// leading connectors..
var i = 1;
while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
nodes.splice(i, 1);
if (index > i) index--;
}
// trailing connectors..
i = nodes.length - 1;
while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
nodes.splice(i, 1);
if (index === i) index = 0; // update leading connector instead
i = nodes.length - 1;
}
}
nodes.splice(index, 1, id);
nodes = nodes.filter(noRepeatNodes);
// If the way was closed before, append a connector node to keep it closed..
if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
nodes.push(nodes[0]);
}
return this.update({nodes: nodes});
},
// Replaces each occurrence of node id needle with replacement.
// Consecutive duplicates are eliminated including existing ones.
// Circularity is preserved.
replaceNode: function(needle, replacement) {
if (this.nodes.indexOf(needle) < 0)
return this;
var nodes = this.nodes.slice(),
isClosed = this.isClosed();
var nodes = this.nodes.slice();
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] === needle) {
nodes[i] = replacement;
}
}
nodes = nodes.filter(noRepeatNodes);
// If the way was closed before, append a connector node to keep it closed..
if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
nodes.push(nodes[0]);
}
return this.update({nodes: nodes});
},
// Removes each occurrence of node id needle with replacement.
// Consecutive duplicates are eliminated including existing ones.
// Circularity is preserved.
removeNode: function(id) {
var nodes = [];
var nodes = this.nodes.slice(),
isClosed = this.isClosed();
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
if (node !== id && nodes[nodes.length - 1] !== node) {
nodes.push(node);
}
}
nodes = nodes
.filter(function(node) { return node !== id; })
.filter(noRepeatNodes);
// Preserve circularity
if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
// If the way was closed before, append a connector node to keep it closed..
if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
nodes.push(nodes[0]);
}
@@ -248,7 +371,9 @@ _.extend(osmWay.prototype, {
})
}
};
if (changeset_id) r.way['@changeset'] = changeset_id;
if (changeset_id) {
r.way['@changeset'] = changeset_id;
}
return r;
},
@@ -297,3 +422,9 @@ _.extend(osmWay.prototype, {
});
}
});
// Filter function to eliminate consecutive duplicates.
function noRepeatNodes(node, i, arr) {
return i === 0 || node !== arr[i - 1];
}
+10 -11
View File
@@ -58,19 +58,18 @@ export function uiCommit(context) {
context.connection().userChangesets(function (err, changesets) {
if (err) return;
var comments = [];
for (var i = 0; i < changesets.length; i++) {
if (changesets[i].tags.comment) {
comments.push({
title: changesets[i].tags.comment,
value: changesets[i].tags.comment
});
}
}
var comments = changesets.map(function(changeset) {
return {
title: changeset.tags.comment,
value: changeset.tags.comment
};
});
commentField
.call(d3combobox().caseSensitive(true).data(comments));
.call(d3combobox()
.caseSensitive(true)
.data(_.uniqBy(comments, 'title'))
);
});
var clippyArea = commentSection.append('div')
+15 -15
View File
@@ -16,20 +16,11 @@ import { utilGetSetValue } from '../../util/get_set_value';
export function uiFieldAddress(field, context) {
var dispatch = d3.dispatch('init', 'change'),
nominatim = services.nominatim,
nominatim = services.geocoder,
wrap = d3.select(null),
isInitialized = false,
entity;
var widths = {
housenumber: 1/3,
street: 2/3,
city: 2/3,
state: 1/4,
postcode: 1/3
};
function getNearStreets() {
var extent = entity.extent(context.graph()),
l = extent.center(),
@@ -98,7 +89,6 @@ export function uiFieldAddress(field, context) {
}
}
function getNearValues(key) {
var extent = entity.extent(context.graph()),
l = extent.center(),
@@ -130,6 +120,11 @@ export function uiFieldAddress(field, context) {
return a && a.countryCodes && _.includes(a.countryCodes, countryCode);
}) || _.first(dataAddressFormats);
var widths = addressFormat.widths || {
housenumber: 1/3, street: 2/3,
city: 2/3, state: 1/4, postcode: 1/3
};
function row(r) {
// Normalize widths.
var total = _.reduce(r, function(sum, field) {
@@ -154,19 +149,25 @@ export function uiFieldAddress(field, context) {
.enter()
.append('input')
.property('type', 'text')
.attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
.attr('placeholder', function (d) {
var localkey = d.id + '!' + countryCode,
tkey = field.strings.placeholders[localkey] ? localkey : d.id;
return field.t('placeholders.' + tkey);
})
.attr('class', function (d) { return 'addr-' + d.id; })
.style('width', function (d) { return d.width * 100 + '%'; });
// Update
var addrTags = [
// setup dropdowns for common address tags
var dropdowns = addressFormat.dropdowns || [
'city', 'county', 'country', 'district', 'hamlet',
'neighbourhood', 'place', 'postcode', 'province',
'quarter', 'state', 'street', 'subdistrict', 'suburb'
];
// If fields exist for any of these tags, create dropdowns to pick nearby values..
addrTags.forEach(function(tag) {
dropdowns.forEach(function(tag) {
var nearValues = (tag === 'street') ? getNearStreets
: (tag === 'city') ? getNearCities
: getNearValues;
@@ -202,7 +203,6 @@ export function uiFieldAddress(field, context) {
.attr('class', 'preset-input-wrap')
.merge(wrap);
if (nominatim && entity) {
var center = entity.extent(context.graph()).center();
nominatim.countryCode(center, initCallback);
+1 -1
View File
@@ -15,7 +15,7 @@ export {
export function uiFieldCombo(field, context) {
var dispatch = d3.dispatch('change'),
nominatim = services.nominatim,
nominatim = services.geocoder,
taginfo = services.taginfo,
isMulti = (field.type === 'multiCombo'),
isNetwork = (field.type === 'networkCombo'),
+1 -1
View File
@@ -15,7 +15,7 @@ export {
export function uiFieldText(field, context) {
var dispatch = d3.dispatch('change'),
nominatim = services.nominatim,
nominatim = services.geocoder,
input,
entity;
-2
View File
@@ -1,5 +1,4 @@
export { uiInit } from './init';
export { uiFields } from './fields/index';
export { uiAccount } from './account';
export { uiAttribution } from './attribution';
export { uiBackground } from './background';
@@ -18,7 +17,6 @@ export { uiGeolocate } from './geolocate';
export { uiHelp } from './help';
export { uiInfo } from './info';
export { uiInspector } from './inspector';
export { uiIntro } from './intro';
export { uiLasso } from './lasso';
export { uiLoading } from './loading';
export { uiMapData } from './map_data';