mirror of
https://github.com/FoggedLens/iD.git
synced 2026-03-30 08:50:32 +02:00
Reverse directional tags and roles when reversing a way
Reverse known direction dependent tags other than `oneway`.
We assume that correcting a backwards oneway is the primary
reason for reversing a way.
The following transforms are performed:
Keys:
*:right=* ⟺ *:left=*
*:forward=* ⟺ *:backward=*
direction=up ⟺ direction=down
incline=up ⟺ incline=down
*=right ⟺ *=left
Relation members:
role=forward ⟺ role=backward
In addition, numeric-valued `incline` tags are negated.
The JOSM implementation was used as a guide, but transformations that
were of unclear benefit or adjusted tags that don't seem to be used
in practice were omitted.
References:
http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
http://wiki.openstreetmap.org/wiki/Key:direction#Steps
http://wiki.openstreetmap.org/wiki/Key:incline
http://wiki.openstreetmap.org/wiki/Route#Members
http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
Fixes #299.
This commit is contained in:
@@ -62,6 +62,7 @@
|
||||
<script src='js/id/actions/remove_way_node.js'></script>
|
||||
<script src='js/id/actions/reverse_way.js'></script>
|
||||
<script src='js/id/actions/split_way.js'></script>
|
||||
<script src='js/id/actions/update_relation_member.js'></script>
|
||||
|
||||
<script src='js/id/behavior.js'></script>
|
||||
<script src='js/id/behavior/drag.js'></script>
|
||||
|
||||
@@ -1,8 +1,75 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
|
||||
/*
|
||||
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
|
||||
reason for reversing a way.)
|
||||
|
||||
The following transforms are performed:
|
||||
|
||||
Keys:
|
||||
*:right=* ⟺ *:left=*
|
||||
*:forward=* ⟺ *:backward=*
|
||||
direction=up ⟺ direction=down
|
||||
incline=up ⟺ incline=down
|
||||
*=right ⟺ *=left
|
||||
|
||||
Relation members:
|
||||
role=forward ⟺ role=backward
|
||||
|
||||
In addition, numeric-valued `incline` tags are negated.
|
||||
|
||||
The JOSM implementation was used as a guide, but transformations that were of unclear benefit
|
||||
or adjusted tags that don't seem to be used in practice were omitted.
|
||||
|
||||
References:
|
||||
http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
|
||||
http://wiki.openstreetmap.org/wiki/Key:direction#Steps
|
||||
http://wiki.openstreetmap.org/wiki/Key:incline
|
||||
http://wiki.openstreetmap.org/wiki/Route#Members
|
||||
http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
|
||||
*/
|
||||
iD.actions.ReverseWay = function(wayId) {
|
||||
var replacements = [
|
||||
[/:right$/, ':left'], [/:left$/, ':right'],
|
||||
[/:forward$/, ':backward'], [/:backward$/, ':forward']
|
||||
], numeric = /^([+-]?)(?=[\d.])/;
|
||||
|
||||
function reverseKey(key) {
|
||||
for (var i = 0; i < replacements.length; ++i) {
|
||||
var replacement = replacements[i];
|
||||
if (replacement[0].test(key)) {
|
||||
return key.replace(replacement[0], replacement[1]);
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
function reverseValue(key, value) {
|
||||
if (key === "incline" && numeric.test(value)) {
|
||||
return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
|
||||
} else if (key === "incline" || key === "direction") {
|
||||
return {up: 'down', down: 'up'}[value] || value;
|
||||
} else {
|
||||
return {left: 'right', right: 'left'}[value] || value;
|
||||
}
|
||||
}
|
||||
|
||||
return function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
nodes = way.nodes.slice().reverse();
|
||||
return graph.replace(way.update({nodes: nodes}));
|
||||
nodes = way.nodes.slice().reverse(),
|
||||
tags = {}, key, role;
|
||||
|
||||
for (key in way.tags) {
|
||||
tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
|
||||
}
|
||||
|
||||
graph.parentRelations(way.id).forEach(function (relation) {
|
||||
relation.members.forEach(function (member, index) {
|
||||
if (member.id === way.id && (role = {forward: 'backward', backward: 'forward'}[member.role])) {
|
||||
graph = iD.actions.UpdateRelationMember(relation.id, index, {role: role})(graph);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return graph.replace(way.update({nodes: nodes, tags: tags}));
|
||||
};
|
||||
};
|
||||
|
||||
9
js/id/actions/update_relation_member.js
Normal file
9
js/id/actions/update_relation_member.js
Normal file
@@ -0,0 +1,9 @@
|
||||
iD.actions.UpdateRelationMember = function(relationId, index, properties) {
|
||||
return function(graph) {
|
||||
var relation = graph.entity(relationId),
|
||||
members = relation.members.slice();
|
||||
|
||||
members.splice(index, 1, _.extend({}, members[index], properties));
|
||||
return graph.replace(relation.update({members: members}));
|
||||
};
|
||||
};
|
||||
@@ -60,6 +60,7 @@
|
||||
<script src='../js/id/actions/remove_relation_member.js'></script>
|
||||
<script src='../js/id/actions/remove_way_node.js'></script>
|
||||
<script src='../js/id/actions/reverse_way.js'></script>
|
||||
<script src='../js/id/actions/update_relation_member.js'></script>
|
||||
|
||||
<script src='../js/id/behavior.js'></script>
|
||||
<script src='../js/id/behavior/drag.js'></script>
|
||||
@@ -111,6 +112,7 @@
|
||||
<script src="spec/actions/remove_way_node.js"></script>
|
||||
<script src="spec/actions/remove_relation_member.js"></script>
|
||||
<script src="spec/actions/reverse_way.js"></script>
|
||||
<script src="spec/actions/update_relation_member.js"></script>
|
||||
|
||||
<script src="spec/behavior/hover.js"></script>
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<script src="spec/actions/remove_way_node.js"></script>
|
||||
<script src="spec/actions/remove_relation_member.js"></script>
|
||||
<script src="spec/actions/reverse_way.js"></script>
|
||||
<script src="spec/actions/update_relation_member.js"></script>
|
||||
|
||||
<script src="spec/behavior/hover.js"></script>
|
||||
|
||||
|
||||
@@ -6,4 +6,112 @@ describe("iD.actions.ReverseWay", function () {
|
||||
graph = iD.actions.ReverseWay(way.id)(iD.Graph([node1, node2, way]));
|
||||
expect(graph.entity(way.id).nodes).to.eql([node2.id, node1.id]);
|
||||
});
|
||||
|
||||
it("preserves non-directional tags", function () {
|
||||
var way = iD.Way({tags: {'highway': 'residential'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'highway': 'residential'});
|
||||
});
|
||||
|
||||
it("preserves oneway tags", function () {
|
||||
var way = iD.Way({tags: {'oneway': 'yes'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'oneway': 'yes'});
|
||||
});
|
||||
|
||||
it("transforms *:right=* ⟺ *:left=*", function () {
|
||||
var way = iD.Way({tags: {'cycleway:right': 'lane'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'cycleway:left': 'lane'});
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'cycleway:right': 'lane'});
|
||||
});
|
||||
|
||||
it("transforms *:forward=* ⟺ *:backward=*", function () {
|
||||
var way = iD.Way({tags: {'maxspeed:forward': '25'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'maxspeed:backward': '25'});
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'maxspeed:forward': '25'});
|
||||
});
|
||||
|
||||
it("transforms direction=up ⟺ direction=down", function () {
|
||||
var way = iD.Way({tags: {'incline': 'up'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': 'down'});
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': 'up'});
|
||||
});
|
||||
|
||||
it("transforms incline=up ⟺ incline=down", function () {
|
||||
var way = iD.Way({tags: {'incline': 'up'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': 'down'});
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': 'up'});
|
||||
});
|
||||
|
||||
it("negates numeric-valued incline tags", function () {
|
||||
var way = iD.Way({tags: {'incline': '5%'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': '-5%'});
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': '5%'});
|
||||
|
||||
way = iD.Way({tags: {'incline': '.8°'}});
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'incline': '-.8°'});
|
||||
});
|
||||
|
||||
it("transforms *=right ⟺ *=left", function () {
|
||||
var way = iD.Way({tags: {'sidewalk': 'right'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'sidewalk': 'left'});
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'sidewalk': 'right'});
|
||||
});
|
||||
|
||||
it("transforms multiple directional tags", function () {
|
||||
var way = iD.Way({tags: {'maxspeed:forward': '25', 'maxspeed:backward': '30'}}),
|
||||
graph = iD.Graph([way]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(way.id).tags).to.eql({'maxspeed:backward': '25', 'maxspeed:forward': '30'});
|
||||
});
|
||||
|
||||
it("transforms role=forward ⟺ role=backward in member relations", function () {
|
||||
var way = iD.Way({tags: {highway: 'residential'}}),
|
||||
relation = iD.Relation({members: [{type: 'way', id: way.id, role: 'forward'}]}),
|
||||
graph = iD.Graph([way, relation]);
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(relation.id).members[0].role).to.eql('backward');
|
||||
|
||||
graph = iD.actions.ReverseWay(way.id)(graph);
|
||||
expect(graph.entity(relation.id).members[0].role).to.eql('forward');
|
||||
});
|
||||
});
|
||||
|
||||
8
test/spec/actions/update_relation_member.js
Normal file
8
test/spec/actions/update_relation_member.js
Normal file
@@ -0,0 +1,8 @@
|
||||
describe("iD.actions.UpdateRelationMember", function () {
|
||||
it("updates the properties of the relation member at the specified index", function () {
|
||||
var node = iD.Node(),
|
||||
relation = iD.Relation({members: [{id: node.id, role: 'forward'}]}),
|
||||
graph = iD.actions.UpdateRelationMember(relation.id, 0, {role: 'backward'})(iD.Graph([node, relation]));
|
||||
expect(graph.entity(relation.id).members).to.eql([{id: node.id, role: 'backward'}]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user