in modules/actions/add_member.js [34:195]
function addWayMember(relation, graph) {
var groups, tempWay, item, i, j, k;
// remove PTv2 stops and platforms before doing anything.
var PTv2members = [];
var members = [];
for (i = 0; i < relation.members.length; i++) {
var m = relation.members[i];
if (/stop|platform/.test(m.role)) {
PTv2members.push(m);
} else {
members.push(m);
}
}
relation = relation.update({ members: members });
if (insertPair) {
// We're adding a member that must stay paired with an existing member.
// (This feature is used by `actionSplit`)
//
// This is tricky because the members may exist multiple times in the
// member list, and with different A-B/B-A ordering and different roles.
// (e.g. a bus route that loops out and back - #4589).
//
// Replace the existing member with a temporary way,
// so that `osmJoinWays` can treat the pair like a single way.
tempWay = osmWay({ id: 'wTemp', nodes: insertPair.nodes });
graph = graph.replace(tempWay);
var tempMember = { id: tempWay.id, type: 'way', role: member.role };
var tempRelation = relation.replaceMember({id: insertPair.originalID}, tempMember, true);
groups = utilArrayGroupBy(tempRelation.members, 'type');
groups.way = groups.way || [];
} else {
// Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
groups = utilArrayGroupBy(relation.members, 'type');
groups.way = groups.way || [];
groups.way.push(member);
}
members = withIndex(groups.way);
var joined = osmJoinWays(members, graph);
// `joined` might not contain all of the way members,
// But will contain only the completed (downloaded) members
for (i = 0; i < joined.length; i++) {
var segment = joined[i];
var nodes = segment.nodes.slice();
var startIndex = segment[0].index;
// j = array index in `members` where this segment starts
for (j = 0; j < members.length; j++) {
if (members[j].index === startIndex) {
break;
}
}
// k = each member in segment
for (k = 0; k < segment.length; k++) {
item = segment[k];
var way = graph.entity(item.id);
// If this is a paired item, generate members in correct order and role
if (tempWay && item.id === tempWay.id) {
if (nodes[0].id === insertPair.nodes[0]) {
item.pair = [
{ id: insertPair.originalID, type: 'way', role: item.role },
{ id: insertPair.insertedID, type: 'way', role: item.role }
];
} else {
item.pair = [
{ id: insertPair.insertedID, type: 'way', role: item.role },
{ id: insertPair.originalID, type: 'way', role: item.role }
];
}
}
// reorder `members` if necessary
if (k > 0) {
if (j+k >= members.length || item.index !== members[j+k].index) {
moveMember(members, item.index, j+k);
}
}
nodes.splice(0, way.nodes.length - 1);
}
}
if (tempWay) {
graph = graph.remove(tempWay);
}
// Final pass: skip dead items, split pairs, remove index properties
var wayMembers = [];
for (i = 0; i < members.length; i++) {
item = members[i];
if (item.index === -1) continue;
if (item.pair) {
wayMembers.push(item.pair[0]);
wayMembers.push(item.pair[1]);
} else {
wayMembers.push(utilObjectOmit(item, ['index']));
}
}
// Put stops and platforms first, then nodes, ways, relations
// This is recommended for Public Transport v2 routes:
// see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
var newMembers = PTv2members.concat( (groups.node || []), wayMembers, (groups.relation || []) );
return graph.replace(relation.update({ members: newMembers }));
// `moveMember()` changes the `members` array in place by splicing
// the item with `.index = findIndex` to where it belongs,
// and marking the old position as "dead" with `.index = -1`
//
// j=5, k=0 jk
// segment 5 4 7 6
// members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k
//
// j=5, k=1 j k
// segment 5 4 7 6
// members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k
// members 0 1 2 3 x 5 4 6 7 8 9 moved
//
// j=5, k=2 j k
// segment 5 4 7 6
// members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k
// members 0 1 2 3 x 5 4 7 6 x 8 9 moved
//
// j=5, k=3 j k
// segment 5 4 7 6
// members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k
//
function moveMember(arr, findIndex, toIndex) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].index === findIndex) {
break;
}
}
var item = Object.assign({}, arr[i]); // shallow copy
arr[i].index = -1; // mark as dead
item.index = toIndex;
arr.splice(toIndex, 0, item);
}
// This is the same as `Relation.indexedMembers`,
// Except we don't want to index all the members, only the ways
function withIndex(arr) {
var result = new Array(arr.length);
for (var i = 0; i < arr.length; i++) {
result[i] = Object.assign({}, arr[i]); // shallow copy
result[i].index = i;
}
return result;
}
}