From 07eff40223965e9cd19d43a739cd83eed10c0c24 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 12 Feb 2013 13:17:07 -0500 Subject: [PATCH 01/10] Add rtree tree to history for intersects --- js/id/core/history.js | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/js/id/core/history.js b/js/id/core/history.js index 1bedaeb60..3e7aa137d 100644 --- a/js/id/core/history.js +++ b/js/id/core/history.js @@ -4,6 +4,9 @@ iD.History = function(context) { dispatch = d3.dispatch('change', 'undone', 'redone'), lock = false; + var rtree = new RTree(), + treeGraph; + function perform(actions) { actions = Array.prototype.slice.call(actions); @@ -35,16 +38,49 @@ iD.History = function(context) { return 'iD_' + window.location.origin + '_' + n; } + function rebaseTree(entities) { + for (var i = 0; i < entities.length; i++) { + insert(treeGraph.entities[entities[i]]); + } + } + + function getRectangle(entity) { + return extentRectangle(entity.extent(treeGraph)); + } + + function extentRectangle(extent) { + var m = 1000 * 1000 * 100, + x = m * extent[0][0], + y = m * extent[0][1], + dx = m * extent[1][0] - x || 2, + dy = m * extent[1][1] - y || 2; + return new RTree.Rectangle(~~x, ~~y, ~~dx - 1, ~~dy - 1); + } + + function insert(entity) { + rtree.insert(getRectangle(entity), entity.id); + } + + function remove(entity) { + rtree.remove(getRectangle(entity), entity.id); + } + var history = { graph: function() { return stack[index].graph; }, merge: function(entities) { + + var base = treeGraph.base(), + newentities = Object.keys(entities).filter(function(i) { + return !treeGraph.entities.hasOwnProperty(i) && !base.entities[i]; + }); + for (var i = 0; i < stack.length; i++) { stack[i].graph.rebase(entities); } - + rebaseTree(newentities); dispatch.change(); }, @@ -124,6 +160,11 @@ iD.History = function(context) { } }, + intersects: function(extent) { + return rtree.search(extentRectangle(extent)) + .map(function(id) { return treeGraph.entity(id) }); + }, + difference: function() { var base = stack[0].graph, head = stack[index].graph; @@ -168,6 +209,7 @@ iD.History = function(context) { reset: function() { stack = [{graph: iD.Graph()}]; index = 0; + treeGraph = stack[0].graph; dispatch.change(); }, From 9bd46af6adb8f0536f1eeafabf52933b6e382bc8 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 12 Feb 2013 17:22:25 -0500 Subject: [PATCH 02/10] Tree intersect now works with changes --- js/id/core/difference.js | 43 ++++++++++++++++++++++++---------------- js/id/core/history.js | 37 +++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/js/id/core/difference.js b/js/id/core/difference.js index e2159271a..5470762c5 100644 --- a/js/id/core/difference.js +++ b/js/id/core/difference.js @@ -25,6 +25,18 @@ iD.Difference = function (base, head) { } }); + function addParents(parents, result) { + for (var i = 0; i < parents.length; i++) { + var parent = parents[i]; + + if (parent.id in result) + continue; + + result[parent.id] = parent; + addParents(head.parentRelations(parent), result); + } + } + var difference = {}; difference.length = function () { @@ -67,21 +79,18 @@ iD.Difference = function (base, head) { return result; }; + difference.addParents = function(entities) { + + for (var i in entities) { + addParents(head.parentWays(entities[i]), entities); + addParents(head.parentRelations(entities[i]), entities); + } + return entities; + }, + difference.complete = function(extent) { var result = {}, id, change; - function addParents(parents) { - for (var i = 0; i < parents.length; i++) { - var parent = parents[i]; - - if (parent.id in result) - continue; - - result[parent.id] = parent; - addParents(head.parentRelations(parent)); - } - } - for (id in changes) { change = changes[id]; @@ -97,21 +106,21 @@ iD.Difference = function (base, head) { if (entity.type === 'way') { var nh = h ? h.nodes : [], nb = b ? b.nodes : [], - diff; + diff, i; diff = _.difference(nh, nb); - for (var i = 0; i < diff.length; i++) { + for (i = 0; i < diff.length; i++) { result[diff[i]] = head.entity(diff[i]); } diff = _.difference(nb, nh); - for (var i = 0; i < diff.length; i++) { + for (i = 0; i < diff.length; i++) { result[diff[i]] = head.entity(diff[i]); } } + addParents(head.parentWays(entity), result); + addParents(head.parentRelations(entity), result); - addParents(head.parentWays(entity)); - addParents(head.parentRelations(entity)); } return result; diff --git a/js/id/core/history.js b/js/id/core/history.js index 3e7aa137d..415d78093 100644 --- a/js/id/core/history.js +++ b/js/id/core/history.js @@ -40,14 +40,10 @@ iD.History = function(context) { function rebaseTree(entities) { for (var i = 0; i < entities.length; i++) { - insert(treeGraph.entities[entities[i]]); + insert(stack[index].graph.entities[entities[i]]); } } - function getRectangle(entity) { - return extentRectangle(entity.extent(treeGraph)); - } - function extentRectangle(extent) { var m = 1000 * 1000 * 100, x = m * extent[0][0], @@ -58,11 +54,16 @@ iD.History = function(context) { } function insert(entity) { - rtree.insert(getRectangle(entity), entity.id); + rtree.insert(extentRectangle(entity.extent(stack[index].graph)), entity.id); } function remove(entity) { - rtree.remove(getRectangle(entity), entity.id); + var r= rtree.remove(extentRectangle(entity.extent(treeGraph)), entity.id); + } + + function reinsert(entity) { + remove(treeGraph.entities[entity.id]); + insert(entity); } var history = { @@ -161,8 +162,26 @@ iD.History = function(context) { }, intersects: function(extent) { + if (treeGraph !== stack[index].graph) { + var diff = iD.Difference(treeGraph, stack[index].graph), + modified = {}; + + diff.modified().forEach(function(d) { + var loc = treeGraph.entities[d.id].loc; + if (!loc || loc[0] !== d.loc[0] || loc[1] !== d.loc[1]) { + modified[d.id] = d; + } + }); + + d3.values(diff.addParents(modified)).map(reinsert); + diff.created().forEach(insert); + diff.deleted().forEach(remove); + + treeGraph = stack[index].graph; + } + return rtree.search(extentRectangle(extent)) - .map(function(id) { return treeGraph.entity(id) }); + .map(function(id) { return treeGraph.entity(id); }); }, difference: function() { @@ -185,7 +204,7 @@ iD.History = function(context) { } return { - modified: difference.modified().map(discardTags), + modified: d3.values(difference.modified()).map(discardTags), created: difference.created().map(discardTags), deleted: difference.deleted() }; From 2f15b0ffcaddcff9804fefbb32c71961e5ed8a02 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 12 Feb 2013 17:38:15 -0500 Subject: [PATCH 03/10] Use tree intersects instead of graph.intersects --- js/id/behavior/lasso.js | 2 +- js/id/renderer/map.js | 4 ++-- js/id/ui/contributors.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/id/behavior/lasso.js b/js/id/behavior/lasso.js index 0a26f311d..95112d8c2 100644 --- a/js/id/behavior/lasso.js +++ b/js/id/behavior/lasso.js @@ -49,7 +49,7 @@ iD.behavior.Lasso = function(context) { .on('mouseup.lasso', null); if (d3.event.clientX !== pos[0] || d3.event.clientY !== pos[1]) { - var selected = context.graph().intersects(extent); + var selected = context.history().intersects(extent); if (selected.length) { context.enter(iD.modes.Select(context, _.pluck(selected, 'id'))); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index ef101dbc7..39f91d0b1 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -43,7 +43,7 @@ iD.Map = function(context) { } }, true) .on('mouseup.zoom', function() { - if (resetTransform) redraw(); + if (resetTransform()) redraw(); }) .attr('id', 'surface') .call(iD.svg.Surface()); @@ -65,7 +65,7 @@ iD.Map = function(context) { graph = context.graph(); if (!difference) { - all = graph.intersects(extent); + all = context.history().intersects(extent); filter = d3.functor(true); } else { var complete = difference.complete(extent); diff --git a/js/id/ui/contributors.js b/js/id/ui/contributors.js index 596c576ad..7f98636ed 100644 --- a/js/id/ui/contributors.js +++ b/js/id/ui/contributors.js @@ -2,7 +2,7 @@ iD.ui.contributors = function(context) { function update(selection) { var users = {}, limit = 3, - entities = context.graph().intersects(context.map().extent()); + entities = context.history().intersects(context.map().extent()); for (var i in entities) { if (entities[i].user) users[entities[i].user] = true; From 6baf6a6dd4f31a70f58bacd28893a23c5644e3b4 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 12 Feb 2013 18:18:58 -0500 Subject: [PATCH 04/10] Move intersection tree to iD.Tree --- index.html | 1 + js/id/core/history.js | 67 ++++++----------------------------------- js/id/core/tree.js | 70 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 58 deletions(-) create mode 100644 js/id/core/tree.js diff --git a/index.html b/index.html index 8c084cf77..31f95d19e 100644 --- a/index.html +++ b/index.html @@ -140,6 +140,7 @@ + diff --git a/js/id/core/history.js b/js/id/core/history.js index 415d78093..9e5daa88e 100644 --- a/js/id/core/history.js +++ b/js/id/core/history.js @@ -1,12 +1,9 @@ iD.History = function(context) { - var stack, index, + var stack, index, tree; imagery_used = 'Bing', dispatch = d3.dispatch('change', 'undone', 'redone'), lock = false; - var rtree = new RTree(), - treeGraph; - function perform(actions) { actions = Array.prototype.slice.call(actions); @@ -38,34 +35,6 @@ iD.History = function(context) { return 'iD_' + window.location.origin + '_' + n; } - function rebaseTree(entities) { - for (var i = 0; i < entities.length; i++) { - insert(stack[index].graph.entities[entities[i]]); - } - } - - function extentRectangle(extent) { - var m = 1000 * 1000 * 100, - x = m * extent[0][0], - y = m * extent[0][1], - dx = m * extent[1][0] - x || 2, - dy = m * extent[1][1] - y || 2; - return new RTree.Rectangle(~~x, ~~y, ~~dx - 1, ~~dy - 1); - } - - function insert(entity) { - rtree.insert(extentRectangle(entity.extent(stack[index].graph)), entity.id); - } - - function remove(entity) { - var r= rtree.remove(extentRectangle(entity.extent(treeGraph)), entity.id); - } - - function reinsert(entity) { - remove(treeGraph.entities[entity.id]); - insert(entity); - } - var history = { graph: function() { return stack[index].graph; @@ -73,15 +42,16 @@ iD.History = function(context) { merge: function(entities) { - var base = treeGraph.base(), + var base = tree.base(), newentities = Object.keys(entities).filter(function(i) { - return !treeGraph.entities.hasOwnProperty(i) && !base.entities[i]; - }); + return !base.entities.hasOwnProperty(i) && !base.entities[i]; + }); for (var i = 0; i < stack.length; i++) { stack[i].graph.rebase(entities); } - rebaseTree(newentities); + tree.rebase(newentities); + dispatch.change(); }, @@ -162,26 +132,7 @@ iD.History = function(context) { }, intersects: function(extent) { - if (treeGraph !== stack[index].graph) { - var diff = iD.Difference(treeGraph, stack[index].graph), - modified = {}; - - diff.modified().forEach(function(d) { - var loc = treeGraph.entities[d.id].loc; - if (!loc || loc[0] !== d.loc[0] || loc[1] !== d.loc[1]) { - modified[d.id] = d; - } - }); - - d3.values(diff.addParents(modified)).map(reinsert); - diff.created().forEach(insert); - diff.deleted().forEach(remove); - - treeGraph = stack[index].graph; - } - - return rtree.search(extentRectangle(extent)) - .map(function(id) { return treeGraph.entity(id); }); + return tree.intersects(extent, stack[index].graph); }, difference: function() { @@ -204,7 +155,7 @@ iD.History = function(context) { } return { - modified: d3.values(difference.modified()).map(discardTags), + modified: difference.modified().map(discardTags), created: difference.created().map(discardTags), deleted: difference.deleted() }; @@ -228,7 +179,7 @@ iD.History = function(context) { reset: function() { stack = [{graph: iD.Graph()}]; index = 0; - treeGraph = stack[0].graph; + tree = iD.Tree(stack[0].graph); dispatch.change(); }, diff --git a/js/id/core/tree.js b/js/id/core/tree.js new file mode 100644 index 000000000..7570ba8ed --- /dev/null +++ b/js/id/core/tree.js @@ -0,0 +1,70 @@ +iD.Tree = function(graph) { + + var rtree = new RTree(), + m = 1000 * 1000 * 100, + head = graph; + + function extentRectangle(extent) { + x = m * extent[0][0], + y = m * extent[0][1], + dx = m * extent[1][0] - x || 2, + dy = m * extent[1][1] - y || 2; + return new RTree.Rectangle(~~x, ~~y, ~~dx - 1, ~~dy - 1); + } + + function insert(entity) { + rtree.insert(extentRectangle(entity.extent(head)), entity.id); + } + + function remove(entity) { + rtree.remove(extentRectangle(entity.extent(graph)), entity.id); + } + + function reinsert(entity) { + remove(graph.entities[entity.id]); + insert(entity); + } + + var tree = { + + rebase: function(entities) { + for (var i = 0; i < entities.length; i++) { + insert(graph.entities[entities[i]]); + } + return tree; + }, + + intersects: function(extent, g) { + + head = g; + + if (graph !== head) { + var diff = iD.Difference(graph, head), + modified = {}; + + diff.modified().forEach(function(d) { + var loc = graph.entities[d.id].loc; + if (!loc || loc[0] !== d.loc[0] || loc[1] !== d.loc[1]) { + modified[d.id] = d; + } + }); + + d3.values(diff.addParents(modified)).map(reinsert); + diff.created().forEach(insert); + diff.deleted().forEach(remove); + + graph = head; + } + + return rtree.search(extentRectangle(extent)) + .map(function(id) { return graph.entity(id); }); + }, + + base: function() { + return graph; + } + + }; + + return tree; +}; From 9c65e58cc14b26a0500bf20b50e471fdb95d20fa Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Feb 2013 10:23:30 -0500 Subject: [PATCH 05/10] Tree handles entities with missing children --- js/id/core/graph.js | 13 +------------ js/id/core/history.js | 2 +- js/id/core/tree.js | 24 ++++++++++++++++++++---- test/index.html | 2 ++ test/spec/core/tree.js | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 test/spec/core/tree.js diff --git a/js/id/core/graph.js b/js/id/core/graph.js index eae11cc15..f9966be7a 100644 --- a/js/id/core/graph.js +++ b/js/id/core/graph.js @@ -225,21 +225,10 @@ iD.Graph.prototype = { return this; }, - // get all objects that intersect an extent. - intersects: function(extent) { - var items = []; - for (var i in this.entities) { - var entity = this.entities[i]; - if (entity && this.hasAllChildren(entity) && entity.intersects(extent, this)) { - items.push(entity); - } - } - return items; - }, - hasAllChildren: function(entity) { // we're only checking changed entities, since we assume fetched data // must have all children present + var i; if (this.entities.hasOwnProperty(entity.id)) { if (entity.type === 'way') { for (i = 0; i < entity.nodes.length; i++) { diff --git a/js/id/core/history.js b/js/id/core/history.js index 9e5daa88e..a8d49cbde 100644 --- a/js/id/core/history.js +++ b/js/id/core/history.js @@ -1,5 +1,5 @@ iD.History = function(context) { - var stack, index, tree; + var stack, index, tree, imagery_used = 'Bing', dispatch = d3.dispatch('change', 'undone', 'redone'), lock = false; diff --git a/js/id/core/tree.js b/js/id/core/tree.js index 7570ba8ed..8c3635c32 100644 --- a/js/id/core/tree.js +++ b/js/id/core/tree.js @@ -2,7 +2,10 @@ iD.Tree = function(graph) { var rtree = new RTree(), m = 1000 * 1000 * 100, - head = graph; + head = graph, + queuedCreated = [], + queuedModified = [], + x, y, dx, dy; function extentRectangle(extent) { x = m * extent[0][0], @@ -29,7 +32,7 @@ iD.Tree = function(graph) { rebase: function(entities) { for (var i = 0; i < entities.length; i++) { - insert(graph.entities[entities[i]]); + insert(graph.entity(entities[i]), true); } return tree; }, @@ -49,8 +52,21 @@ iD.Tree = function(graph) { } }); - d3.values(diff.addParents(modified)).map(reinsert); - diff.created().forEach(insert); + var created = diff.created().concat(queuedCreated); + modified = d3.values(diff.addParents(modified)).concat(queuedModified); + queuedCreated = []; + queuedModified = []; + + modified.forEach(function(d) { + if (head.hasAllChildren(d)) reinsert(d); + else queuedModified.push(d); + }); + + created.forEach(function(d) { + if (head.hasAllChildren(d)) insert(d); + else queuedCreated.push(d); + }); + diff.deleted().forEach(remove); graph = head; diff --git a/test/index.html b/test/index.html index fe81a4ec5..7862c53a5 100644 --- a/test/index.html +++ b/test/index.html @@ -133,6 +133,7 @@ + @@ -172,6 +173,7 @@ + diff --git a/test/spec/core/tree.js b/test/spec/core/tree.js new file mode 100644 index 000000000..d279bc0f3 --- /dev/null +++ b/test/spec/core/tree.js @@ -0,0 +1,18 @@ +describe("iD.Tree", function() { + var tree; + + beforeEach(function() { + tree = iD.Tree(iD.Graph()); + }); + + describe("intersects", function() { + it("excludes entities with missing children, adds them when all are present", function() { + var way = iD.Way({id: 'w1', nodes: ['n']}); + var g = tree.base().replace(way); + expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([]); + var node = iD.Node({id: 'n', loc: [0.5, 0.5]}); + g = tree.base().replace(node); + expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([way, node]); + }); + }); +}); From 9a4d4ab9c5475b26752db44c744d2ac22738dfcd Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Feb 2013 14:16:18 -0500 Subject: [PATCH 06/10] Retry queued entities after rebase --- js/id/core/history.js | 2 +- js/id/core/tree.js | 8 +++++--- test/spec/core/tree.js | 26 +++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/js/id/core/history.js b/js/id/core/history.js index 501f3646e..392c3db40 100644 --- a/js/id/core/history.js +++ b/js/id/core/history.js @@ -42,7 +42,7 @@ iD.History = function(context) { merge: function(entities) { - var base = tree.base(), + var base = tree.graph(), newentities = Object.keys(entities).filter(function(i) { return !base.entities.hasOwnProperty(i) && !base.entities[i]; }); diff --git a/js/id/core/tree.js b/js/id/core/tree.js index 8c3635c32..d548ec8f3 100644 --- a/js/id/core/tree.js +++ b/js/id/core/tree.js @@ -5,7 +5,7 @@ iD.Tree = function(graph) { head = graph, queuedCreated = [], queuedModified = [], - x, y, dx, dy; + x, y, dx, dy, rebased; function extentRectangle(extent) { x = m * extent[0][0], @@ -34,6 +34,7 @@ iD.Tree = function(graph) { for (var i = 0; i < entities.length; i++) { insert(graph.entity(entities[i]), true); } + rebased = true; return tree; }, @@ -41,7 +42,7 @@ iD.Tree = function(graph) { head = g; - if (graph !== head) { + if (graph !== head || rebased) { var diff = iD.Difference(graph, head), modified = {}; @@ -70,13 +71,14 @@ iD.Tree = function(graph) { diff.deleted().forEach(remove); graph = head; + rebased = false; } return rtree.search(extentRectangle(extent)) .map(function(id) { return graph.entity(id); }); }, - base: function() { + graph: function() { return graph; } diff --git a/test/spec/core/tree.js b/test/spec/core/tree.js index d279bc0f3..5684ba6a8 100644 --- a/test/spec/core/tree.js +++ b/test/spec/core/tree.js @@ -5,13 +5,33 @@ describe("iD.Tree", function() { tree = iD.Tree(iD.Graph()); }); - describe("intersects", function() { + describe("#rebase", function() { + it("adds entities to the tree", function() { + var node = iD.Node({ id: 'n', loc: [1, 1]}); + tree.graph().rebase({ 'n': node }); + tree.rebase(['n']); + expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), tree.graph())).to.eql([node]); + }); + }); + + describe("#intersects", function() { it("excludes entities with missing children, adds them when all are present", function() { var way = iD.Way({id: 'w1', nodes: ['n']}); - var g = tree.base().replace(way); + var g = tree.graph().replace(way); expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([]); var node = iD.Node({id: 'n', loc: [0.5, 0.5]}); - g = tree.base().replace(node); + g = tree.graph().replace(node); + expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([way, node]); + }); + + it("includes entities that used to have missing children, after rebase added them", function() { + var base = tree.graph(); + var way = iD.Way({id: 'w1', nodes: ['n']}); + var g = base.replace(way); + expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([]); + var node = iD.Node({id: 'n', loc: [0.5, 0.5]}); + base.rebase({ 'n': node }); + tree.rebase(['n']); expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([way, node]); }); }); From b80bbc1fbe6872b588b6dbb069ea6878fe8634f9 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Feb 2013 14:40:37 -0500 Subject: [PATCH 07/10] Add tree test --- js/id/core/history.js | 6 ++++-- js/id/core/tree.js | 6 +++++- test/spec/core/tree.js | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/js/id/core/history.js b/js/id/core/history.js index 392c3db40..fea546329 100644 --- a/js/id/core/history.js +++ b/js/id/core/history.js @@ -42,14 +42,16 @@ iD.History = function(context) { merge: function(entities) { - var base = tree.graph(), + + var base = stack[0].graph.base(), newentities = Object.keys(entities).filter(function(i) { - return !base.entities.hasOwnProperty(i) && !base.entities[i]; + return !base.entities[i]; }); for (var i = 0; i < stack.length; i++) { stack[i].graph.rebase(entities); } + tree.rebase(newentities); dispatch.change(); diff --git a/js/id/core/tree.js b/js/id/core/tree.js index d548ec8f3..34b46535f 100644 --- a/js/id/core/tree.js +++ b/js/id/core/tree.js @@ -1,3 +1,5 @@ + + iD.Tree = function(graph) { var rtree = new RTree(), @@ -32,7 +34,9 @@ iD.Tree = function(graph) { rebase: function(entities) { for (var i = 0; i < entities.length; i++) { - insert(graph.entity(entities[i]), true); + if (!graph.hasOwnProperty(entities[i])) { + insert(graph.entity(entities[i]), true); + } } rebased = true; return tree; diff --git a/test/spec/core/tree.js b/test/spec/core/tree.js index 5684ba6a8..39769c40d 100644 --- a/test/spec/core/tree.js +++ b/test/spec/core/tree.js @@ -12,6 +12,16 @@ describe("iD.Tree", function() { tree.rebase(['n']); expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), tree.graph())).to.eql([node]); }); + + it("does not insert if entity has a modified version", function() { + var node = iD.Node({ id: 'n', loc: [1, 1]}), + node_ = node.update({ loc: [10, 10]}), + g = tree.graph().replace(node_); + expect(tree.intersects(iD.geo.Extent([9, 9], [11, 11]), g)).to.eql([node_]); + tree.graph().rebase({ 'n': node }); + tree.rebase(['n']); + expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), tree.graph())).to.eql([]); + }); }); describe("#intersects", function() { From 64c8ef626ffab7c6a79829854febf54c939afa7c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Feb 2013 15:10:48 -0500 Subject: [PATCH 08/10] Add general test for spatial tree --- js/id/core/tree.js | 4 ++-- test/spec/core/tree.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/js/id/core/tree.js b/js/id/core/tree.js index 34b46535f..498ccf429 100644 --- a/js/id/core/tree.js +++ b/js/id/core/tree.js @@ -12,8 +12,8 @@ iD.Tree = function(graph) { function extentRectangle(extent) { x = m * extent[0][0], y = m * extent[0][1], - dx = m * extent[1][0] - x || 2, - dy = m * extent[1][1] - y || 2; + dx = m * extent[1][0] - x || 1, + dy = m * extent[1][1] - y || 1; return new RTree.Rectangle(~~x, ~~y, ~~dx - 1, ~~dy - 1); } diff --git a/test/spec/core/tree.js b/test/spec/core/tree.js index 39769c40d..4cb0cf073 100644 --- a/test/spec/core/tree.js +++ b/test/spec/core/tree.js @@ -44,5 +44,12 @@ describe("iD.Tree", function() { tree.rebase(['n']); expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([way, node]); }); + + it("includes entities within extent, excludes those without", function() { + var n1 = iD.Node({ id: 'n1', loc: [1, 1]}); + var n2 = iD.Node({ id: 'n2', loc: [3, 3]}); + var g = tree.graph().replace(n1).replace(n2); + expect(tree.intersects(iD.geo.Extent([0, 0], [1.1, 1.1]), g)).to.eql([n1]); + }); }); }); From 8c1e0212f91d8b6720c61c292fa9fce9823ede9f Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Feb 2013 16:08:39 -0500 Subject: [PATCH 09/10] Fix rebasing and test --- js/id/core/tree.js | 2 +- test/spec/core/tree.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/js/id/core/tree.js b/js/id/core/tree.js index 498ccf429..94b28f12e 100644 --- a/js/id/core/tree.js +++ b/js/id/core/tree.js @@ -34,7 +34,7 @@ iD.Tree = function(graph) { rebase: function(entities) { for (var i = 0; i < entities.length; i++) { - if (!graph.hasOwnProperty(entities[i])) { + if (!graph.entities.hasOwnProperty(entities[i])) { insert(graph.entity(entities[i]), true); } } diff --git a/test/spec/core/tree.js b/test/spec/core/tree.js index 4cb0cf073..8cd4e55b6 100644 --- a/test/spec/core/tree.js +++ b/test/spec/core/tree.js @@ -20,7 +20,8 @@ describe("iD.Tree", function() { expect(tree.intersects(iD.geo.Extent([9, 9], [11, 11]), g)).to.eql([node_]); tree.graph().rebase({ 'n': node }); tree.rebase(['n']); - expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), tree.graph())).to.eql([]); + expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), g)).to.eql([]); + expect(tree.intersects(iD.geo.Extent([0, 0], [11, 11]), g)).to.eql([node_]); }); }); From 7102fea04d83f0fc0b404983b6b85da327fecb2c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Feb 2013 18:12:53 -0500 Subject: [PATCH 10/10] add history.intersects to context --- js/id/behavior/lasso.js | 2 +- js/id/id.js | 1 + js/id/renderer/map.js | 2 +- js/id/ui/contributors.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/js/id/behavior/lasso.js b/js/id/behavior/lasso.js index a3ab3cf6f..f9df080e2 100644 --- a/js/id/behavior/lasso.js +++ b/js/id/behavior/lasso.js @@ -49,7 +49,7 @@ iD.behavior.Lasso = function(context) { .on('mouseup.lasso', null); if (d3.event.clientX !== pos[0] || d3.event.clientY !== pos[1]) { - var selected = context.history().intersects(extent); + var selected = context.intersects(extent); if (selected.length) { context.enter(iD.modes.Select(context, _.pluck(selected, 'id'))); diff --git a/js/id/id.js b/js/id/id.js index 5db609593..a6ee7378f 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -36,6 +36,7 @@ window.iD = function () { context.undo = history.undo; context.redo = history.redo; context.changes = history.changes; + context.intersects = history.intersects; /* Graph */ context.entity = function(id) { diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 57ec07846..e9d0529d6 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -65,7 +65,7 @@ iD.Map = function(context) { graph = context.graph(); if (!difference) { - all = context.history().intersects(extent); + all = context.intersects(extent); filter = d3.functor(true); } else { var complete = difference.complete(extent); diff --git a/js/id/ui/contributors.js b/js/id/ui/contributors.js index b87a0f113..cc44316c4 100644 --- a/js/id/ui/contributors.js +++ b/js/id/ui/contributors.js @@ -2,7 +2,7 @@ iD.ui.Contributors = function(context) { function update(selection) { var users = {}, limit = 3, - entities = context.history().intersects(context.map().extent()); + entities = context.intersects(context.map().extent()); for (var i in entities) { if (entities[i].user) users[entities[i].user] = true;