Initial support for Multi Fetch GET

It will also be much faster to fetch the remote entities in batches
rather than one at a time through LoadEntity.

One bonus/hazard with Multi Fetch GET is that it will get deleted entities
with `visible=false`, rather than returning a HTTP Status Code 410 (Gone).

This will be the only way that we can really do proper undeletion
(Incrementing the current version by 1 is not guaranteed to work.  And
if a way is moved, fetching way/full will tell us whether the childnodes
are part of the way, but not necessarily whether they exist or not.)

We must be careful never to merge deleted entities into the real graph.
e.g, a deleted node will not have a 'loc' attribute, so code that assumes
every node must have a `loc` will be broken.

So because deleted entities are very special, the output from `loadMultiple`
should only be used for conflict resolution for now.
This commit is contained in:
Bryan Housel
2015-03-03 20:54:09 -05:00
parent edda24360a
commit cb0e8ab66c
6 changed files with 70 additions and 6 deletions
+41 -4
View File
@@ -58,6 +58,30 @@ iD.Connection = function() {
});
};
connection.loadMultiple = function(ids, callback) {
// TODO: upgrade lodash and just use _.chunk
function chunk(arr, chunkSize) {
var result = [];
for (var i = 0; i < arr.length; i += chunkSize) {
result.push(arr.slice(i, i + chunkSize));
}
return result;
}
_.each(_.groupBy(ids, iD.Entity.id.type), function(v, k) {
var type = k + 's',
osmIDs = _.map(v, iD.Entity.id.toOSM);
_.each(chunk(osmIDs, 150), function(arr) {
connection.loadFromURL(
url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
function(err, entities) {
if (callback) callback(err, {data: entities});
});
});
});
};
function authenticating() {
event.authenticating();
}
@@ -66,6 +90,12 @@ iD.Connection = function() {
event.authenticated();
}
function getLoc(attrs) {
var lon = attrs.lon && attrs.lon.value,
lat = attrs.lat && attrs.lat.value;
return [parseFloat(lon), parseFloat(lat)];
}
function getNodes(obj) {
var elems = obj.getElementsByTagName(ndStr),
nodes = new Array(elems.length);
@@ -99,15 +129,20 @@ iD.Connection = function() {
return members;
}
function getVisible(attrs) {
return (!attrs.visible || attrs.visible.value !== 'false');
}
var parsers = {
node: function nodeData(obj) {
var attrs = obj.attributes;
return new iD.Node({
id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
loc: [parseFloat(attrs.lon.value), parseFloat(attrs.lat.value)],
loc: getLoc(attrs),
version: attrs.version.value,
user: attrs.user && attrs.user.value,
tags: getTags(obj)
tags: getTags(obj),
visible: getVisible(attrs)
});
},
@@ -118,7 +153,8 @@ iD.Connection = function() {
version: attrs.version.value,
user: attrs.user && attrs.user.value,
tags: getTags(obj),
nodes: getNodes(obj)
nodes: getNodes(obj),
visible: getVisible(attrs)
});
},
@@ -129,7 +165,8 @@ iD.Connection = function() {
version: attrs.version.value,
user: attrs.user && attrs.user.value,
tags: getTags(obj),
members: getMembers(obj)
members: getMembers(obj),
visible: getVisible(attrs)
});
}
};
+3
View File
@@ -56,6 +56,9 @@ iD.Entity.prototype = {
if (!this.id && this.type) {
this.id = iD.Entity.id(this.type);
}
if (!this.hasOwnProperty('visible')) {
this.visible = true;
}
if (iD.debug) {
Object.freeze(this);
+1 -1
View File
@@ -116,7 +116,7 @@ iD.Graph.prototype = {
for (i = 0; i < entities.length; i++) {
var entity = entities[i];
if (!force && base.entities[entity.id])
if (!entity.visible || (!force && base.entities[entity.id]))
continue;
// Merging data into the base graph
+1 -1
View File
@@ -45,7 +45,7 @@ iD.Tree = function(head) {
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
if (!force && (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]))
if (!entity.visible || (!force && (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id])))
continue;
insertions[entity.id] = entity;
+15
View File
@@ -116,6 +116,21 @@ describe('iD.Connection', function () {
});
});
describe('#loadMultiple', function () {
beforeEach(function() {
server = sinon.fakeServer.create();
});
afterEach(function() {
server.restore();
});
it('loads nodes');
it('loads ways');
});
describe('#osmChangeJXON', function() {
it('converts change data to JXON', function() {
var jxon = c.osmChangeJXON('1234', {created: [], modified: [], deleted: []});
+9
View File
@@ -77,6 +77,15 @@ describe('iD.Graph', function() {
expect(graph.entity('n')).to.equal(node);
});
it("doesn't rebase deleted entities", function () {
var node = iD.Node({id: 'n', visible: false}),
graph = iD.Graph();
graph.rebase([node], [graph]);
expect(graph.hasEntity('n')).to.be.not.ok;
});
it("gives precedence to existing entities", function () {
var a = iD.Node({id: 'n'}),
b = iD.Node({id: 'n'}),