fix splitting of (route) relation member ways

instead of fully re-sorting the whole relation every time a member is split, perform a local operation: This works under the assumption that the relation is already sorted properly. The new way is inserted into the relation before or after the existing member, depending on how the old/new way connect to their neighboring members.

for cases where two ways form a loop, a additional look-ahead is implemented to disambiguate the order
This commit is contained in:
Martin Raifer
2024-03-02 12:16:57 +01:00
parent cc920d3fba
commit 11dfbe804c
5 changed files with 261 additions and 224 deletions
-116
View File
@@ -119,122 +119,6 @@ describe('iD.actionAddMember', function() {
expect(members(graph)).to.eql(['-', '=', '~']);
});
it('inserts the member multiple times if insertPair provided (middle)', function() {
// Before: a ---> b .. c ~~~> d <~~~ c .. b <--- a
// After: a ---> b ===> c ~~~> d <~~~ c <=== b <--- a
var graph = iD.coreGraph([
iD.osmNode({id: 'a', loc: [0, 0]}),
iD.osmNode({id: 'b', loc: [0, 0]}),
iD.osmNode({id: 'c', loc: [0, 0]}),
iD.osmNode({id: 'd', loc: [0, 0]}),
iD.osmWay({id: '-', nodes: ['a', 'b']}),
iD.osmWay({id: '=', nodes: ['b', 'c']}),
iD.osmWay({id: '~', nodes: ['c', 'd']}),
iD.osmRelation({id: 'r', members: [
{id: '-', type: 'way'},
{id: '~', type: 'way'},
{id: '~', type: 'way'},
{id: '-', type: 'way'}
]})
]);
var member = { id: '=', type: 'way' };
var insertPair = {
originalID: '-',
insertedID: '=',
nodes: ['a','b','c']
};
graph = iD.actionAddMember('r', member, undefined, insertPair)(graph);
expect(members(graph)).to.eql(['-', '=', '~', '~', '=', '-']);
});
it('inserts the member multiple times if insertPair provided (middle) (reversed pair)', function() {
// Before: a .. b ===> c ~~~> d <~~~ c <=== b .. a
// After: a ---> b ===> c ~~~> d <~~~ c <=== b <--- a
var graph = iD.coreGraph([
iD.osmNode({id: 'a', loc: [0, 0]}),
iD.osmNode({id: 'b', loc: [0, 0]}),
iD.osmNode({id: 'c', loc: [0, 0]}),
iD.osmNode({id: 'd', loc: [0, 0]}),
iD.osmWay({id: '-', nodes: ['a', 'b']}),
iD.osmWay({id: '=', nodes: ['b', 'c']}),
iD.osmWay({id: '~', nodes: ['c', 'd']}),
iD.osmRelation({id: 'r', members: [
{id: '=', type: 'way'},
{id: '~', type: 'way'},
{id: '~', type: 'way'},
{id: '=', type: 'way'}
]})
]);
var member = { id: '=', type: 'way' };
var insertPair = {
originalID: '=',
insertedID: '-',
nodes: ['a','b','c']
};
graph = iD.actionAddMember('r', member, undefined, insertPair)(graph);
expect(members(graph)).to.eql(['-', '=', '~', '~', '=', '-']);
});
it('inserts the member multiple times if insertPair provided (beginning/end)', function() {
// Before: b <=== c ~~~> d <~~~ c ===> b
// After: a <--- b <=== c ~~~> d <~~~ c ===> b ---> a
var graph = iD.coreGraph([
iD.osmNode({id: 'a', loc: [0, 0]}),
iD.osmNode({id: 'b', loc: [0, 0]}),
iD.osmNode({id: 'c', loc: [0, 0]}),
iD.osmNode({id: 'd', loc: [0, 0]}),
iD.osmWay({id: '-', nodes: ['b', 'a']}),
iD.osmWay({id: '=', nodes: ['c', 'b']}),
iD.osmWay({id: '~', nodes: ['c', 'd']}),
iD.osmRelation({id: 'r', members: [
{id: '=', type: 'way'},
{id: '~', type: 'way'},
{id: '~', type: 'way'},
{id: '=', type: 'way'}
]})
]);
var member = { id: '-', type: 'way' };
var insertPair = {
originalID: '=',
insertedID: '-',
nodes: ['c','b','a']
};
graph = iD.actionAddMember('r', member, undefined, insertPair)(graph);
expect(members(graph)).to.eql(['-', '=', '~', '~', '=', '-']);
});
it('inserts the member multiple times if insertPair provided (beginning/end) (reversed pair)', function() {
// Before: a <--- b .. c ~~~> d <~~~ c .. b ---> a
// After: a <--- b <=== c ~~~> d <~~~ c ===> b ---> a
var graph = iD.coreGraph([
iD.osmNode({id: 'a', loc: [0, 0]}),
iD.osmNode({id: 'b', loc: [0, 0]}),
iD.osmNode({id: 'c', loc: [0, 0]}),
iD.osmNode({id: 'd', loc: [0, 0]}),
iD.osmWay({id: '-', nodes: ['b', 'a']}),
iD.osmWay({id: '=', nodes: ['c', 'b']}),
iD.osmWay({id: '~', nodes: ['c', 'd']}),
iD.osmRelation({id: 'r', members: [
{id: '-', type: 'way'},
{id: '~', type: 'way'},
{id: '~', type: 'way'},
{id: '-', type: 'way'}
]})
]);
var member = { id: '-', type: 'way' };
var insertPair = {
originalID: '-',
insertedID: '=',
nodes: ['c','b','a']
};
graph = iD.actionAddMember('r', member, undefined, insertPair)(graph);
expect(members(graph)).to.eql(['-', '=', '~', '~', '=', '-']);
});
it('keeps stops and platforms ordered before node, way, relation (for PTv2 routes)', function() {
var graph = iD.coreGraph([
iD.osmNode({id: 'a', loc: [0, 0]}),
+73 -14
View File
@@ -425,15 +425,14 @@ describe('iD.actionSplit', function () {
}
it('handles incomplete relations', function () {
it('disables split action on too incomplete relations', function () {
//
// Situation:
// a ---> b ---> c split at 'b'
// Relation: ['~', '-']
// Relation: ['?', '-'] member '?' missing
//
// Expected result:
// a ---> b ===> c
// Relation: ['~', '-', '=']
// forbidden, because correct order of -/= cannot be determined
//
var graph = iD.coreGraph([
iD.osmNode({ id: 'a', loc: [0, 0] }),
@@ -441,13 +440,73 @@ describe('iD.actionSplit', function () {
iD.osmNode({ id: 'c', loc: [2, 0] }),
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c'] }),
iD.osmRelation({id: 'r', members: [
{ id: '~', type: 'way' },
{ id: '?', type: 'way' },
{ id: '-', type: 'way' }
]})
]);
graph = iD.actionSplit('b', ['='])(graph);
expect(members(graph)).to.eql(['~', '-', '=']);
var action = iD.actionSplit('b', ['=']);
expect(action.disabled(graph)).to.be.ok;
});
it('enables split action on partially incomplete, but still sufficiently complete relations (before split)', function () {
//
// Situation:
// a ~~~> b ---> c ---> d split at 'c'
// Relation: ['~', '-', '?'] member '?' missing
//
// Expected result:
// a ~~~> b ---> c ===> d
// Relation: ['~', '-', '=', '?']
//
var graph = iD.coreGraph([
iD.osmNode({ id: 'a', loc: [0, 0] }),
iD.osmNode({ id: 'b', loc: [1, 0] }),
iD.osmNode({ id: 'c', loc: [2, 0] }),
iD.osmNode({ id: 'd', loc: [3, 0] }),
iD.osmWay({ id: '~', nodes: ['a', 'b'] }),
iD.osmWay({ id: '-', nodes: ['b', 'c', 'd'] }),
iD.osmRelation({id: 'r', members: [
{ id: '~', type: 'way' },
{ id: '-', type: 'way' },
{ id: '?', type: 'way' }
]})
]);
var action = iD.actionSplit('c', ['=']);
expect(action.disabled(graph)).to.be.not.ok;
graph = action(graph);
expect(members(graph)).to.eql(['~', '-', '=', '?']);
});
it('enables split action on partially incomplete, but still sufficiently complete relations (after split)', function () {
//
// Situation:
// a ---> b ---> c ~~~> d split at 'b'
// Relation: ['?', '-', '~'] member '?' missing
//
// Expected result:
// a ---> b ===> c ~~~> d
// Relation: ['?', '-', '=', '~']
//
var graph = iD.coreGraph([
iD.osmNode({ id: 'a', loc: [0, 0] }),
iD.osmNode({ id: 'b', loc: [1, 0] }),
iD.osmNode({ id: 'c', loc: [2, 0] }),
iD.osmNode({ id: 'd', loc: [3, 0] }),
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c'] }),
iD.osmWay({ id: '~', nodes: ['c', 'd'] }),
iD.osmRelation({id: 'r', members: [
{ id: '?', type: 'way' },
{ id: '-', type: 'way' },
{ id: '~', type: 'way' }
]})
]);
var action = iD.actionSplit('b', ['=']);
expect(action.disabled(graph)).to.be.not.ok;
graph = action(graph);
expect(members(graph)).to.eql(['?', '-', '=', '~']);
});
@@ -593,28 +652,28 @@ describe('iD.actionSplit', function () {
]);
});
it('reorders members as node, way, relation (for Public Transport routing)', function () {
it('preserves other members (example: Public Transport routing)', function () {
var graph = iD.coreGraph([
iD.osmNode({ id: 'a', loc: [0, 0] }),
iD.osmNode({ id: 'b', loc: [1, 0] }),
iD.osmNode({ id: 'c', loc: [2, 0] }),
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c'] }),
iD.osmRelation({id: 'r', members: [
{ id: 'n1', type: 'node', role: 'forward' },
{ id: 'n1', type: 'node', role: 'stop' },
{ id: '-', type: 'way', role: 'forward' },
{ id: 'r1', type: 'relation', role: 'forward' },
{ id: 'n2', type: 'node', role: 'forward' }
{ id: 'r1', type: 'relation', role: '' },
{ id: 'n2', type: 'node', role: 'stop' }
]})
]);
graph = iD.actionSplit('b', ['='])(graph);
expect(graph.entity('r').members).to.eql([
{ id: 'n1', type: 'node', role: 'forward' },
{ id: 'n2', type: 'node', role: 'forward' },
{ id: 'n1', type: 'node', role: 'stop' },
{ id: '-', type: 'way', role: 'forward' },
{ id: '=', type: 'way', role: 'forward' },
{ id: 'r1', type: 'relation', role: 'forward'}
{ id: 'r1', type: 'relation', role: ''},
{ id: 'n2', type: 'node', role: 'stop' }
]);
});
});