mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 01:02:58 +00:00
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
153 lines
5.5 KiB
JavaScript
153 lines
5.5 KiB
JavaScript
import { osmJoinWays } from '../osm/multipolygon';
|
|
import { utilArrayGroupBy, utilObjectOmit } from '../util';
|
|
|
|
|
|
export function actionAddMember(relationId, member, memberIndex) {
|
|
|
|
return function action(graph) {
|
|
var relation = graph.entity(relationId);
|
|
|
|
// There are some special rules for Public Transport v2 routes.
|
|
var isPTv2 = /stop|platform/.test(member.role);
|
|
|
|
if (member.type === 'way' && !isPTv2) {
|
|
// Try to perform sensible inserts based on how the ways join together
|
|
graph = addWayMember(relation, graph);
|
|
} else {
|
|
// see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
|
|
// Stops and Platforms for PTv2 should be ordered first.
|
|
// hack: We do not currently have the ability to place them in the exactly correct order.
|
|
if (isPTv2 && isNaN(memberIndex)) {
|
|
memberIndex = 0;
|
|
}
|
|
|
|
graph = graph.replace(relation.addMember(member, memberIndex));
|
|
}
|
|
|
|
return graph;
|
|
};
|
|
|
|
|
|
// Add a way member into the relation "wherever it makes sense".
|
|
function addWayMember(relation, graph) {
|
|
var groups, 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 });
|
|
|
|
// 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);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// Final pass: skip dead items, remove index properties
|
|
var wayMembers = [];
|
|
for (i = 0; i < members.length; i++) {
|
|
item = members[i];
|
|
if (item.index === -1) continue;
|
|
|
|
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) {
|
|
var i;
|
|
for (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 previous entry as dead
|
|
delete item.index; // inserted items must never be moved again
|
|
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;
|
|
}
|
|
}
|
|
|
|
}
|