diff --git a/modules/services/osm.js b/modules/services/osm.js
index 1f57c9fc6..3a3424232 100644
--- a/modules/services/osm.js
+++ b/modules/services/osm.js
@@ -27,7 +27,7 @@ var oauth = osmAuth({
});
var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
-var _tileCache = { loaded: {}, inflight: {}, seen: {} };
+var _tileCache = { loaded: {}, inflight: {}, seen: {}, rtree: rbush() };
var _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: rbush() };
var _userCache = { toLoad: {}, user: {} };
var _changeset = {};
@@ -365,7 +365,7 @@ export default {
Object.values(_noteCache.inflightPost).forEach(abortRequest);
if (_changeset.inflight) abortRequest(_changeset.inflight);
- _tileCache = { loaded: {}, inflight: {}, seen: {} };
+ _tileCache = { loaded: {}, inflight: {}, seen: {}, rtree: rbush() };
_noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: rbush() };
_userCache = { toLoad: {}, user: {} };
_changeset = {};
@@ -801,6 +801,9 @@ export default {
delete _tileCache.inflight[tile.id];
if (!err) {
_tileCache.loaded[tile.id] = true;
+ var bbox = tile.extent.bbox();
+ bbox.id = tile.id;
+ _tileCache.rtree.insert(bbox);
}
if (callback) {
callback(err, Object.assign({ data: parsed }, tile));
@@ -819,6 +822,12 @@ export default {
},
+ isDataLoaded: function(loc) {
+ var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
+ return _tileCache.rtree.collides(bbox);
+ },
+
+
// Load notes from the API in tiles
// GET /api/0.6/notes?bbox=
loadNotes: function(projection, noteOptions) {
@@ -992,22 +1001,18 @@ export default {
// This is used to save/restore the state when entering/exiting the walkthrough
// Also used for testing purposes.
caches: function(obj) {
- function cloneDeep(source) {
- return JSON.parse(JSON.stringify(source));
- }
-
- function cloneNoteCache(source) {
+ function cloneCache(source) {
var target = {};
Object.keys(source).forEach(function(k) {
if (k === 'rtree') {
- target.rtree = rbush().fromJSON(source.rtree.toJSON());
+ target.rtree = rbush().fromJSON(source.rtree.toJSON()); // clone rbush
} else if (k === 'note') {
target.note = {};
Object.keys(source.note).forEach(function(id) {
- target.note[id] = osmNote(source.note[id]);
+ target.note[id] = osmNote(source.note[id]); // copy notes
});
} else {
- target[k] = cloneDeep(source[k]);
+ target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
}
});
return target;
@@ -1015,9 +1020,9 @@ export default {
if (!arguments.length) {
return {
- tile: cloneDeep(_tileCache),
- note: cloneNoteCache(_noteCache),
- user: cloneDeep(_userCache)
+ tile: cloneCache(_tileCache),
+ note: cloneCache(_noteCache),
+ user: cloneCache(_userCache)
};
}
diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js
index 476ff73c3..561f25406 100644
--- a/test/spec/services/osm.js
+++ b/test/spec/services/osm.js
@@ -293,14 +293,62 @@ describe('iD.serviceOsm', function () {
});
+ describe('#loadTiles', function() {
+ var tileXML = '' +
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+
+ beforeEach(function() {
+ var dimensions = [64, 64];
+ context.projection
+ .scale(iD.geoZoomToScale(20))
+ .translate([55212042.434589595, 33248879.510193843]) // -74.0444216, 40.6694299
+ .clipExtent([[0,0], dimensions]);
+ });
+
+ it('calls callback when data tiles are loaded', function() {
+ var spy = sinon.spy();
+ connection.loadTiles(context.projection, spy);
+
+ server.respondWith('GET', /map\?bbox/,
+ [200, { 'Content-Type': 'text/xml' }, tileXML]);
+ server.respond();
+
+ expect(spy).to.have.been.calledOnce;
+ });
+
+ it('#isDataLoaded', function() {
+ expect(connection.isDataLoaded([-74.0444216, 40.6694299])).to.be.not.ok;
+
+ connection.loadTiles(context.projection);
+ server.respondWith('GET', /map\?bbox/,
+ [200, { 'Content-Type': 'text/xml' }, tileXML]);
+ server.respond();
+
+ expect(connection.isDataLoaded([-74.0444216, 40.6694299])).to.be.ok;
+ });
+ });
+
describe('#loadEntity', function () {
- var nodeXML = '' +
- '' +
- '';
- var wayXML = '' +
- '' +
- '' +
- '';
+ var nodeXML = '' +
+ '' +
+ '' +
+ '';
+ var wayXML = '' +
+ '' +
+ '' +
+ '' +
+ '';
beforeEach(function() {
server = sinon.fakeServer.create();
@@ -357,12 +405,14 @@ describe('iD.serviceOsm', function () {
describe('#loadEntityVersion', function () {
- var nodeXML = '' +
- '' +
- '';
- var wayXML = '' +
- '' +
- '';
+ var nodeXML = '' +
+ '' +
+ '' +
+ '';
+ var wayXML = '' +
+ '' +
+ '' +
+ '';
beforeEach(function() {
server = sinon.fakeServer.create();
@@ -539,21 +589,10 @@ describe('iD.serviceOsm', function () {
describe('#caches', function() {
it('loads reset caches', function (done) {
- var resetCaches = {
- tile: {
- inflight: {}, loaded: {}, seen: {}
- },
- note: {
- loaded: {}, inflight: {}, inflightPost: {}, note: {} // not including rtree
- },
- user: {
- toLoad: {}, user: {}
- }
- };
var caches = connection.caches();
- expect(caches.tile).to.eql(resetCaches.tile);
- expect(caches.note.loaded).to.eql(resetCaches.note.loaded);
- expect(caches.user).to.eql(resetCaches.user);
+ expect(caches.tile).to.have.all.keys(['loaded','inflight','seen','rtree']);
+ expect(caches.note).to.have.all.keys(['loaded','inflight','inflightPost','note','closed','rtree']);
+ expect(caches.user).to.have.all.keys(['toLoad','user']);
done();
});