From 3205650798910ab77d3f89118fbcaefb1b4579b8 Mon Sep 17 00:00:00 2001 From: mstn Date: Thu, 2 Mar 2017 09:17:08 +0100 Subject: [PATCH 1/2] order to-be-created relations by dependencies in a changeset --- modules/services/osm.js | 48 ++++++++++++++++++++++++++++++++++++++- test/spec/services/osm.js | 29 +++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index 1aab53baa4..49ce10bca2 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -314,6 +314,52 @@ export default { }); return ordered; } + // sort relations in a changeset by dependencies + function sort(changes) { + // find a referenced relation in the current changeset + function resolve(item){ + return _.find(relations, function(relation) { + return item.keyAttributes.ref === relation['@id']; + }); + } + // a new item is an item that has not been already processed + function isNew(item) { + return !sorted[ item['@id'] ] && !_.find(processing, function(proc){ + return proc['@id'] === item['@id']; + }); + } + var processing = [], + sorted = {}, + relations = changes.relation; + + if (!relations) return changes; + + for (var i = 0; i < relations.length; i++) { + + var relation = relations[i]; + + // skip relation if already sorted + if ( !sorted[relation['@id']] ) { + processing.push( relation ); + } + + while ( processing.length > 0 ){ + var next = processing[0], + deps = _.filter( _.compact(next.member.map(resolve)), isNew); + + if ( deps.length === 0 ){ + sorted[ next['@id'] ] = next; + processing.shift(); + } else { + processing = deps.concat( processing ); + } + } + + } + + changes.relation = _.values(sorted); + return changes; + } function rep(entity) { return entity.asJXON(changeset_id); @@ -323,7 +369,7 @@ export default { osmChange: { '@version': 0.6, '@generator': 'iD', - 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']), + 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])), 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']), 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true}) } diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js index b75c233784..000127b629 100644 --- a/test/spec/services/osm.js +++ b/test/spec/services/osm.js @@ -362,6 +362,35 @@ describe('iD.serviceOsm', function () { ]); }); + it('includes creations ordered by dependencies', function() { + var n = iD.Node({loc: [0, 0]}), + w = iD.Way({nodes: [n.id]}), + r1 = iD.Relation({members: [{id: w.id}]}), + r2 = iD.Relation({members: [{id: r1.id}]}), + changes = {created: [r2, r1, w, n], modified: [], deleted: []}, + jxon = connection.osmChangeJXON('1234', changes); + + expect(d3.entries(jxon.osmChange.create)).to.eql([ + {key: 'node', value: [n.asJXON('1234').node]}, + {key: 'way', value: [w.asJXON('1234').way]}, + {key: 'relation', value: [r1.asJXON('1234').relation, r2.asJXON('1234').relation]}, + ]); + }); + + it('includes creations ignoring circular dependencies', function() { + var r1 = iD.Relation(), + r2 = iD.Relation(), + changes, jxon; + r1.addMember({id: r2.id}); + r2.addMember({id: r1.id}); + changes = {created: [r1,r2], modified: [], deleted: []}; + jxon = connection.osmChangeJXON('1234', changes); + + expect(d3.entries(jxon.osmChange.create)).to.eql([ + {key: 'relation', value: [r1.asJXON('1234').relation, r2.asJXON('1234').relation]}, + ]); + }); + it('includes modifications', function() { var n = iD.Node({loc: [0, 0]}), w = iD.Way(), From 05e5cf9030a253c3ed4a07d8751d7078de10b375 Mon Sep 17 00:00:00 2001 From: mstn Date: Thu, 2 Mar 2017 11:26:14 +0100 Subject: [PATCH 2/2] add type information to references --- modules/services/osm.js | 3 ++- test/spec/services/osm.js | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index 49ce10bca2..c52f7c0ad1 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -319,7 +319,8 @@ export default { // find a referenced relation in the current changeset function resolve(item){ return _.find(relations, function(relation) { - return item.keyAttributes.ref === relation['@id']; + return item.keyAttributes.type === 'relation' + && item.keyAttributes.ref === relation['@id']; }); } // a new item is an item that has not been already processed diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js index 000127b629..e4bc236034 100644 --- a/test/spec/services/osm.js +++ b/test/spec/services/osm.js @@ -365,8 +365,8 @@ describe('iD.serviceOsm', function () { it('includes creations ordered by dependencies', function() { var n = iD.Node({loc: [0, 0]}), w = iD.Way({nodes: [n.id]}), - r1 = iD.Relation({members: [{id: w.id}]}), - r2 = iD.Relation({members: [{id: r1.id}]}), + r1 = iD.Relation({members: [{id: w.id, type: 'way'}]}), + r2 = iD.Relation({members: [{id: r1.id, type: 'relation'}]}), changes = {created: [r2, r1, w, n], modified: [], deleted: []}, jxon = connection.osmChangeJXON('1234', changes); @@ -381,8 +381,8 @@ describe('iD.serviceOsm', function () { var r1 = iD.Relation(), r2 = iD.Relation(), changes, jxon; - r1.addMember({id: r2.id}); - r2.addMember({id: r1.id}); + r1.addMember({id: r2.id, type: 'relation'}); + r2.addMember({id: r1.id, type: 'relation'}); changes = {created: [r1,r2], modified: [], deleted: []}; jxon = connection.osmChangeJXON('1234', changes);